Skip to content

Commit

Permalink
Merge pull request #1 from evdb/master
Browse files Browse the repository at this point in the history
Django 1.3 compat
  • Loading branch information
dracos committed Nov 4, 2011
2 parents bf136e4 + 6832180 commit 988572f
Show file tree
Hide file tree
Showing 10 changed files with 840 additions and 24 deletions.
1 change: 1 addition & 0 deletions .gitignore
@@ -0,0 +1 @@
*.pyc
696 changes: 696 additions & 0 deletions LICENSE.txt

Large diffs are not rendered by default.

File renamed without changes.
Empty file.
79 changes: 57 additions & 22 deletions fields.py → django_date_extensions/fields.py
Expand Up @@ -9,8 +9,12 @@
class ApproximateDate(object): class ApproximateDate(object):
"""A date object that accepts 0 for month or day to mean we don't """A date object that accepts 0 for month or day to mean we don't
know when it is within that month/year.""" know when it is within that month/year."""
def __init__(self, year, month=0, day=0): def __init__(self, year=0, month=0, day=0, future=False):
if year and month and day: if future:
d = None
if year or month or day:
raise ValueError("Future dates can have no year, month or day")
elif year and month and day:
d = date(year, month, day) d = date(year, month, day)
elif year and month: elif year and month:
d = date(year, month, 1) d = date(year, month, 1)
Expand All @@ -20,45 +24,66 @@ def __init__(self, year, month=0, day=0):
d = date(year, 1, 1) d = date(year, 1, 1)
else: else:
raise ValueError("You must specify a year") raise ValueError("You must specify a year")
self.year = year
self.month = month self.future = future
self.day = day self.year = year
self.month = month
self.day = day


def __repr__(self): def __repr__(self):
if self.year and self.month and self.day: if self.future:
return 'future'
elif self.year and self.month and self.day:
return "%04d-%02d-%02d" % (self.year, self.month, self.day) return "%04d-%02d-%02d" % (self.year, self.month, self.day)
elif self.year and self.month: elif self.year and self.month:
return "%04d-%02d-00" % (self.year, self.month) return "%04d-%02d-00" % (self.year, self.month)
elif self.year: elif self.year:
return "%04d-00-00" % self.year return "%04d-00-00" % self.year


def __str__(self): def __str__(self):
if self.year and self.month and self.day: if self.future:
return 'future'
elif self.year and self.month and self.day:
return dateformat.format(self, "jS F Y") return dateformat.format(self, "jS F Y")
elif self.year and self.month: elif self.year and self.month:
return dateformat.format(self, "F Y") return dateformat.format(self, "F Y")
elif self.year: elif self.year:
return dateformat.format(self, "Y") return dateformat.format(self, "Y")


def __eq__(self, other):
if other is None:
return False
if not isinstance(other, ApproximateDate):
return False
elif (self.year, self.month, self.day, self.future) != (other.year, other.month, other.day, other.future):
return False
else:
return True

def __lt__(self, other): def __lt__(self, other):
if other is None or (self.year, self.month, self.day) >= (other.year, other.month, other.day): if other is None:
return False
elif self.future or other.future:
if self.future:
return False # regardless of other.future it won't be less
else:
return True # we were not in future so they are
elif(self.year, self.month, self.day) < (other.year, other.month, other.day):
return True
else:
return False return False
return True


def __le__(self, other): def __le__(self, other):
if other is None or (self.year, self.month, self.day) > (other.year, other.month, other.day): return self < other or self == other
return False
return True


def __gt__(self, other): def __gt__(self, other):
if other is None or (self.year, self.month, self.day) <= (other.year, other.month, other.day): return not self <= other
return False
return True


def __ge__(self, other): def __ge__(self, other):
if other is None or (self.year, self.month, self.day) < (other.year, other.month, other.day): return self > other or self == other
return False
return True def __len__(self):
return len( self.__repr__() )


ansi_date_re = re.compile(r'^\d{4}-\d{1,2}-\d{1,2}$') ansi_date_re = re.compile(r'^\d{4}-\d{1,2}-\d{1,2}$')


Expand All @@ -78,6 +103,9 @@ def to_python(self, value):
if isinstance(value, ApproximateDate): if isinstance(value, ApproximateDate):
return value return value


if value == 'future':
return ApproximateDate(future=True)

if not ansi_date_re.search(value): if not ansi_date_re.search(value):
raise ValidationError('Enter a valid date in YYYY-MM-DD format.') raise ValidationError('Enter a valid date in YYYY-MM-DD format.')


Expand All @@ -88,13 +116,16 @@ def to_python(self, value):
msg = _('Invalid date: %s') % _(str(e)) msg = _('Invalid date: %s') % _(str(e))
raise exceptions.ValidationError(msg) raise exceptions.ValidationError(msg)


