#!/usr/bin/python """Implements a pure Python version of strptime. Copyright (c) 2001, Brett Cannon All rights reserved. Released under the BSD license (http://www.opensource.org/licenses/bsd-license.html) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The main function of this module is strptime() which attempts to simulate time.strptime as best as possible while following the explanation of time.strptime as spelled out by the official Python documentation. CLASSES: TimeObjError -- Exception class for TimeObj object. StrptimeError -- Exception class for anything that might be triggered by user input. TimeObj -- Object for handling basic time manipulation. StrpObj -- Subclasses TimeObj to provide functionality for strptime(). FUNCTIONS: LocaleAssembly -- Takes in locale-specific information and returns a tuple and returns a new dictionary set up for use by strptime. strptime -- Acts like time.strptime() function using this module's various classes. VARIABLES: CENTURY -- The century value used in conjunction with %y to have a complete year representation. ENGLISH SWEDISH -- Used for locale_setting parameter for strptime(). AS_IS -- Used for option paramter for strptime() to extrapolate date info without checking its validity or filling in of missing info. CHECK -- Used for option parameter for strptime() to check validity of extrapolated info. FILL_IN -- Used for option parameter for strptime() to check the validity of extrapolated info and to fill in missing info for the time. Using from strptime import * will only export strptime(). Language support by this module is dependent on hard-coding it into the StrpObj.DictAssembly() method. If you come up with improvements or bug-fixes, please let me know at drifty@bigfoot.com so I can integrate them into the official version. Specifically, I am interested in more equations for filling in information and more locale support. Thanks has to be given to Andrew Markebo (flognat@fukt.hk-r.se) for his pure Python version of strptime. Made me improve locale support. And obviously thanks has to be given to Guido van Rossum and everyone else who has contributed to Python. It is the greatest language I have ever used and I thank you all for it. """ import re from exceptions import Exception __all__=['strptime','AS_IS','CHECK','FILL_IN','LocaleAssembly','ENGLISH','SWEDISH'] """General info about the module""" __author__='Brett Cannon' __email__='drifty@bigfoot.com' __version__='1.5' __url__='http://www.drifty.org/' """Global settings""" CENTURY=2000 """Paramter constants""" AS_IS='AS_IS' CHECK='CHECK' FILL_IN='FILL_IN' def LocaleAssembly(DirectiveDict, MonthDict, DayDict, am_pmTuple): """Creates locale tuple for use by strptime. Takes in DirectiveDict (locale-specific regexes for extracting info from time string), MonthDict (locale full and abbreviated month names), DayDict (locale full and abbreviated weekday names), and am_pmDict (locale's valid representation of AM and PM). DirectiveDict, MonthDict, and DayDict are dictionaries while am_pmTuple is a tuple. Look at how the ENGLISH dictionary is created for an example on how the passed-in values should be constructed. Also, make sure that every value in your language dictionary has a corresponding version to one in the ENGLISH dictionary; leaving any out could cause problems. Also note that if there are any values in the BasicDict that you would like to override, then put it in the DirectiveDict dictionary that you pass in. """ BasicDict={'%d':r'(?P[0-3]\d)', #Day of the month [01,31]. '%H':r'(?P[0-2]\d)', #Hour (24-h) [00,23]. '%I':r'(?P[01]\d)', #Hour (12-h) [01,12]. '%j':r'(?P[0-3]\d\d)', #Day of the year [001,366]. '%m':r'(?P[01]\d)', #Month [01,12]. '%M':r'(?P[0-5]\d)', #Minute [00,59]. '%S':r'(?P[0-6]\d)', #Second [00,61]. '%U':r'(?P[0-5]\d)', #Week number of the year with Sunday as first day of the week [00,53]. '%w':r'(?P[0-6])', #Weekday [0(Sunday),6]. '%W':r'(?P[0-5]\d)', #Week number of the year with Monday as the first day of the week [00,53]. '%y':r'(?P\d\d)', #Year without century [00,99]. '%Y':r'(?P\d\d\d\d)', #Year with century. '%Z':r'(?P(\D+ Time)|([\S\D]{3,3}))', #Time zone name (or no characters if no time zone exists). '%%':r'(?P%)' #Literal "%". Doesn't matter that group name is not same as directive since literal % will be ignored in the end. } BasicDict.update(DirectiveDict) return (BasicDict,MonthDict,DayDict,am_pmTuple) """Built-in locales""" ENGLISH_Lang=( {'%a':r'(?P[^\s\d]{3,3})', #Abbreviated weekday name. '%A':r'(?P[^\s\d]{6,9})', #Full weekday name. '%b':r'(?P[^\s\d]{3,3})', #Abbreviated month name. '%B':r'(?P[^\s\d]{3,9})', #Full month name. '%c':r'(?P\d\d)/(?P\d\d)/(?P\d\d) (?P\d\d):(?P\d\d):(?P\d\d)', #Appropriate date and time representation. '%p':r'(?P

