blob: 263a2a6c4d0b21463773a2e89d4f6fd624ef741b [file] [log] [blame]
Guido van Rossum00efe7e2002-07-19 17:04:46 +00001"""Strptime-related classes and functions.
2
3CLASSES:
4 LocaleTime -- Discovers and/or stores locale-specific time information
Tim Peters469cdad2002-08-08 20:19:19 +00005 TimeRE -- Creates regexes for pattern matching string of text containing
Guido van Rossum00efe7e2002-07-19 17:04:46 +00006 time information as is returned by time.strftime()
7
8FUNCTIONS:
9 firstjulian -- Calculates the Julian date up to the first of the specified
10 year
11 gregorian -- Calculates the Gregorian date based on the Julian day and
12 year
Tim Peters469cdad2002-08-08 20:19:19 +000013 julianday -- Calculates the Julian day since the first of the year based
Guido van Rossum00efe7e2002-07-19 17:04:46 +000014 on the Gregorian date
15 dayofweek -- Calculates the day of the week from the Gregorian date.
16 strptime -- Calculates the time struct represented by the passed-in string
17
18Requires Python 2.2.1 or higher.
19Can be used in Python 2.2 if the following line is added:
20 >>> True = 1; False = 0
Guido van Rossum00efe7e2002-07-19 17:04:46 +000021"""
22import time
23import locale
24import calendar
25from re import compile as re_compile
26from re import IGNORECASE
27from string import whitespace as whitespace_string
28
Barry Warsaw35816e62002-08-29 16:24:50 +000029__version__ = (2,1,6)
Guido van Rossum00efe7e2002-07-19 17:04:46 +000030__author__ = "Brett Cannon"
31__email__ = "drifty@bigfoot.com"
32
33__all__ = ['strptime']
34
Barry Warsaw35816e62002-08-29 16:24:50 +000035RegexpType = type(re_compile(''))
36
37
Guido van Rossum00efe7e2002-07-19 17:04:46 +000038class LocaleTime(object):
39 """Stores and handles locale-specific information related to time.
40
41 ATTRIBUTES (all read-only after instance creation! Instance variables that
42 store the values have mangled names):
43 f_weekday -- full weekday names (7-item list)
44 a_weekday -- abbreviated weekday names (7-item list)
Tim Peters469cdad2002-08-08 20:19:19 +000045 f_month -- full weekday names (14-item list; dummy value in [0], which
Guido van Rossum00efe7e2002-07-19 17:04:46 +000046 is added by code)
Tim Peters469cdad2002-08-08 20:19:19 +000047 a_month -- abbreviated weekday names (13-item list, dummy value in
Guido van Rossum00efe7e2002-07-19 17:04:46 +000048 [0], which is added by code)
49 am_pm -- AM/PM representation (2-item list)
50 LC_date_time -- format string for date/time representation (string)
51 LC_date -- format string for date representation (string)
52 LC_time -- format string for time representation (string)
Tim Peters469cdad2002-08-08 20:19:19 +000053 timezone -- daylight- and non-daylight-savings timezone representation
54 (3-item list; code tacks on blank item at end for
Guido van Rossum00efe7e2002-07-19 17:04:46 +000055 possible lack of timezone such as UTC)
56 lang -- Language used by instance (string)
Guido van Rossum00efe7e2002-07-19 17:04:46 +000057 """
58
Tim Peters469cdad2002-08-08 20:19:19 +000059 def __init__(self, f_weekday=None, a_weekday=None, f_month=None,
Barry Warsaw35816e62002-08-29 16:24:50 +000060 a_month=None, am_pm=None, LC_date_time=None, LC_time=None,
61 LC_date=None, timezone=None, lang=None):
Guido van Rossum00efe7e2002-07-19 17:04:46 +000062 """Optionally set attributes with passed-in values."""
Barry Warsaw35816e62002-08-29 16:24:50 +000063 if f_weekday is None:
64 self.__f_weekday = None
65 elif len(f_weekday) == 7:
66 self.__f_weekday = list(f_weekday)
Guido van Rossum00efe7e2002-07-19 17:04:46 +000067 else:
68 raise TypeError("full weekday names must be a 7-item sequence")
Barry Warsaw35816e62002-08-29 16:24:50 +000069 if a_weekday is None:
70 self.__a_weekday = None
71 elif len(a_weekday) == 7:
72 self.__a_weekday = list(a_weekday)
Guido van Rossum00efe7e2002-07-19 17:04:46 +000073 else:
74 raise TypeError(
Barry Warsaw35816e62002-08-29 16:24:50 +000075 "abbreviated weekday names must be a 7-item sequence")
76 if f_month is None:
77 self.__f_month = None
Guido van Rossum00efe7e2002-07-19 17:04:46 +000078 elif len(f_month) == 12:
79 self.__f_month = self.__pad(f_month, True)
80 else:
81 raise TypeError("full month names must be a 12-item sequence")
Barry Warsaw35816e62002-08-29 16:24:50 +000082 if a_month is None:
83 self.__a_month = None
Guido van Rossum00efe7e2002-07-19 17:04:46 +000084 elif len(a_month) == 12:
85 self.__a_month = self.__pad(a_month, True)
86 else:
87 raise TypeError(
Barry Warsaw35816e62002-08-29 16:24:50 +000088 "abbreviated month names must be a 12-item sequence")
Guido van Rossum00efe7e2002-07-19 17:04:46 +000089 if am_pm is None:
90 self.__am_pm = None
91 elif len(am_pm) == 2:
92 self.__am_pm = am_pm
93 else:
94 raise TypeError("AM/PM representation must be a 2-item sequence")
95 self.__LC_date_time = LC_date_time
96 self.__LC_time = LC_time
97 self.__LC_date = LC_date
98 self.__timezone = timezone
99 if timezone:
100 if len(timezone) != 2:
101 raise TypeError("timezone names must contain 2 items")
102 else:
103 self.__timezone = self.__pad(timezone, False)
104 self.__lang = lang
105
106 def __pad(self, seq, front):
Barry Warsaw35816e62002-08-29 16:24:50 +0000107 # Add '' to seq to either front (is True), else the back.
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000108 seq = list(seq)
Barry Warsaw35816e62002-08-29 16:24:50 +0000109 if front:
110 seq.insert(0, '')
111 else:
112 seq.append('')
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000113 return seq
114
115 def __set_nothing(self, stuff):
Barry Warsaw35816e62002-08-29 16:24:50 +0000116 # Raise TypeError when trying to set an attribute.
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000117 raise TypeError("attribute does not support assignment")
118
119 def __get_f_weekday(self):
Barry Warsaw35816e62002-08-29 16:24:50 +0000120 # Fetch self.f_weekday.
121 if not self.__f_weekday:
122 self.__calc_weekday()
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000123 return self.__f_weekday
124
125 def __get_a_weekday(self):
Barry Warsaw35816e62002-08-29 16:24:50 +0000126 # Fetch self.a_weekday.
127 if not self.__a_weekday:
128 self.__calc_weekday()
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000129 return self.__a_weekday
130
Tim Peters469cdad2002-08-08 20:19:19 +0000131 f_weekday = property(__get_f_weekday, __set_nothing,
Barry Warsaw35816e62002-08-29 16:24:50 +0000132 doc="Full weekday names")
Tim Peters469cdad2002-08-08 20:19:19 +0000133 a_weekday = property(__get_a_weekday, __set_nothing,
Barry Warsaw35816e62002-08-29 16:24:50 +0000134 doc="Abbreviated weekday names")
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000135
136 def __get_f_month(self):
Barry Warsaw35816e62002-08-29 16:24:50 +0000137 # Fetch self.f_month.
138 if not self.__f_month:
139 self.__calc_month()
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000140 return self.__f_month
141
142 def __get_a_month(self):
Barry Warsaw35816e62002-08-29 16:24:50 +0000143 # Fetch self.a_month.
144 if not self.__a_month:
145 self.__calc_month()
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000146 return self.__a_month
147
148 f_month = property(__get_f_month, __set_nothing,
Barry Warsaw35816e62002-08-29 16:24:50 +0000149 doc="Full month names (dummy value at index 0)")
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000150 a_month = property(__get_a_month, __set_nothing,
Barry Warsaw35816e62002-08-29 16:24:50 +0000151 doc="Abbreviated month names (dummy value at index 0)")
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000152
153 def __get_am_pm(self):
Barry Warsaw35816e62002-08-29 16:24:50 +0000154 # Fetch self.am_pm.
155 if not self.__am_pm:
156 self.__calc_am_pm()
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000157 return self.__am_pm
158
159 am_pm = property(__get_am_pm, __set_nothing, doc="AM/PM representation")
160
161 def __get_timezone(self):
Barry Warsaw35816e62002-08-29 16:24:50 +0000162 # Fetch self.timezone.
163 if not self.__timezone:
164 self.__calc_timezone()
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000165 return self.__timezone
166
167 timezone = property(__get_timezone, __set_nothing,
168 doc="Timezone representation (dummy value at index 2)")
169
170 def __get_LC_date_time(self):
Barry Warsaw35816e62002-08-29 16:24:50 +0000171 # Fetch self.LC_date_time.
172 if not self.__LC_date_time:
173 self.__calc_date_time()
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000174 return self.__LC_date_time
175
176 def __get_LC_date(self):
Barry Warsaw35816e62002-08-29 16:24:50 +0000177 # Fetch self.LC_date.
178 if not self.__LC_date:
179 self.__calc_date_time()
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000180 return self.__LC_date
181
182 def __get_LC_time(self):
Barry Warsaw35816e62002-08-29 16:24:50 +0000183 # Fetch self.LC_time.
184 if not self.__LC_time:
185 self.__calc_date_time()
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000186 return self.__LC_time
187
Barry Warsaw35816e62002-08-29 16:24:50 +0000188 LC_date_time = property(
189 __get_LC_date_time, __set_nothing,
190 doc=
191 "Format string for locale's date/time representation ('%c' format)")
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000192 LC_date = property(__get_LC_date, __set_nothing,
193 doc="Format string for locale's date representation ('%x' format)")
194 LC_time = property(__get_LC_time, __set_nothing,
195 doc="Format string for locale's time representation ('%X' format)")
196
197 def __get_lang(self):
Barry Warsaw35816e62002-08-29 16:24:50 +0000198 # Fetch self.lang.
199 if not self.__lang:
200 self.__calc_lang()
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000201 return self.__lang
202
Barry Warsaw35816e62002-08-29 16:24:50 +0000203 lang = property(__get_lang, __set_nothing,
204 doc="Language used for instance")
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000205
206 def __calc_weekday(self):
Barry Warsaw35816e62002-08-29 16:24:50 +0000207 # Set self.__a_weekday and self.__f_weekday using the calendar
208 # module.
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000209 a_weekday = [calendar.day_abbr[i] for i in range(7)]
210 f_weekday = [calendar.day_name[i] for i in range(7)]
Barry Warsaw35816e62002-08-29 16:24:50 +0000211 if not self.__a_weekday:
212 self.__a_weekday = a_weekday
213 if not self.__f_weekday:
214 self.__f_weekday = f_weekday
Tim Peters469cdad2002-08-08 20:19:19 +0000215
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000216 def __calc_month(self):
Barry Warsaw35816e62002-08-29 16:24:50 +0000217 # Set self.__f_month and self.__a_month using the calendar module.
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000218 a_month = [calendar.month_abbr[i] for i in range(13)]
219 f_month = [calendar.month_name[i] for i in range(13)]
Barry Warsaw35816e62002-08-29 16:24:50 +0000220 if not self.__a_month:
221 self.__a_month = a_month
222 if not self.__f_month:
223 self.__f_month = f_month
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000224
225 def __calc_am_pm(self):
Barry Warsaw35816e62002-08-29 16:24:50 +0000226 # Set self.__am_pm by using time.strftime().
Tim Peters469cdad2002-08-08 20:19:19 +0000227
Barry Warsaw35816e62002-08-29 16:24:50 +0000228 # The magic date (1999,3,17,hour,44,55,2,76,0) is not really that
229 # magical; just happened to have used it everywhere else where a
230 # static date was needed.
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000231 am_pm = []
232 for hour in (01,22):
233 time_tuple = time.struct_time((1999,3,17,hour,44,55,2,76,0))
234 am_pm.append(time.strftime("%p", time_tuple))
235 self.__am_pm = am_pm
236
237 def __calc_date_time(self):
Barry Warsaw35816e62002-08-29 16:24:50 +0000238 # Set self.__date_time, self.__date, & self.__time by using
239 # time.strftime().
Tim Peters469cdad2002-08-08 20:19:19 +0000240
Barry Warsaw35816e62002-08-29 16:24:50 +0000241 # Use (1999,3,17,22,44,55,2,76,0) for magic date because the amount of
242 # overloaded numbers is minimized. The order in which searches for
243 # values within the format string is very important; it eliminates
244 # possible ambiguity for what something represents.
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000245 time_tuple = time.struct_time((1999,3,17,22,44,55,2,76,0))
246 date_time = [None, None, None]
247 date_time[0] = time.strftime("%c", time_tuple)
248 date_time[1] = time.strftime("%x", time_tuple)
249 date_time[2] = time.strftime("%X", time_tuple)
250 for offset,directive in ((0,'%c'), (1,'%x'), (2,'%X')):
251 current_format = date_time[offset]
252 current_format = current_format.replace('%', '%%')
253 current_format = current_format.replace(self.f_weekday[2], '%A')
254 current_format = current_format.replace(self.f_month[3], '%B')
255 current_format = current_format.replace(self.a_weekday[2], '%a')
256 current_format = current_format.replace(self.a_month[3], '%b')
257 current_format = current_format.replace(self.am_pm[1], '%p')
258 current_format = current_format.replace(self.timezone[0], '%Z')
259 current_format = current_format.replace(self.timezone[1], '%Z')
260 current_format = current_format.replace('1999', '%Y')
261 current_format = current_format.replace('99', '%y')
262 current_format = current_format.replace('22', '%H')
263 current_format = current_format.replace('44', '%M')
264 current_format = current_format.replace('55', '%S')
265 current_format = current_format.replace('76', '%j')
266 current_format = current_format.replace('17', '%d')
267 current_format = current_format.replace('03', '%m')
268 current_format = current_format.replace('2', '%w')
269 current_format = current_format.replace('10', '%I')
270 time_tuple = time.struct_time((1999,1,3,1,1,1,6,3,0))
271 if time.strftime(directive, time_tuple).find('00'):
272 U_W = '%U'
273 else:
274 U_W = '%W'
275 date_time[offset] = current_format.replace('11', U_W)
Barry Warsaw35816e62002-08-29 16:24:50 +0000276 if not self.__LC_date_time:
277 self.__LC_date_time = date_time[0]
278 if not self.__LC_date:
279 self.__LC_date = date_time[1]
280 if not self.__LC_time:
281 self.__LC_time = date_time[2]
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000282
283 def __calc_timezone(self):
Barry Warsaw35816e62002-08-29 16:24:50 +0000284 # Set self.__timezone by using time.tzname.
285 #
286 # Empty string used for matching when timezone is not used/needed such
287 # as with UTC.
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000288 self.__timezone = self.__pad(time.tzname, 0)
289
290 def __calc_lang(self):
Barry Warsaw35816e62002-08-29 16:24:50 +0000291 # Set self.lang by using locale.getlocale() or
292 # locale.getdefaultlocale().
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000293 current_lang = locale.getlocale(locale.LC_TIME)[0]
Barry Warsaw35816e62002-08-29 16:24:50 +0000294 if current_lang:
295 self.__lang = current_lang
296 else:
297 self.__lang = locale.getdefaultlocale()[0]
298
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000299
300class TimeRE(dict):
301 """Handle conversion from format directives to regexes."""
302
303 def __init__(self, locale_time=LocaleTime()):
Barry Warsaw35816e62002-08-29 16:24:50 +0000304 """Init inst with non-locale regexes and store LocaleTime object."""
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000305 super(TimeRE,self).__init__({
Barry Warsaw35816e62002-08-29 16:24:50 +0000306 # The " \d" option is to make %c from ANSI C work
307 'd': r"(?P<d>3[0-1]|[0-2]\d|\d| \d)",
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000308 'H': r"(?P<H>2[0-3]|[0-1]\d|\d)",
309 'I': r"(?P<I>0\d|1[0-2]|\d)",
310 'j': r"(?P<j>(?:3[0-5]\d|6[0-6])|[0-2]\d\d|\d)",
311 'm': r"(?P<m>0\d|1[0-2]|\d)",
312 'M': r"(?P<M>[0-5]\d|\d)",
313 'S': r"(?P<S>6[0-1]|[0-5]\d|\d)",
314 'U': r"(?P<U>5[0-3]|[0-4]\d|\d)",
315 'w': r"(?P<w>[0-6])",
Barry Warsaw35816e62002-08-29 16:24:50 +0000316 'W': r"(?P<W>5[0-3]|[0-4]\d|\d)", # Same as U
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000317 'y': r"(?P<y>\d\d)",
318 'Y': r"(?P<Y>\d\d\d\d)"})
319 self.locale_time = locale_time
320
321 def __getitem__(self, fetch):
322 """Try to fetch regex; if it does not exist, construct it."""
323 try:
324 return super(TimeRE,self).__getitem__(fetch)
325 except KeyError:
326 if fetch == 'A':
Tim Peters469cdad2002-08-08 20:19:19 +0000327 self[fetch] = self.__seqToRE(self.locale_time.f_weekday,
Barry Warsaw35816e62002-08-29 16:24:50 +0000328 fetch)
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000329 elif fetch == 'a':
Tim Peters469cdad2002-08-08 20:19:19 +0000330 self[fetch] = self.__seqToRE(self.locale_time.a_weekday,
Barry Warsaw35816e62002-08-29 16:24:50 +0000331 fetch)
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000332 elif fetch == 'B':
Tim Peters469cdad2002-08-08 20:19:19 +0000333 self[fetch] = self.__seqToRE(self.locale_time.f_month[1:],
Barry Warsaw35816e62002-08-29 16:24:50 +0000334 fetch)
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000335 elif fetch == 'b':
Tim Peters469cdad2002-08-08 20:19:19 +0000336 self[fetch] = self.__seqToRE(self.locale_time.a_month[1:],
Barry Warsaw35816e62002-08-29 16:24:50 +0000337 fetch)
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000338 elif fetch == 'c':
339 self[fetch] = self.pattern(self.locale_time.LC_date_time)
340 elif fetch == 'p':
341 self[fetch] = self.__seqToRE(self.locale_time.am_pm, fetch)
342 elif fetch == 'x':
343 self[fetch] = self.pattern(self.locale_time.LC_date)
344 elif fetch == 'X':
345 self[fetch] = self.pattern(self.locale_time.LC_time)
346 elif fetch == 'Z':
Tim Peters469cdad2002-08-08 20:19:19 +0000347 self[fetch] = self.__seqToRE(self.locale_time.timezone,
Barry Warsaw35816e62002-08-29 16:24:50 +0000348 fetch)
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000349 elif fetch == '%':
350 return '%'
351 return super(TimeRE,self).__getitem__(fetch)
Tim Peters469cdad2002-08-08 20:19:19 +0000352
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000353 def __seqToRE(self, to_convert, directive):
354 """Convert a list to a regex string for matching directive."""
355 def sorter(a, b):
356 """Sort based on length.
Tim Peters469cdad2002-08-08 20:19:19 +0000357
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000358 Done in case for some strange reason that names in the locale only
359 differ by a suffix and thus want the name with the suffix to match
360 first.
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000361 """
Barry Warsaw35816e62002-08-29 16:24:50 +0000362 try:
363 a_length = len(a)
364 except TypeError:
365 a_length = 0
366 try:
367 b_length = len(b)
368 except TypeError:
369 b_length = 0
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000370 return cmp(b_length, a_length)
Tim Peters469cdad2002-08-08 20:19:19 +0000371
Barry Warsaw35816e62002-08-29 16:24:50 +0000372 to_convert = to_convert[:] # Don't want to change value in-place.
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000373 to_convert.sort(sorter)
374 regex = '(?P<%s>' % directive
375 for item in to_convert:
376 regex = "%s(?:%s)|" % (regex, item)
377 else:
378 regex = regex[:-1]
379 return '%s)' % regex
380
381 def pattern(self, format):
382 """Return re pattern for the format string."""
383 processed_format = ''
384 for whitespace in whitespace_string:
385 format = format.replace(whitespace, r'\s*')
386 while format.find('%') != -1:
387 directive_index = format.index('%')+1
Tim Peters469cdad2002-08-08 20:19:19 +0000388 processed_format = "%s%s%s" % (processed_format,
Barry Warsaw35816e62002-08-29 16:24:50 +0000389 format[:directive_index-1],
390 self[format[directive_index]])
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000391 format = format[directive_index+1:]
392 return "%s%s" % (processed_format, format)
393
394 def compile(self, format):
395 """Return a compiled re object for the format string."""
396 format = "(?#%s)%s" % (self.locale_time.lang,format)
397 return re_compile(self.pattern(format), IGNORECASE)
398
399
400def strptime(data_string, format="%a %b %d %H:%M:%S %Y"):
Barry Warsaw35816e62002-08-29 16:24:50 +0000401 """Return a time struct based on the input data and the format string.
Tim Peters469cdad2002-08-08 20:19:19 +0000402
Barry Warsaw35816e62002-08-29 16:24:50 +0000403 The format argument may either be a regular expression object compiled by
404 strptime(), or a format string. If False is passed in for data_string
405 then the re object calculated for format will be returned. The re object
406 must be used with the same locale as was used to compile the re object.
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000407 """
408 locale_time = LocaleTime()
Barry Warsaw35816e62002-08-29 16:24:50 +0000409 if isinstance(format, RegexpType):
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000410 if format.pattern.find(locale_time.lang) == -1:
Barry Warsaw35816e62002-08-29 16:24:50 +0000411 raise TypeError("re object not created with same language as "
412 "LocaleTime instance")
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000413 else:
414 compiled_re = format
415 else:
416 compiled_re = TimeRE(locale_time).compile(format)
417 if data_string is False:
418 return compiled_re
419 else:
420 found = compiled_re.match(data_string)
421 if not found:
422 raise ValueError("time data did not match format")
Barry Warsaw35816e62002-08-29 16:24:50 +0000423 year = month = day = hour = minute = second = weekday = julian = tz =-1
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000424 found_dict = found.groupdict()
425 for group_key in found_dict.iterkeys():
Barry Warsaw35816e62002-08-29 16:24:50 +0000426 if group_key == 'y':
427 year = int("%s%s" %
428 (time.strftime("%Y")[:-2], found_dict['y']))
429 elif group_key == 'Y':
430 year = int(found_dict['Y'])
431 elif group_key == 'm':
432 month = int(found_dict['m'])
433 elif group_key == 'B':
434 month = locale_time.f_month.index(found_dict['B'])
435 elif group_key == 'b':
436 month = locale_time.a_month.index(found_dict['b'])
437 elif group_key == 'd':
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000438 day = int(found_dict['d'])
Barry Warsaw35816e62002-08-29 16:24:50 +0000439 elif group_key is 'H':
440 hour = int(found_dict['H'])
441 elif group_key == 'I':
442 hour = int(found_dict['I'])
443 ampm = found_dict.get('p')
444 if ampm == locale_time.am_pm[0]:
445 # We're in AM so the hour is correct unless we're
446 # looking at 12 midnight.
447 # 12 midnight == 12 AM == hour 0
448 if hour == 12:
449 hour = 0
450 elif ampm == locale_time.am_pm[1]:
451 # We're in PM so we need to add 12 to the hour unless
452 # we're looking at 12 noon.
453 # 12 noon == 12 PM == hour 12
454 if hour != 12:
455 hour += 12
456 elif group_key == 'M':
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000457 minute = int(found_dict['M'])
Barry Warsaw35816e62002-08-29 16:24:50 +0000458 elif group_key == 'S':
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000459 second = int(found_dict['S'])
Barry Warsaw35816e62002-08-29 16:24:50 +0000460 elif group_key == 'A':
461 weekday = locale_time.f_weekday.index(found_dict['A'])
462 elif group_key == 'a':
463 weekday = locale_time.a_weekday.index(found_dict['a'])
464 elif group_key == 'w':
465 weekday = int(found_dict['w'])
466 if weekday == 0:
467 weekday = 6
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000468 else:
Barry Warsaw35816e62002-08-29 16:24:50 +0000469 weekday -= 1
470 elif group_key == 'j':
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000471 julian = int(found_dict['j'])
Barry Warsaw35816e62002-08-29 16:24:50 +0000472 elif group_key == 'Z':
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000473 if locale_time.timezone[0] == found_dict['Z']:
474 tz = 0
475 elif locale_time.timezone[1] == found_dict['Z']:
476 tz = 1
477 elif locale_time.timezone[2] == found_dict['Z']:
478 tz = 0
479 if julian == -1 and year != -1 and month != -1 and day != -1:
480 julian = julianday(year, month, day)
481 if (month == -1 or day == -1) and julian != -1 and year != -1:
Barry Warsaw35816e62002-08-29 16:24:50 +0000482 year, month, day = gregorian(julian, year)
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000483 if weekday == -1 and year != -1 and month != -1 and day != -1:
484 weekday = dayofweek(year, month, day)
Barry Warsaw35816e62002-08-29 16:24:50 +0000485 return time.struct_time(
486 (year,month,day,hour,minute,second,weekday, julian,tz))
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000487
488def firstjulian(year):
489 """Calculate the Julian date up until the first of the year."""
Barry Warsaw35816e62002-08-29 16:24:50 +0000490 return ((146097 * (year + 4799)) // 400) - 31738
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000491
492def julianday(year, month, day):
Barry Warsaw35816e62002-08-29 16:24:50 +0000493 """Calculate the Julian day since the beginning of the year.
494 Calculated from the Gregorian date.
495 """
496 a = (14 - month) // 12
497 return (day - 32045
498 + (((153 * (month + (12 * a) - 3)) + 2) // 5)
499 + ((146097 * (year + 4800 - a)) // 400)) - firstjulian(year) + 1
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000500
501def gregorian(julian, year):
Barry Warsaw35816e62002-08-29 16:24:50 +0000502 """Return 3-item list containing Gregorian date based on the Julian day."""
503 a = 32043 + julian + firstjulian(year)
504 b = ((4 * a) + 3) // 146097
505 c = a - ((146097 * b) // 4)
506 d = ((4 * c) + 3) // 1461
507 e = c - ((1461 * d) // 4)
508 m = ((5 * e) + 2) // 153
509 day = 1 + e - (((153 * m) + 2) // 5)
510 month = m + 3 - (12 * (m // 10))
511 year = (100 * b) + d - 4800 + (m // 10)
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000512 return [year, month, day]
513
514def dayofweek(year, month, day):
515 """Calculate the day of the week (Monday is 0)."""
Barry Warsaw35816e62002-08-29 16:24:50 +0000516 a = (14 - month) // 12
517 y = year - a
518 weekday = (day + y + ((97 * y) // 400)
519 + ((31 * (month + (12 * a) -2 )) // 12)) % 7
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000520 if weekday == 0:
521 return 6
522 else:
523 return weekday-1