def get_db_prep_value(self, value): # note - could rename to 'get_prep_value' but would break 1.1 compatability
def get_db_prep_value(self, value, connection=None, prepared=False):
if value in (None, ''): if value in (None, ''):
return '' return ''
if isinstance(value, ApproximateDate): if isinstance(value, ApproximateDate):
return repr(value) return repr(value)
if isinstance(value, date): if isinstance(value, date):
return dateformat.format(value, "Y-m-d") return dateformat.format(value, "Y-m-d")
if value == 'future':
return 'future'
if not ansi_date_re.search(value): if not ansi_date_re.search(value):
raise ValidationError('Enter a valid date in YYYY-MM-DD format.') raise ValidationError('Enter a valid date in YYYY-MM-DD format.')
return value return value
Expand Down Expand Up @@ -137,6 +168,8 @@ def clean(self, value):
super(ApproximateDateFormField, self).clean(value) super(ApproximateDateFormField, self).clean(value)
if value in (None, ''): if value in (None, ''):
return None return None
if value == 'future':
return ApproximateDate(future=True)
if isinstance(value, ApproximateDate): if isinstance(value, ApproximateDate):
return value return value
value = re.sub('(?<=\d)(st|nd|rd|th)', '', value.strip()) value = re.sub('(?<=\d)(st|nd|rd|th)', '', value.strip())
Expand Down Expand Up @@ -184,6 +217,8 @@ def clean(self, value):
super(PrettyDateField, self).clean(value) super(PrettyDateField, self).clean(value)
if value in (None, ''): if value in (None, ''):
return None return None
if value == 'future':
return ApproximateDate(future=True)
if isinstance(value, datetime.datetime): if isinstance(value, datetime.datetime):
return value.date() return value.date()
if isinstance(value, datetime.date): if isinstance(value, datetime.date):
Expand Down
75 changes: 75 additions & 0 deletions django_date_extensions/tests.py
@@ -0,0 +1,75 @@
import os
os.environ['DJANGO_SETTINGS_MODULE'] = 'example.settings'

from fields import ApproximateDate
import unittest


class CompareDates(unittest.TestCase):

def test_compare(self):

y_past = ApproximateDate( year=2000 );
y_future = ApproximateDate( year=2100 );
future = ApproximateDate( future=True );

# check that we can be compared to None, '' and u''
for bad_val in ( '', u'', None ):
self.assertFalse( y_past in ( bad_val, ) )
self.assertFalse( y_past == bad_val )
self.assertTrue( y_past != bad_val )

# sanity check
self.assertTrue( y_past == y_past )
self.assertTrue( y_future == y_future )

self.assertFalse( y_past != y_past )
self.assertFalse( y_future != y_future )

self.assertTrue( y_past != y_future )
self.assertTrue( y_future != y_past )

self.assertTrue( y_future > y_past )
self.assertTrue( y_future >= y_past )
self.assertFalse( y_past > y_future )
self.assertFalse( y_past >= y_future )

self.assertTrue( y_past < y_future )
self.assertTrue( y_past <= y_future )
self.assertFalse( y_future < y_past )
self.assertFalse( y_future <= y_past )

# Future dates are always greater
self.assertTrue( y_past < future )
self.assertTrue( y_past <= future )
self.assertTrue( y_future < future )
self.assertTrue( y_future <= future )

self.assertTrue( future > y_past )
self.assertTrue( future >= y_past )
self.assertTrue( future > y_future )
self.assertTrue( future >= y_future )

# Future dates are equal to themselves (so that sorting is sane)
self.assertFalse( future < future )
self.assertTrue( future <= future )
self.assertTrue( future == future )
self.assertTrue( future >= future )
self.assertFalse( future > future )


class Lengths(unittest.TestCase):
known_lengths = (
({ 'year':1999, }, 10 ),
({ 'year':1999, 'month': 01, }, 10 ),
({ 'year':1999, 'month': 01, 'day': 01 }, 10 ),
({ 'future': True }, 6 ),
);

def test_length(self):
for kwargs, length in self.known_lengths:
approx = ApproximateDate( **kwargs )
self.assertEqual( len( approx ), length )

if __name__ == "__main__":
unittest.main()
File renamed without changes.
2 changes: 1 addition & 1 deletion example/ext/fields.py
2 changes: 1 addition & 1 deletion example/ext/widgets.py
9 changes: 9 additions & 0 deletions setup.py
@@ -0,0 +1,9 @@
from distutils.core import setup

setup(
name='django_date_extensions',
version='0.1dev',
packages=['django_date_extensions',],
license='GNU Affero General Public license',
long_description=open('README.txt').read(),
)

0 comments on commit 988572f

Please sign in to comment.