(a|A|p|P)(m|M))', #Equivalent of either AM or PM. '%x':r'(?P\d\d)/(?P\d\d)/(?P\d\d)', #Appropriate date representation. '%X':r'(?P\d\d):(?P\d\d):(?P\d\d)'}, #Appropriate time representation. {'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, 'October':10, 'Oct':10, 'November':11, 'Nov':11, 'December':12, 'Dec':12}, {'Monday':0, 'Mon':0, #Adjusted for Monday-first counting. 'Tuesday':1, 'Tue':1, 'Wednesday':2, 'Wed':2, 'Thursday':3, 'Thu':3, 'Friday':4, 'Fri':4, 'Saturday':5, 'Sat':5, 'Sunday':6, 'Sun':6}, (('am','AM'),('pm','PM')) ) ENGLISH=LocaleAssembly(ENGLISH_Lang[0],ENGLISH_Lang[1],ENGLISH_Lang[2],ENGLISH_Lang[3]) SWEDISH_Lang=( {'%a':r'(?P[^\s\d]{3,3})', '%A':r'(?P[^\s\d]{6,7})', '%b':r'(?P[^\s\d]{3,3})', '%B':r'(?P[^\s\d]{3,8})', '%c':r'(?P[^\s\d]{3,3}) (?P[0-3]\d) (?P[^\s\d]{3,3}) (?P\d\d\d\d) (?P[0-2]\d):(?P[0-5]\d):(?P[0-6]\d)', '%p':r'(?P

(a|A|p|P)(m|M))', '%x':r'(?P\d\d)/(?P\d\d)/(?P\d\d)', '%X':r'(?P\d\d):(?P\d\d):(?P\d\d)'}, {'Januari':1, 'Jan':1, 'Februari':2, 'Feb':2, 'Mars':3, 'Mar':3, 'April':4, 'Apr':4, 'Maj':5, 'Maj':5, 'Juni':6, 'Jun':6, 'Juli':7, 'Jul':7, 'Augusti':8, 'Aug':8, 'September':9, 'Sep':9, 'Oktober':10, 'Okt':10, 'November':11, 'Nov':11, 'December':12, 'Dec':12}, {'Måndag':0, 'Mån':0, 'Tisdag':1, 'Tis':1, 'Onsdag':2, 'Ons':2, 'Torsdag':3, 'Tor':3, 'Fredag':4, 'Fre':4, 'Lördag':5, 'Lör':5, 'Söndag':6, 'Sön':6}, (('am','AM'),('pm','PM')) ) SWEDISH=LocaleAssembly(SWEDISH_Lang[0],SWEDISH_Lang[1],SWEDISH_Lang[2],SWEDISH_Lang[3]) class TimeObjError(Exception): """Generic TimeObj object exception""" def __init__(self, args=None): self.args=args class StrptimeError(TimeObjError): """Generic StrpObj object exception""" def __init__(self,args=None): self.args=args class TimeObj: """An object with basic time manipulation. VARIABLES: year -- Any integer. month -- [1,12] day -- [1,366] hour -- [0,23] minute -- [0,59] second -- [0,61] day_week -- [0,6] Day of the week. Monday is 0. daylight -- [-1,1] Daylight savings. METHODS: __init__ (self, year=None, month=None, day=None, hour=None, minute=None, second=None, day_week=None, julian_date=None, daylight=None) -- Sets up instance variables. __repr__(self) -> string -- Representation of instance. julianFirst(self) -> integer -- Calculates and returns the julian day for January 1 of this year. Requires that the year be known. gregToJulian(self) -> integer -- Figures out the julian date using the year, month, day, and julianFirst() method. julianToGreg(self) -> tuple_of_ints -- Figures out the Gregorian date according to the year and julian date. Returns the tuple in the format of (year, month, day). dayWeek(self) -> integer -- Figures out the day of the week according to year, month, and day. FillInInfo(self) -- Figures out what info is missing and can be filled in with the current knowledge and proceeds to do so using gregToJulian(), julianToGreg(), and dayWeek(). CheckIntegrity(self) -> integer -- Checks if the known information about the time is within allowed parameters. If a check fails, StrptimeError is raised. return_time(self) -> tuple -- Returns a tuple of the time information. All found instances of None are replaced replaced with 0. The format of the tuple is (year, month, day, hour, minute, second, day of the week, julian date, daylight savings). """ def __init__(self, year=None, month=None, day=None, hour=None, minute=None, second=None, day_week=None, julian_date=None, daylight=None): """Sets up instances variables. All values can be set at initialization. Any left out info is automatically set to None.""" self.year=year self.month=month self.day=day self.hour=hour self.minute=minute self.second=second self.day_week=day_week self.julian_date=julian_date self.daylight=daylight def __repr__(self): """Representation of the instance lists all info on the time""" return ''% ( self.year,self.month,self.day,self.hour,self.minute,self.second,self.day_week,self.julian_date,self.daylight) def julianFirst(self): """Calculates the julian day for the first of the current year. Uses self.year to figure out the year being used.""" a=(14-1)/12 y=(self.year+4800)-a m=1+12*a-3 julian_first=1+((153*m+2)/5)+365*y+y/4-y/100+y/400-32045 return julian_first def gregToJulian(self): """Converts the Gregorian date to the Julian date. Uses self.year, self.month, and self.day along with julianFirst(). If the Julian day needs to be calculated, just add the results of julianFirst() andgregToJulian().""" a=(14-self.month)/12 y=self.year+4800-a m=self.month+12*a-3 julian_day=self.day+((153*m+2)/5)+365*y+y/4-y/100+y/400-32045 julian_first=self.julianFirst() julian_date=julian_day-julian_first julian_date=julian_date+1 #To make result be same as what strftime would give. return julian_date def julianToGreg(self): """Converts the Julian date to the Gregorian date. Uses julianFirst() (which uses self.year) and self.julian_date.""" julian_first=self.julianFirst() julian_day=self.julian_date+julian_first julian_day=julian_day-1 a=julian_day+32044 b=(4*a+3)/146097 c=a-((146097*b)/4) d=(4*c+3)/1461 e=c-((1461*d)/4) m=(5*e+2)/153 day=e-((153*m+2)/5)+1 month=m+3-12*(m/10) year=100*b+d-4800+(m/10) return (year,month,day) def dayWeek(self): """Figures out the day of the week using self.year, self.month, and self.day. Monday is 0.""" a=(14-self.month)/12 y=self.year-a m=self.month+12*a-2 day_week=(self.day+y+(y/4)-(y/100)+(y/400)+((31*m)/12))%7 if day_week==0: day_week=6 else: day_week=day_week-1 return day_week def FillInInfo(self): """Based on the current time information, it figures out what other info can be filled in.""" if self.julian_date==None and self.year and self.month and self.day: julian_date=self.gregToJulian() self.julian_date=julian_date if (self.month==None or self.day==None) and self.year and self.julian_date: gregorian=self.julianToGreg() self.month=gregorian[1] #Year tossed out since needed to do calculation in the first place. self.day=gregorian[2] if self.day_week==None and self.year and self.month and self.day: self.dayWeek() def CheckIntegrity(self): """Checks info integrity based on the range that a number can be. Checking whether numbers are integers or not is unneeded since regex would catch that. Year is not checked since it can be any positive 4- digit integer number. Any invalid values raises a StrptimeError exception.""" success=1 if self.month!=None: if not (1<=self.month<=12): success=0 raise StrptimeError,'Month incorrect' if self.day!=None: if not 1<=self.day<=31: success=0 raise StrptimeError,'Day incorrect' if self.hour!=None: if not 0<=self.hour<=23: success=0 raise StrptimeError,'Hour incorrect' if self.minute!=None: if not 0<=self.minute<=59: success=0 raise StrptimeError,'Minute incorrect' if self.second!=None: if not 0<=self.second<=61: #61 covers leap seconds. success=0 raise StrptimeError,'Second incorrect' if self.day_week!=None: if not 0<=self.day_week<=6: success=0 raise StrptimeError,'Day of the Week incorrect' if self.julian_date!=None: if not 0<=self.julian_date<=366: success=0 raise StrptimeError,'Julian Date incorrect' if self.daylight!=None: if not -1<=self.daylight<=1: success=0 raise StrptimeError,'Daylight Savings incorrect' return success def return_time(self): """Returns a tuple in the format used by time.gmtime(). All instances of None in the information are replaced with 0.""" temp_time=[self.year, self.month, self.day, self.hour, self.minute, self.second, self.day_week, self.julian_date, self.daylight] for current in xrange(9): if temp_time[current]==None: temp_time[current]=0 return tuple(temp_time) class StrpObj(TimeObj): """Subclasses TimeObj to provide methods for functionality like time.strptime(). METHODS: RECreation(self, format, DIRECTIVEDict) -> re object -- Creates the re object used for extrapolating the info out of a string based on the passed-in format string. convert(self, string, format, locale_setting) -- Takes the string and extrapolates time info from it based on the format string and chosen locale. """ def RECreation(self, format, DIRECTIVEDict): """Takes in a format string and the DIRECTIVEDict and creates a regex object for convert().""" Directive=0 REString='' for char in format: if char=='%' and not Directive: Directive=1 elif Directive: if not DIRECTIVEDict.has_key('%'+char): raise GenericError else: REString=REString+DIRECTIVEDict['%'+char] Directive=0 else: REString=REString+char return re.compile(REString, re.IGNORECASE) def convert(self, string, format, locale_setting): """Extrapolates the info from the passed in string based on the format and locale setting. Uses DictAssembly() and RECreation().""" DIRECTIVEDict,MONTHDict,DAYDict,AM_PM=locale_setting REComp=self.RECreation(format, DIRECTIVEDict) reobj=REComp.match(string) for found in reobj.groupdict().keys(): if found in ('y','Y'): #Year if found=='y': #Without century self.year=CENTURY+int(reobj.group('y')) else: #With century self.year=int(reobj.group('Y')) elif found in ('b','B','m'): #Month if found=='m': self.month=int(reobj.group(found)) else: #Int try: self.month=MONTHDict[reobj.group(found)] except KeyError: raise StrptimeError, 'Unrecognized month' elif found=='d': #Day of the Month self.day=int(reobj.group(found)) elif found in ('H','I'): #Hour hour=int(reobj.group(found)) if found=='H': self.hour=hour else: try: if reobj.group('p') in AM_PM[0]: AP=0 else: AP=1 except KeyError: raise StrptimeError, 'Lacking needed AM/PM information' if AP: if hour==12: self.hour=12 else: self.hour=12+hour else: if hour==12: self.hour=0 else: self.hour=hour elif found=='M': #Minute self.minute=int(reobj.group(found)) elif found=='S': #Second self.second=int(reobj.group(found)) elif found in ('a','A','w'): #Day of the week if found=='w': day_value=int(reobj.group(found)) if day_value==0: self.day_week=6 else: self.day_week=day_value-1 else: try: self.day_week=DAYDict[reobj.group(found)] except KeyError: raise StrptimeError, 'Unrecognized day' elif found=='j': #Julian date self.julian_date=int(reobj.group(found)) elif found=='Z': #Daylight savings TZ=reobj.group(found) if len(TZ)==3: if TZ[1] in ('D','d'): self.daylight=1 else: self.daylight=0 elif TZ.find('Daylight')!=-1: self.daylight=1 else: self.daylight=0 def strptime(string, format='%a %b %d %H:%M:%S %Y', option=AS_IS, locale_setting=ENGLISH): """Creates a StrpObj instance and uses its methods to find out the tuple representation (as based on time.gmtime()) of the string passed in based on the format string and the locale setting. Also triggers the checking of the integrity of the extrapolated info and fills in missing gaps where possible based on the passed in option. It then returns the tuple representation of the instance.""" Obj=StrpObj() Obj.convert(string, format, locale_setting) if option in (FILL_IN,CHECK): Obj.CheckIntegrity() if option in (FILL_IN,): Obj.FillInInfo() return Obj.return_time()