Viewing file: Time.py (34.36 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
######################################################################## # $Header: /var/local/cvsroot/4Suite/Ft/Lib/Time.py,v 1.21 2005/04/06 23:36:48 jkloth Exp $ """ Date and time related functionality for use within 4Suite only.
This module is experimental and may not be staying in 4Suite for long; application developers should avoid forming dependencies on it.
Copyright 2005 Fourthought, Inc. (USA). Detailed license and copyright information: http://4suite.org/COPYRIGHT Project home, documentation, distributions: http://4suite.org/ """
import re, time, calendar, rfc822
_month_days = ( (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), )
def DayOfYearFromYMD(year, month, day): """ Calculates the Julian day (day of year, between 1 and 366), for the given date. This function is accurate for dates back to 01 Jan 0004 (that's 4 A.D.), when the Julian calendar stabilized. """ days_table = _month_days[calendar.isleap(year)] days = 0 for m in range(1, month): days += days_table[m] return days + day
def WeekdayFromYMD(year, month, day): """ Calculates the day of week (0=Mon, 6=Sun) for the given date. This function is accurate for dates on/after Friday, 15 Oct 1582, when the Gregorian reform took effect, although it should be noted that some nations didn't adopt the Gregorian calendar until as late as the 20th century, so dates that were referenced before then would have fallen on a different day of the week, at the time. """
# An alternate way of calculating weekdays is to let the python time # module do it for you, like this: # # time.localtime() on Windows won't take negative arguments! # min_year = time.localtime(0)[0] + 1 # max_year = time.localtime(sys.maxint)[0] - 1 # if self._localYear > min_year and self._localYear < max_year: # secsSinceEpoch = time.mktime(self.asPythonTimeTuple(local=1)) # self._utcWeekday = time.gmtime(secsSinceEpoch)[6] # self._localWeekday = time.localtime(secsSinceEpoch)[6] # # However, this can only be used with dates that time.localtime() can # handle, which varies from system to system and is likely to be within # the bounds of 1970 and 2037.
# We use a convenient epoch of 2001-01-01: it was a Monday if year == 2001: days_from_epoch = 0 else: years = range(2001, year, cmp(year, 2001)) leap_days = len(filter(calendar.isleap, years)) days_from_epoch = len(years) * 365 + leap_days if year < 2001: # add the days after this date in the year day_of_year = DayOfYearFromYMD(year, month, day) days_from_epoch += (365 + calendar.isleap(year) - day_of_year) # this is the total number of days before the 20010101 epoch days_from_epoch *= -1 else: # this is the total number of days after the 20010101 epoch days_from_epoch += (DayOfYearFromYMD(year, month, day) - 1) return days_from_epoch % 7
class DT: """ A class that contains the data needed to represent a single point in time using many different date and time formats.
Its constructor requires a UTC (GMT) date and time (year, month (0-11), day (0-31), hour (0-23), minute (0-59), second (0-59), millisecond (0-999), plus some information to help express this time in local terms: a local time zone name, or if that's not available, an hour offset of the local time from GMT (-11 to 14, typically), a minute offset of the local time from GMT (0 or 30, usually), and an optional flag indicating Daylight Savings Time, to help determine the time zone name. """
def __init__(self, year, month, day, hour, #In GMT minute, #in GMT second, milliSecond, daylightSavings, #1 means yes tzName, tzHourOffset, tzMinuteOffset):
tzMinuteOffset = int(tzMinuteOffset) tzHourOffset = int(tzHourOffset) milliSecond = float(milliSecond) second = int(second) minute = int(minute) hour = int(hour) day = int(day) month = int(month) year = int(year) daylightSavings = not not daylightSavings #This is purely to help look up the tzName
if tzName: self._tzName = tzName else:
k = float(tzHourOffset) + float(tzMinuteOffset)/60.0 d = self.tzNameTable.get(k) if d: if daylightSavings: n = d[3] if not n: n = d[2] else: n = d[2] if not n: n = d[0] self._tzName = n else: self._tzName = ''
#Normalize the milli-seconds between 0 and 999 secondShift = 0 while milliSecond < 0: secondShift -=1 milliSecond += 1000 while milliSecond > 999: secondShift +=1 milliSecond -=1000 self._milliSecond = milliSecond
#Normalize the seconds between 0 and 59 second += secondShift minuteShift = 0 while second < 0: minuteShift -=1 second += 60 while second > 59: minuteShift +=1 second -=60 self._second = second
#Normalize Minute between 0 and 59 utcHourShift, self._utcMinute = self.__normalizeMinute(minute + minuteShift) localHourShift, self._localMinute = self.__normalizeMinute(minute + minuteShift + tzMinuteOffset)
#Normalize Hour between 0 and 23 utcDayShift, self._utcHour = self.__normalizeHour(hour + utcHourShift) localDayShift, self._localHour = self.__normalizeHour(hour + localHourShift + tzHourOffset)
#Normalize Date as one between 0 and max day of month self._utcYear, self._utcMonth, self._utcDay = self.__normalizeDate(day + utcDayShift, month, year) self._localYear, self._localMonth, self._localDay = self.__normalizeDate(day + localDayShift, month, year)
self._tzHourOffset = tzHourOffset self._tzMinuteOffset = tzMinuteOffset
#Set Day In Year (Julian day) self._utcDayOfYear = DayOfYearFromYMD(self._utcYear, self._utcMonth, self._utcDay) self._localDayOfYear = DayOfYearFromYMD(self._localYear, self._localMonth, self._localDay)
#Set Weekday self._utcWeekday = WeekdayFromYMD(self._utcYear, self._utcMonth, self._utcDay) self._localWeekday = WeekdayFromYMD(self._localYear, self._localMonth, self._localDay)
#Lastly, set this for XPath self.stringValue = self.asISO8601DateTime(local=1)
def asISO8601DateTime(self, local=0): """ Represents this DT object as an ISO 8601 date-time string, using UTC time like '2001-01-01T00:00:00Z' if local=0, or local time with UTC offset like '2000-12-31T17:00:00-07:00' if local=1. """ return "%s%s" % (self.asISO8601Date(local), self.asISO8601Time(local))
def asISO8601Date(self, local=0): """ Represents this DT object as an ISO 8601 date-time string, like '2001-01-01' if local=0, or '2000-12-31' if local=1. The local date may vary from UTC date depending on the time of day that is stored in the object. """ if local: y = self._localYear m = self._localMonth d = self._localDay else: y = self._utcYear m = self._utcMonth d = self._utcDay
return "%d-%02d-%02d" % (y, m, d)
def asISO8601Time(self, local=0): """ Represents this DT object as an ISO 8601 time string, using UTC time like 'T00:00:00Z' if local=0, or local time with UTC offset like 'T17:00:00-07:00' if local=1 """ if local: h = self._localHour m = self._localMinute useTz = 1 else: h = self._utcHour m = self._utcMinute useTz = 0 s = self._second ms = self._milliSecond * 100 rt = "T%02d:%02d:%02d" % (h, m, s) if ms: t = "%d" % ms if len(t) > 3: t = t[:3] while(t and t[-1] == '0'): t = t[:-1] rt += "," + t
if not useTz or (not self._tzHourOffset and not self._tzMinuteOffset): rt += "Z" else: if self._tzHourOffset < 0: sign = "-" tzh = -1 * self._tzHourOffset else: sign = "+" tzh = self._tzHourOffset rt += ("%s%02d:%02d" % (sign, tzh, self._tzMinuteOffset))
return rt
def asRFC822DateTime(self, local=0): """ Represents this DT object as an RFC 1123 (which updated RFC 822) date string, using UTC time like 'Mon, 01 Jan 2001 00:00:00 GMT' if local=0, or local time with time zone indicator or offset like 'Sun, 31 Dec 2000 17:00:00 MDT' if local=1. Although RFC 822 allows the weekday to be optional, it is always included in the returned string. """ if local: wday = self.abbreviatedWeekdayNameTable[self._localWeekday] mon = self.abbreviatedMonthNameTable[self._localMonth] day = self._localDay year = self._localYear hour = self._localHour minute = self._localMinute # RFC 822 only allows certain timezone names if self._tzName and self._tzName in ['GMT', 'EST', 'EDT', 'CST', 'CDT', 'MST', 'MDT', 'PST', 'PDT']: tz = self._tzName else: tz = '%+03d%02d' % (self._tzHourOffset, self._tzMinuteOffset) else: wday = self.abbreviatedWeekdayNameTable[self._utcWeekday] mon = self.abbreviatedMonthNameTable[self._utcMonth] day = self._utcDay year = self._utcYear hour = self._utcHour minute = self._utcMinute tz = "GMT"
# "Thu, 04 Jan 2001 09:15:39 MDT" # RFC 1123 changed the RFC 822 format to use 4-digit years return "%s, %02d %s %d %02d:%02d:%02d %s" % (wday, day, mon, year, hour, minute, self._second, tz)
def asPythonTime(self, local=0): """ Returns the stored date and time as a float indicating the number of seconds since the local machine's epoch. """ return time.mktime(self.asPythonTimeTuple(local))
def asPythonTimeTuple(self, local=0): """ Returns the stored date and time as a Python time tuple, as documented in the time module. If the tuple is going to be passed to a function that expects the local time, set local=1. The Daylight Savings flag is always -1, which means unknown, and may or may not have ramifications. """ if local: return (self._localYear, self._localMonth, self._localDay, self._localHour, self._localMinute, self._second, self._localWeekday, self._localDayOfYear, -1) else: return (self._utcYear, self._utcMonth, self._utcDay, self._utcHour, self._utcMinute, self._second, self._utcWeekday, self._utcDayOfYear, -1)
def year(self, local=0): """ Returns the year component of the stored date and time as an int like 2001. """ if local: return self._localYear return self._utcYear
def month(self, local=0): """ Returns the month component of the stored date and time as an int in the range 0-11. """ if local: return self._localMonth return self._utcMonth
def monthName(self, local=0): """ Returns the month component of the stored date and time as a string like 'January'. """ if local: return self.monthNameTable[self._localMonth] return self.monthNameTable[self._utcMonth]
def abbreviatedMonthName(self, local=0): """ Returns the month component of the stored date and time as a string like 'Jan'. """ if local: return self.abbreviatedMonthNameTable[self._localMonth] return self.abbreviatedMonthNameTable[self._utcMonth]
def day(self, local=0): """ Returns the day component of the stored date and time as an integer in the range 1-31. """ if local: return self._localDay return self._utcDay
def dayOfYear(self, local=0): """ Returns the day of year component of the stored date and time as an int in the range 1-366. """ if local: return self._localDayOfYear return self._utcDayOfYear
def dayOfWeek(self, local=0): """ Returns the day of week component of the stored date and time as an int in the range 0-6 (0=Monday). """ if local: return self._localWeekday return self._utcWeekday
def hour(self, local=0): """ Returns the hour component of the stored date and time as an int in the range 0-23. """ if local: return self._localHour return self._utcHour
def minute(self, local=0): """ Returns the minute component of the stored date and time as an int in the range 0-59. """ if local: return self._localMinute return self._utcMinute
def second(self): """ Returns the second component of the stored date and time as an int in the range 0-59. """ return self._second
def milliSecond(self): """ Returns the millisecond component of the stored date and time as an int in the range 0-999. """ return self._milliSecond
def tzName(self): """ Returns the local time's time zone name component of the stored date and time as a string like 'MST'. """ return self._tzName
def tzHourOffset(self): """ Returns the local time's hour offset from GMT component of the stored date and time as an int, typically in the range -12 to 14. """ return self._tzHourOffset
def tzMinuteOffset(self): """ Returns the local time's minute offset from GMT component of the stored date and time as an int in the range 0-59. """ return self._tzMinuteOffset
def __normalizeMinute(self, minute): hourShift = 0 while minute < 0: hourShift -=1 minute += 60 while minute > 59: hourShift +=1 minute -= 60 return hourShift, minute
def __normalizeHour(self, hour): dayShift = 0 while hour < 0: dayShift -=1 hour += 24 while hour > 23: dayShift +=1 hour -= 24 return dayShift, hour
def __normalizeDate(self, day, month, year): # Returns a valid year, month and day, given a day value that is out # of the acceptable range. This is needed so that the correct local # date can be determined after adding the local time offset to the # UTC time. The time difference may result in the day being shifted, # for example Jan 1 may become Jan 0, which needs to be normalized # to Dec 31 of the preceding year. This function may also be used to # convert a Julian day (1-366) for the given year to a proper year, # month and day, if the month is initially set to 1. while (month < 1 or month > 12 or day < 1 or day > _month_days[calendar.isleap(year)][month] ): if month < 1: year -= 1 month += 12 elif month > 12: year += 1 month -= 12 elif day < 1: month -= 1 if month == 0: #Special case day += 31 else: day += _month_days[calendar.isleap(year)][month] elif day > _month_days[calendar.isleap(year)][month]: day -= _month_days[calendar.isleap(year)][month] month += 1 return year, month, day
#Pythonic Interface __str__ = asISO8601DateTime
def __cmp__(self, other): if isinstance(other, (str, unicode)): return cmp(self.asISO8601DateTime(), other) elif isinstance(other, (int, float)): return cmp(self.asPythonTime(), other) elif not isinstance(other, DT): raise TypeError("Cannot Compare DT with %s" % repr(other)) #Compare two instances #For now, compare our strings return cmp(self.asISO8601DateTime(), other.asISO8601DateTime())
def __hash__(self): return id(self)
#For internal lookups abbreviatedMonthNameTable = ('ERR', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec')
monthNameTable = ('ERROR', 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December')
weekdayNameTable = ('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday')
abbreviatedWeekdayNameTable = ('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun')
# keyed by offset; # values are (GMT TZ, military TZ, most likely civ TZ, # most likely civ TZ if on summer/daylight savings time) tzNameTable = { +0 : ("GMT", "Zulu", "GMT", "BST"), +1 : ("GMT+1", "Alpha", "CET", "MEST"), +2 : ("GMT+2", "Bravo", "EET", ""), +3 : ("GMT+3", "Charlie", "BT", ""), +3.5 : ("GMT+3:30", "", "", ""), +4 : ("GMT+4", "Delta", "", ""), +4.5 : ("GMT+4:30", "", "", ""), +5 : ("GMT+5", "Echo", "", ""), +5.5 : ("GMT+5:30", "", "", ""), +6 : ("GMT+6", "Foxtrot", "", ""), +6.5 : ("GMT+6:30", "", "", ""), +7 : ("GMT+7", "Golf", "WAST", ""), +8 : ("GMT+8", "Hotel", "CCT", ""), +9 : ("GMT+9", "India", "JST", ""), +9.5 : ("GMT+9:30", "", "Australia Central Time", ""), +10 : ("GMT+10", "Kilo", "GST", ""), +10.5 : ("GMT+10:30", "", "", ""), +11 : ("GMT+11", "Lima", "", ""), +11.5 : ("GMT+11:30", "", "", ""), +12 : ("GMT+12", "Mike", "NZST", ""), +13 : ("GMT+13", "", "", ""), +14 : ("GMT+14", "", "", ""), -1 : ("GMT-1", "November", "WAT", ""), -2 : ("GMT-2", "Oscar", "AT", ""), -3 : ("GMT-3", "Papa", "", "ADT"), -3.5 : ("GMT-3", "", "", ""), -4 : ("GMT-4", "Quebec", "AST", "EDT"), -5 : ("GMT-5", "Romeo", "EST", "CDT"), -6 : ("GMT-6", "Sierra", "CST", "MDT"), -7 : ("GMT-7", "Tango", "MST", "PDT"), -8 : ("GMT-8", "Uniform", "PST", ""), -8.5 : ("GMT-8:30", "", "", "YDT"), -9 : ("GMT-9", "Victor", "YST", ""), -9.5 : ("GMT-9:30", "", "", "HDT"), -10 : ("GMT-10", "Whiskey", "AHST", ""), -11 : ("GMT-11", "XRay", "NT", ""), -12 : ("GMT-11", "Yankee", "IDLW", ""), }
CENTURY="(?P<Century>[0-9]{2,2})" YEAR="(?P<Year>[0-9]{2,2})" MONTH="(?P<Month>[0-9]{2,2})" DAY="(?P<Day>[0-9]{2,2})" BASIC_DATE="%s?%s%s%s" % (CENTURY, YEAR, MONTH, DAY) EXTENDED_DATE="%s?%s-%s-%s" % (CENTURY, YEAR, MONTH, DAY) YEAR_AND_MONTH_DATE="(-|%s)%s-%s" % (CENTURY, YEAR, MONTH) YEAR_AND_MONTH_DATE_EXTENDED="-%s%s" % (YEAR, MONTH) YEAR_ONLY_DATE="(-|%s)%s" % (CENTURY, YEAR) CENTURY_ONLY_DATE=CENTURY DAY_OF_MONTH="--%s(?:-?%s)?" % (MONTH, DAY) DAY_ONLY_DATE="---%s" % (DAY)
#build the list of calendar date expressions cd_expressions = [BASIC_DATE, EXTENDED_DATE, YEAR_AND_MONTH_DATE, YEAR_AND_MONTH_DATE_EXTENDED, YEAR_ONLY_DATE, CENTURY_ONLY_DATE, DAY_OF_MONTH, DAY_ONLY_DATE] cd_expressions = map(lambda x:"(?P<CalendarDate>%s)" % x, cd_expressions)
ORDINAL_DAY="(?P<Ordinal>[0-9]{3,3})" ORDINAL_DATE="(?P<OrdinalDate>%s?%s-?%s)" % (CENTURY, YEAR, ORDINAL_DAY) ORDINAL_DATE_ONLY="(?P<OrdinalDate>-%s)" % (ORDINAL_DAY) od_expressions = [ORDINAL_DATE, ORDINAL_DATE_ONLY]
WEEK="(?P<Week>[0-9][0-9])" WEEK_DAY="(?P<Weekday>[1-7])" BASIC_WEEK_DATE="%s?%sW%s%s?" %(CENTURY, YEAR, WEEK, WEEK_DAY) EXTENDED_WEEK_DATE="%s?%s-W%s(?:-%s)?" %(CENTURY, YEAR, WEEK, WEEK_DAY) WEEK_IN_DECADE="-(?P<YearInDecade>[0-9])W%s%s" % (WEEK, WEEK_DAY) WEEK_IN_DECADE_EXTENDED="-(?P<YearInDecade>[0-9])-W%s-%s" % (WEEK, WEEK_DAY) WEEK_AND_DAY_BASIC="-W%s(?:-?%s)?"%(WEEK, WEEK_DAY) WEEKDAY_ONLY="-W?-%s" % (WEEK_DAY)
#build the list of week date expressions wd_expressions=[BASIC_WEEK_DATE, EXTENDED_WEEK_DATE, WEEK_IN_DECADE, WEEK_IN_DECADE_EXTENDED, WEEK_AND_DAY_BASIC, WEEKDAY_ONLY] wd_expressions = map(lambda x:"(?P<WeekDate>%s)" % x, wd_expressions)
#Build the list of date expressions date_expressions = map(lambda x:"(?P<Date>%s)" % x, cd_expressions+od_expressions+wd_expressions)
HOUR="(?P<Hour>(?:0[0-9])|(?:1[0-9])|(?:2[0-4]))" MINUTE="(?P<Minute>(?:[0-5][0-9])|(?:60))" SECOND="(?P<Second>(?:[0-5][0-9])|(?:60))" DECIMAL_SEPARATOR="(?:\.|,)" DECIMAL_VALUE="(?P<DecimalValue>[0-9]*)"
BASIC_TIME_FORMAT="(?:%s%s%s(?:%s%s)?)" % (HOUR, MINUTE, SECOND, DECIMAL_SEPARATOR, DECIMAL_VALUE) EXTENDED_TIME_FORMAT="(?:%s:%s:%s(?:%s%s)?)" % (HOUR, MINUTE, SECOND, DECIMAL_SEPARATOR, DECIMAL_VALUE)
HOUR_MINUTE_TIME="(?:%s:?%s(?:%s%s)?)" % (HOUR, MINUTE, DECIMAL_SEPARATOR, DECIMAL_VALUE) HOUR_TIME="(?:%s(?:%s%s)?)" % (HOUR, DECIMAL_SEPARATOR, DECIMAL_VALUE) MINUTE_SECOND_TIME="(?:-%s:?%s(?:%s%s)?)" % (MINUTE, SECOND, DECIMAL_SEPARATOR, DECIMAL_VALUE) MINUTE_TIME="(?:-%s(?:%s%s)?)" % (MINUTE, DECIMAL_SEPARATOR, DECIMAL_VALUE) SECOND_TIME="(?P<CurrentSecond>--%s(?:%s%s)?)" % (SECOND, DECIMAL_SEPARATOR, DECIMAL_VALUE)
#build the basic time expressions bt_expressions = [BASIC_TIME_FORMAT, EXTENDED_TIME_FORMAT, HOUR_MINUTE_TIME, HOUR_TIME, MINUTE_SECOND_TIME, MINUTE_TIME, SECOND_TIME] bt_expressions = map(lambda x:"(?P<Time>%s)"%x, bt_expressions)
UTC_TIME_ZONE="Z" TZ_DIRECTION="(?P<TzDirection>\+|-)" TZ_HOUR="(?P<TzHour>(?:0[0-9])|(?:1[0-9])|(?:2[0-4]))" TZ_MINUTE="(?P<TzMinute>(?:[0-5][0-9])|(?:60))" BASIC_TIME_ZONE="(?P<TzOffset>%s%s(?::?%s)?)" % (TZ_DIRECTION, TZ_HOUR, TZ_MINUTE) TIME_ZONE="(?P<TimeZone>%s|%s)" % (UTC_TIME_ZONE, BASIC_TIME_ZONE)
#build the tz expressions tz_expressions=map(lambda x, t=TIME_ZONE: "%s%s?" % (x, t), bt_expressions)
#Lastly build the list of all possible expressions g_isoExpressions = [] #First, All Date expressions for e in date_expressions: g_isoExpressions.append(["^"+e+"$", None])
#Then, all Time expressions. Not to worry about clashes because date has precedence and is first for e in tz_expressions: g_isoExpressions.append(["^"+e+"$", None]) for e in tz_expressions: g_isoExpressions.append(["^T"+e+"$", None])
#now add the combination of the two for d in date_expressions: for t in tz_expressions: g_isoExpressions.append(["^"+d+"T"+t+"$", None])
#cleanup namespace a bit del BASIC_DATE, BASIC_TIME_FORMAT, BASIC_TIME_ZONE, BASIC_WEEK_DATE del CENTURY, CENTURY_ONLY_DATE del DAY, DAY_OF_MONTH, DAY_ONLY_DATE, DECIMAL_SEPARATOR, DECIMAL_VALUE del EXTENDED_DATE, EXTENDED_TIME_FORMAT, EXTENDED_WEEK_DATE del HOUR, HOUR_MINUTE_TIME, HOUR_TIME del MINUTE, MINUTE_SECOND_TIME, MINUTE_TIME, MONTH del ORDINAL_DATE, ORDINAL_DATE_ONLY, ORDINAL_DAY del SECOND, SECOND_TIME del TIME_ZONE, TZ_DIRECTION, TZ_HOUR del TZ_MINUTE, UTC_TIME_ZONE, del WEEK, WEEKDAY_ONLY, WEEK_AND_DAY_BASIC, WEEK_DAY, WEEK_IN_DECADE, WEEK_IN_DECADE_EXTENDED del YEAR, YEAR_AND_MONTH_DATE, YEAR_AND_MONTH_DATE_EXTENDED, YEAR_ONLY_DATE, del bt_expressions, cd_expressions, d, date_expressions, e, od_expressions, t, tz_expressions, wd_expressions
def FromISO8601(st): """ Create a DT object from an ISO 8601 date, time or date-time string. The DT object must contain a complete date and time, and the ISO 8601 string might represent a partial date or time, so some assumptions are made about the 'implied' information (ISO 8601's terminology). """
global g_isoExpressions for d in g_isoExpressions: e, c = d if c is None: c = re.compile(e) d[1] = c g = c.match(st) if g: break else: raise SyntaxError("Invalid ISO-8601 format: %s" % st)
#Create the information we need year = 0 month = 0 day = 0 hour = 0 minute = 0 second = 0 milliSecond = 0 tzHourOffset = 0 tzMinuteOffset = 0 tzName = "" dst = 0
gd = g.groupdict()
if gd.has_key('Time'): #Look for h, m, s, tzh, tzm if gd.has_key('Hour'): hour = int(gd['Hour']) else: #current hour #Minute and Second of the current hour hour = time.localtime(time.time())[3]
if gd.has_key('CurrentSecond'): #Second of the current Minute minute = time.localtime(time.time())[4] else: minute = int(gd.get('Minute', 0)) second = int(gd.get('Second', 0)) if gd.get("DecimalValue") is not None: #Turn it into a decimal den = len(gd['DecimalValue']) * 10.0 val = float(gd["DecimalValue"]) / den if gd.get('Second') != None: #Decimal Value applies to seconds. milliSecond = val * 1000.0 elif gd.get('Minute') != None: #Decimal value applies to minute second += int(val*60.0)
#milli seconds could be effected ms = (val*60.0) - float(int(float(val*60.0))) milliSecond += ms * 100.0 else: #Decimal value applies to hour minute += int(val*60.0)
#seconds could be effected s = (val*60.0) - float(int(float(val*60.0))) second += s * 60.0
auto_dst = 0 if gd.has_key('TimeZone') and gd['TimeZone'] != 'Z' and gd['TimeZone'] != None: if gd['TzDirection'] == '-': tzMod = -1.0 else: tzMod = 1.0 tzHourOffset = float(gd['TzHour']) * tzMod if gd['TzMinute'] is not None: tzMinuteOffset = float(gd['TzMinute']) * tzMod #DT accepts local time. hour -= tzHourOffset minute -= tzMinuteOffset
elif gd.has_key('TimeZone') and gd['TimeZone'] == 'Z': #Use the local TZ settings for the offset but don't adjust #tzHourOffset = time.timezone/-3600 #tzMinuteOffset = int((time.timezone%3600) * -60.0) #if time.daylight: # tzHourOffset += 1 # tzName = time.tzname[1] #else: # tzName = time.tzname[0] auto_dst = 1
# Construct a 'ccyy' format year from optional components Century and Year # cc = given Century or current century # yy = given Year or current year if no cc # ccyy = cc * 100 + yy, or current year if no cc or yy thisyear = time.localtime(time.time())[0] if gd.get('Date') is not None: year = int('%02d%02d' % (int(gd.get('Century') or thisyear / 100), int(gd.get('Year') or thisyear % 100), ) ) else: year = thisyear del thisyear if gd.get('CalendarDate') is not None: if gd['CalendarDate'][:3] == '---': #Special Case month = time.localtime(time.time())[1] else: month += int(gd.get('Month', 1)) day += int(gd.get('Day') or 1) elif gd.get('OrdinalDate') is not None: month = 1 day += int(gd.get('Ordinal') or 1) elif gd.get('WeekDate') is not None: #First ordinal week number is the first week that has a Thursday??? #Adjust the day so it is at day zero if the first week #We know what year we are dealing with
if gd.get("YearInDecade") != None: #Use the current decade and the Year in decade to adjust the year year = int(time.localtime(time.time())[0]/10.0) * 10 year += int(gd['YearInDecade'])
#We know that 2001-01-01 is a Monday weekDayOfFirst = 1 y = year while y < 2001: daysInYear = calendar.isleap(y) and 366 or 365 weekDayOfFirst = (weekDayOfFirst - daysInYear) % 7 y += 1 while y > 2001: daysInYear = calendar.isleap(y) and 366 or 365 weekDayOfFirst = (weekDayOfFirst + daysInYear) % 7 y -= 1
if weekDayOfFirst > 4: #The first is Fri, Sat or Sun day += 7-weekDayOfFirst+1 else: #The first is Mon, Tues, Wed, Thur day -= (weekDayOfFirst - 1) #day has now been adjusted to the proper start of the first week month = 1 #Start at the first month day += (int(gd['Week'])-1) * 7 #Add 7 days / week day += int(gd['Weekday']or 1) #Add the number of week days
if month == 0: month = time.localtime(time.time())[1] if day == 0: day = time.localtime(time.time())[2]
# automatically figure out dst and offset if auto_dst: dst = isDST((year, month, day, hour, minute, second, 0, 0, -1)) if dst: offset_secs = time.altzone tzName = time.tzname[1] else: offset_secs = time.timezone tzName = time.tzname[0] tzHourOffset = -offset_secs / 3600 tzMinuteOffset = abs(offset_secs) % 60 del auto_dst
return DT(year, month, day, hour, minute, second, milliSecond, dst, tzName, tzHourOffset, tzMinuteOffset)
def FromRFC822(st): """ Create a DT object from an RFC 822/1123 date string """ t = rfc822.parsedate_tz(st) #t is in local time with a tz offset if not t or len(t) != 10: raise SyntaxError("Invalid RFC 822 date: '%s'" % st) year, month, day, hour, min, sec, temp, temp, temp, offset = t
if year < 100: year += 2000
if offset is None: minOff = 0 hourOff = 0 else: secOff = offset % 60 minOff = ((offset - secOff)/60.0) % 60 hourOff = (offset/60.0 - minOff) / 60.0
#See if there was a tzname on it data = st.split() if len(data) == 6 or (st.count(',') == 1 and len(data) == 5): tz = data[-1] else: tz = ""
#print "Hour: %d" % hour #print "HourOffset: %d" % hourOff
return DT(year, month, day, hour - hourOff, min - minOff, sec, 0, 0, tz, hourOff, minOff)
def FromPythonTime(t=None): """ Create a DT object from a float that represents seconds elapsed since the local machine's epoch. If not specified, then current time is used. """ if t is None: t = time.time() if t >=0: return FromPythonTimeTuple(time.gmtime(t)) else: raise ValueError("%r is not a valid time value" % t)
def FromPythonTimeTuple(t): """ Create a DT object from a Python time tuple as documented in the time module. This 9-tuple must represent a UTC date and time. """ (year, month, day, hour, minute, second, temp, temp, temp) = t if isDST(t): offset_secs = time.altzone name = time.tzname[1] else: offset_secs = time.timezone name = time.tzname[0] hourOff = -offset_secs / 3600 minOff = abs(offset_secs) % 60 return DT(year, month, day, hour, minute, second, 0, 0, name, hourOff, minOff)
def isDST(t): """ Indicates whether the given UTC time tuple corresponds to a date and time that falls during Daylight Savings Time in the local time zone. """ return time.localtime(calendar.timegm(t))[8] == 1
|