# This is a set of functions (some erroneous) which I wrote to make it # easy to use dates in some programs. Some material is borrowed from # calendar and some from some Zope file (I forgot which). The most # interesting function is DateSerial() which takes a date and returns a # serial number, with 1/1/1900 being 1 (note that MS Excel and Lotus123 # missed or added one leap year early on and are off by one for recent # dates. There is an explanation that used to be on the MS website # somewhere.) I am trying to learn how to do python classes, so the class # DateInfo: is probably defective. Using a serial number date allows more # flexibility with dates and makes financial calculations such as # interest, days360, weekDay, and other such stuff easier, but all my # functions must be checked for correctness and any improvements, # additions, etc. are quite welcome. I would hope that this could be part # of a useful financial analysis or calendar class (if I ever figure out # how to use classes). # Hoping you find this interesting... # Thanks! # # import string from types import * import calendar yearbase = 1900 monthLength = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31) daysToMonth = (0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334) monthNames = ('January','February','March','April','May','June','July', 'August', 'September', 'October', 'November', 'December') dayNames = ('Sunday','Monday','Tuesday','Wednesday', 'Thursday','Friday','Saturday') dytmoD = {'M1':0, 'M2':31, 'M3':59, 'M4':90, 'M5':120, 'M6':151, 'M7':181, 'M8':212, 'M9':243, 'M10':273, 'M11':304, 'M12':334 } molenD = {'M1':31, 'M2':28, 'M3':31, 'M4':30, 'M5':31, 'M6':30, 'M7':31, 'M8':31, 'M9':30, 'M10':31, 'M11':30, 'M12':31 } monameD = {'M1':'January', 'M2':'February', 'M3':'March', 'M4':'April', 'M5':'May', 'M6':'June', 'M7':'July','M8':'August', 'M9':'September', 'M10':'October', 'M11':'November', 'M12':'December' } #========= class DateInfo(yr, mo, dy): """ Intended to take a date and provide information such as serial number (1900 base), day of week (Sunday begins), year to date days, days 360 """ if yr < baseYear: raise ValueError, 'year: ' + repr(yr) + ' less than ' + repr(baseYear) if mo > 12: raise ValueError, 'month: ' + repr(mo) + ' greater than 12' if yr < baseYear: raise ValueError, 'year: ' + repr(yr) + ' less than ' + repr(baseYear) #date = dateMDY if yr < 1900: if yr > 35: year = 1900 + yr else: year = 2000 + yr month = int(mo) day = dy moId = 'M%d' % month baseYear = 1900 dSep = '/' dytmoD = {'M1':0, 'M2':31, 'M3':59, 'M4':90, 'M5':120, 'M6':151, 'M7':181, 'M8':212, 'M9':243, 'M10':273, 'M11':304, 'M12':334 } molenD = {'M1':31, 'M2':28, 'M3':31, 'M4':30, 'M5':31, 'M6':30, 'M7':31, 'M8':31, 'M9':30, 'M10':31, 'M11':30, 'M12':31 } monameD = {'M1':'January', 'M2':'February', 'M3':'March', 'M4':'April', 'M5':'May', 'M6':'June', 'M7':'July','M8':'August', 'M9':'September', 'M10':'October', 'M11':'November', 'M12':'December' } moKeys = dytmoD.keys() moKeys.sort() dayNames = ((1, 'Sun', 'Sunday'),(2, 'Mon', 'Monday'), (3, 'Tue', 'Tuesday'),(4, 'Wed', 'Wednesday'), (5, 'Thur', 'Thursday'),(6, 'Fri', 'Friday'), (7, 'Sat', 'Saturday')) serial = dateSerial(year, month, day) def isLeapYear(yr): return (0 == (yr % 4)) def dateSerial(yr, mo, dy): # assume base year of 1900 so that 1-1-1900 is day 1 sy = (yr - baseYear) * 365 # 365 days to a year # Leap Year occurs every four years, except for years ending in 00, # in which case only if the year is divisible by 400. # for any year add: - (yr/100 - baseYear/100) + (yr/400 - baseYear/400) # but since this is for centuries 1900 and 2000, it is superfluous sly = (yr/4 - baseYear/4) # sm = dtymoD[moId] if isLeapYear(yr) and mo < 3: sm = sm - 1 return sy + sly + sm + dy def serialDate(dSerial): """verify the correction for leap years""" sy = (dSerial / 365) + baseYear sly = (yr - baseYear) / 4 sd = dSerial - sy - sly mo = 0 for ky in moKeys: if sd < dytmoD[ky]: mo = mo + 1 else: break dy = sd - dytmoD[ky] if isLeapYear(sy) and mo < 3: dy = dy - 1 return (yr, mo, dy) def DayOfWeek(dSerial, Onum1abrev2word=0): """ Argument is a serial number date. Onum1abrev2word -> 0=integer, 1=abbreviation, 2=word Returns the day of the week where Sunday=1, Monday=2, ..., Saturday = 7 """ try: rt = int(Onum1abrev2word) except ValueError: print 'Onum1abrev2word: %s must be an integer 0, 1, or 2' % Onum1abrev2word try: dNum = int(dSerial) except ValueError: print 'dSerial: %s must be an integer as from dateSerial()' % dSerial # assuming that 1/1/1900 was a Monday firstDay = 1 if dSerial < 7: dw = dNum else: dw = dNum % 7 return dayNames[dw][rt] #========= def tupleYMD(sYYMMDD, yearLen=2, delimiter='/', isTuple=0): dType = type(sYYMMDD) if dType is types.StringType: # we want a string here strDate = sYYMMDD else: strDate = repr(sYYMMDD) # convert dateYMD to a string if yrLen == 2: yr = int(strDate(:yearLen)) if yr < 35: yr = yr + 2000 else: yr = yr + 1900 else: yr = int(strDate(:yearLen) mo = int(strDate(yearLen:yearLen+2)) dy = int(strDate(-2:)) if isTuple: return (yr, mo, dy) else: return %d + delimiter + %d % strMo + delimiter + %d % strYr #=========== def dateSerial(year, month, day): # assume base year of 1900 so that 1-1-1900 is day 1 yr = int(year) mo = int(month) dy = int(day) moKy = 'M%d' % mo if yr < baseYear: raise ValueError, 'year: ' + repr(yr) + ' less than ' + repr(baseYear) if mo > 12: raise ValueError, 'month: ' + repr(mo) + ' greater than 12' if yr < baseYear: raise ValueError, 'year: ' + repr(yr) + ' less than ' + repr(baseYear) moIdx = mo - 1 #Leap Year occurs every four years, except for years ending in 00, #in which case only if the year is divisible by 400. sy = (yr - baseYear) * 365 sly = (yr/4 - baseYear/4) - (yr/100 - baseYear/100) + (yr/400 - baseYear/400) sm = daysToMonth[moIdx] sm = dtymoD['M%' % mo] if isLeapYear(yr): if moIdx <= 1: sly = sly - 1 return sy + sly + sm + dy #=========== def serialDate(dSerial): """verify the correction for leap years""" sly = dSerial / (365 * 4) dS = dSerial - sly yr = dS / 365 dy1 = dS % 365 for i in range(len(daysToMonth)): if dy1 < daysToMonth[i]: mo = i + 1 break dy = dy1 - daysToMonth[mo-1] return (yr, mo, dy) #========== def dayOfWeek(dSerial): """Argument is a serial number date. Returns the integer value for the day of the week where Sunday = 1, Monday = 2, ... Saturday = 7""" # assuming that 1/1/1900 was a Monday firstDay = 1 id dSerial < 7: dw = dSerial + firstDay else: dw = dSerial % 7 + firstDay return dw #=========== def isLeapYear(yr): isLY = (0 == (yr % 4) return isLY def isleap(year): """Return 1 for leap years, 0 for non-leap years.""" return year % 4 == 0 and (year % 100 <> 0 or year % 400 == 0) def leapdays(y1, y2): """Return number of leap years in range [y1, y2). Assume y1 <= y2.""" y1 -= 1 y2 -= 1 return (y2/4 - y1/4) - (y2/100 - y1/100) + (y2/400 - y1/400) #=========== def getInfo(getWhat='help'): """Returns different information based on getWhat""" argsL = ('monthLength -> int tuple', 'monthNames -> string tuple', 'dayNames -> string tuple', 'daysToMonth -> int tuple') argsR = { 'help': argsL, 'monthLength': monthLength, 'monthNames': monthNames, 'dayNames': dayNames, 'daysToMonth': daysToMonth} rVal = 'help' for itm in argsR.keys(): if getWhat == itm: rVal = itm break return argsR[rVal] ## # known separators are / - . ## def FindSeparator(dateMDY): ## sep = ('/','-','.') ## for s in sep: ## try: ## DateTimeError='DateTimeError' ## SyntaxError ='Invalid Date-Time String' ## DateError ='Invalid Date Components' ## int_pattern =ts_regex.compile('\([0-9]+\)') #TS ## flt_pattern =ts_regex.compile(':\([0-9]+\.[0-9]+\)') #TS ## name_pattern =ts_regex.compile('\([a-z][a-z]+\)', ts_regex.casefold) #TS ## space_chars =' \t\n' ## delimiters ='-/.:,' ## _month_len =((0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31), ## (0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)) ## _until_month=((0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334), ## (0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335)) ## _months =['','January','February','March','April','May','June','July', ## 'August', 'September', 'October', 'November', 'December'] ## _months_a =['','Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', ## 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] ## _months_p =['','Jan.', 'Feb.', 'Mar.', 'Apr.', 'May', 'June', ## 'July', 'Aug.', 'Sep.', 'Oct.', 'Nov.', 'Dec.'] ## _monthmap ={'january': 1, 'jan': 1, ## 'february': 2, 'feb': 2, ## 'march': 3, 'mar': 3, ## 'april': 4, 'apr': 4, ## 'may': 5, ## 'june': 6, 'jun': 6, ## 'july': 7, 'jul': 7, ## 'august': 8, 'aug': 8, ## 'september': 9, 'sep': 9, 'sept': 9, ## 'october': 10, 'oct': 10, ## 'november': 11, 'nov': 11, ## 'december': 12, 'dec': 12} ## _days =['Sunday','Monday','Tuesday','Wednesday', ## 'Thursday','Friday','Saturday'] ## _days_a =['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ] ## _days_p =['Sun.', 'Mon.', 'Tue.', 'Wed.', 'Thu.', 'Fri.', 'Sat.'] ## _daymap ={'sunday': 1, 'sun': 1, ## 'monday': 2, 'mon': 2, ## 'tuesday': 3, 'tues': 3, 'tue': 3, ## 'wednesday': 4, 'wed': 4, ## 'thursday': 5, 'thurs': 5, 'thur': 5, 'thu': 5, ## 'friday': 6, 'fri': 6, ## 'saturday': 7, 'sat': 7}