Date type
Armin Wittfoth
armin_wittfoth at yahoo.com.au
Thu May 22 03:27:46 EDT 2003
"Batista, Facundo" <FBatista at uniFON.com.ar> wrote in message news:<mailman.1053095964.23749.python-list at python.org>...
> I need a "date type" and I can t find it anywhere.
I recently wrote such a Date type, you might be able to use my code as
a starting point. Since 'Date' is subclassed to the built-in 'int',
you will have to be using a version of Python that can subclass
built-ins (which is when Python really made it imho). Date,
understands dates in ISO-8601 'internal' (ie sans hyphens, eg
20030522) format, and behaves just like an int except in these
regards: it will only accept a integer that represents a valid date,
adding or subtracting n to it will result in the date +/- n days from
the original date (ie 20010101 - 1 = 19991231, not 20010100) and it
can be pretty printed. Oh yeah, and it has the attributes, year,
month and day, that you were after. However, it doesn't to time,
sorry.
This code could be improved upon! A lot. (Any suggestions gratefully
considered) In particular '__add__' and '__sub__' work iteratively,
whereas you (or someone else here) should be able to work out a
constant time solution. The reason for this is that my application
only ever has to find the tomorrow or yesterday of any given date and
it was easier to write this way. With little extra effort (put the
super call in __init__ after the, slightly amended regexp etc.), you
should get it to accept iso8601 'standard' (with hyphens) format date
strings as well. (This version requires an integer argument). You
would also want to rewrite the pretty print method to get the kind of
output you were after (though I note there you are also interested in
outputting time info, a lot more work there).
class Date (int) :
def __init__ (self, val) :
super(int, self).__init__(val)
match = re.search('^(\d{1,4})(\d{2})(\d{2})$', str(self))
if match :
year, month, day = match.groups()
self.year = int(year)
self.month = int(month)
self.day = int(day)
if not self._isdate() :
raise InvalidDate
else :
raise InvalidDateFormat
def __add__ (self, incr) :
year = self.year
month = self.month
day = self.day
for i in range(incr) :
#max = calendar.monthrange(year, month)[1]
max = self.daysofmonth(year, month)
day, carry = self._incrDay(day, max)
if carry :
month, carry = self._incrMonth(month)
if carry :
year += 1
return Date(int("%04d%02d%02d" % (year, month, day)))
def _incrDay (self, day, max) :
m = 0
day += 1
if day > max :
day -= max
m = 1
return (day, m)
def _incrMonth (self, month) :
y = 0
month += 1
if month > 12 :
month -= 12
y = 1
return month, y
def __sub__ (self, decr) :
year = self.year
month = self.month
day = self.day
for i in range(decr) :
prev_month, carry = self._decrMonth(month)
p_year = carry and year - 1 or year
#max = calendar.monthrange(p_year, prev_month)[1]
max = self.daysofmonth(p_year, prev_month)
day, carry = self._decrDay(day, max)
if carry :
month, carry = self._decrMonth(month)
if carry :
year -= 1
return Date(int("%04d%02d%02d" % (year, month, day)))
def _decrDay (self, day, max) :
m = 0
day -= 1
if day < 1 :
day += max
m = 1
return (day, m)
def _decrMonth (self, month) :
y = 0
month -= 1
if month < 1 :
month += 12
y = 1
return (month, y)
def daysofmonth (self, year, month) :
#does what calendar.monthrange(yr, mn)[1] should do.
#in 2.2 monthrange seems to be broken for years before 1970.
#Fixed in 2.3 :)
monthdays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
if month == 2 and calendar.isleap(year) :
max = 29
else :
max = monthdays[month - 1]
return max
def _isdate (self) :
if not 1 <= self.month <= 12 :
ret = False
else :
ret = 1 <= self.day <= self.daysofmonth(self.year,
self.month)
return ret
def pretty (self) :
months = { '01' : 'January',
'02' : 'February',
'03' : 'March',
'04' : 'April',
'05' : 'May',
'06' : 'June',
'07' : 'July',
'08' : 'August',
'09' : 'September',
'10' : 'October',
'11' : 'November',
'12' : 'December'}
found = re.search('(\d{1,4})(\d{2})(\d{2})', str(self))
if found :
(year, month, day) = found.groups()
nmonth = months[month]
nday = int(day)
return "%s %s %s" % (nday, nmonth, year)
else :
raise TypeError
Obviously you'll need to import 're' and 'calendar' as well as create
the 'InvalidDate' and 'InvalidDateFormat' exceptions (which I have
subclassed to nothing more imaginative than StandardError).
Sample usage:
>>> today = Date('%04d%02d%02d' % time.localtime()[:3])
>>> today
20030522
>>> today + 14
20030605
>>> (today - 1).pretty()
'21 May 2003'
>>>
cheers,
Armin
More information about the Python-list
mailing list