# Simple date class

 

import re

patt = "[/.,; ]"

 

class Dates(object):

 

    monthDict = dict(zip(range(1,13),['January', 'February', 'March', 'April', \

                                      'May', 'June', 'July', 'August', 'September', \

                                      'October', 'November', 'December']))

    daysList1 = [31,28,31,30,31,30,31,31,30,31,30,31]

    daysList2 = [31,29,31,30,31,30,31,31,30,31,30,31]

 

    def __init__(self, dateStr):

        # dateStr is in the format month/day/year (xx/xx/xxxx) or month.day.year

        self.dateStr = dateStr

        try:

            self.month, self.day, self.year = [int(s) for s in re.split(patt, dateStr)]

        except:

            raise ValueError, "Invalid date format"

        self.month_str = self.monthStr()

        self.day_num = self.dayNo()

 

    def isLeap(self, yr):       

        if not yr % 4:

            return 1

        return 0

 

    def daysList(self, year):

        return [self.daysList1, self.daysList2][self.isLeap(year)]

 

    def monthStr(self):

        # return the text representation of the month number

        try:

            return self.monthDict[self.month]

        except KeyError:

            raise ValueError, "Invalid month number"

 

    def dayNo(self):

        # return the day number in the year

        if self.day < 1 or self.day > self.daysList(self.year)[self.month-1]:

            raise ValueError, "Invalid day number"

        return sum(self.daysList(self.year)[:self.month-1]) + self.day

 

    def monthDayYr(self, day_num):

        # return a tuple of month, day, year given a day number

        # day number is with respect to the year of the instance

        idx = 0

        year = self.year

        while day_num < 1:

            day_num += sum(self.daysList(year))

            year -= 1

        while True:

            if day_num <= self.daysList(year)[idx]:

                return idx+1, day_num, year

            day_num -= self.daysList(year)[idx]

            idx += 1

            if idx == 12:

                idx = 0

                year += 1

 

    def nextDay(self):

        return self+1

    

    def preDay(self):

        return self-1

 

    def daysToZero(self):

        # calculate the number of days with respect to year 0

        if self.year < 0:

            yrList = xrange(self.year, 0)

            days = -self.dayNo()

        else:

            yrList = xrange(0, self.year)

            days = self.dayNo()

        return days + sum([sum(self.daysList(yr)) for yr in yrList])       

 

    def diff(self, other):

        year_diff = self.year - other.year

        if year_diff < 0:

            yrList = xrange(self.year, other.year)

            days = self.dayNo() - other.dayNo()

        else:

            yrList = xrange(other.year, self.year)

            days = other.dayNo() - self.dayNo()

        return days - sum([sum(self.daysList(yr)) for yr in yrList])

 

    def __add__(self, days):

        return Dates('/'.join([str(item) for item in self.monthDayYr(self.dayNo() + days)]))

 

    def __sub__(self, days):

        # days can be an int or a Dates() object

        # return a Dates() object, or the number of days difference

        if isinstance(days, int):

            return Dates('/'.join([str(item) for item in self.monthDayYr(self.dayNo() - days)]))

        # days is an instance of Dates if not an int

        return self.daysToZero()-days.daysToZero()

   

    def __str__(self):

        return "%s %d, %d" % (self.month_str, self.day, self.year)

 

if __name__ == '__main__':

    a = Dates('6/8/2007')

    print a

    print a.nextDay()

    print a.preDay()

    b = a+90039

    print b

    c = a-4665

    print c

    d = Dates('12/31/2000')

    print d.dayNo()

 

    f = Dates('2.29.2000')

    print f

    print f.isLeap(f.year)

    print f.daysList(f.year)

 

    print f-d

    print f.diff(d)

    print d.diff(f)

 

    print a-b

    print a.diff(b)

    print b-a

    print b.diff(a)

 

'''

>>> June 8, 2007

June 9, 2007

June 7, 2007

December 12, 2253

August 30, 1994

366

February 29, 2000

1

[31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]

-306

-306

306

-90039

-90039

90039

90039

>>> >>> a = Dates('12/31/2007')

>>> b = Dates('12/31/2006')

>>> c = Dates('12/31/2005')

>>> d = Dates('12/31/2004')

>>> e = Dates('12/31/2008')

>>> f = Dates('12/31/2003')

>>> a-b

365

>>> e-a

366

>>> e-f

1827

>>> 1827%365

2

>>> a-e

-366

>>> f-e

-1827

>>>

'''