blob: 896c05036627777a3e0411dc65f7d307e8ef9605 [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
Barry Warsaw4d895fa2002-09-23 22:46:49 +00005 TimeRE -- Creates regexes for pattern matching a 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
Guido van Rossum00efe7e2002-07-19 17:04:46 +000029__author__ = "Brett Cannon"
30__email__ = "drifty@bigfoot.com"
31
32__all__ = ['strptime']
33
Barry Warsaw35816e62002-08-29 16:24:50 +000034RegexpType = type(re_compile(''))
35
36
Guido van Rossum00efe7e2002-07-19 17:04:46 +000037class LocaleTime(object):
38 """Stores and handles locale-specific information related to time.
39
40 ATTRIBUTES (all read-only after instance creation! Instance variables that
41 store the values have mangled names):
42 f_weekday -- full weekday names (7-item list)
43 a_weekday -- abbreviated weekday names (7-item list)
Tim Peters469cdad2002-08-08 20:19:19 +000044 f_month -- full weekday names (14-item list; dummy value in [0], which
Guido van Rossum00efe7e2002-07-19 17:04:46 +000045 is added by code)
Tim Peters469cdad2002-08-08 20:19:19 +000046 a_month -- abbreviated weekday names (13-item list, dummy value in
Guido van Rossum00efe7e2002-07-19 17:04:46 +000047 [0], which is added by code)
48 am_pm -- AM/PM representation (2-item list)
49 LC_date_time -- format string for date/time representation (string)
50 LC_date -- format string for date representation (string)
51 LC_time -- format string for time representation (string)
Tim Peters469cdad2002-08-08 20:19:19 +000052 timezone -- daylight- and non-daylight-savings timezone representation
53 (3-item list; code tacks on blank item at end for
Guido van Rossum00efe7e2002-07-19 17:04:46 +000054 possible lack of timezone such as UTC)
55 lang -- Language used by instance (string)
Guido van Rossum00efe7e2002-07-19 17:04:46 +000056 """
57
Tim Peters469cdad2002-08-08 20:19:19 +000058 def __init__(self, f_weekday=None, a_weekday=None, f_month=None,
Barry Warsaw35816e62002-08-29 16:24:50 +000059 a_month=None, am_pm=None, LC_date_time=None, LC_time=None,
60 LC_date=None, timezone=None, lang=None):
Guido van Rossum00efe7e2002-07-19 17:04:46 +000061 """Optionally set attributes with passed-in values."""
Barry Warsaw35816e62002-08-29 16:24:50 +000062 if f_weekday is None:
63 self.__f_weekday = None
64 elif len(f_weekday) == 7:
65 self.__f_weekday = list(f_weekday)
Guido van Rossum00efe7e2002-07-19 17:04:46 +000066 else:
67 raise TypeError("full weekday names must be a 7-item sequence")
Barry Warsaw35816e62002-08-29 16:24:50 +000068 if a_weekday is None:
69 self.__a_weekday = None
70 elif len(a_weekday) == 7:
71 self.__a_weekday = list(a_weekday)
Guido van Rossum00efe7e2002-07-19 17:04:46 +000072 else:
73 raise TypeError(
Barry Warsaw35816e62002-08-29 16:24:50 +000074 "abbreviated weekday names must be a 7-item sequence")
75 if f_month is None:
76 self.__f_month = None
Guido van Rossum00efe7e2002-07-19 17:04:46 +000077 elif len(f_month) == 12:
78 self.__f_month = self.__pad(f_month, True)
79 else:
80 raise TypeError("full month names must be a 12-item sequence")
Barry Warsaw35816e62002-08-29 16:24:50 +000081 if a_month is None:
82 self.__a_month = None
Guido van Rossum00efe7e2002-07-19 17:04:46 +000083 elif len(a_month) == 12:
84 self.__a_month = self.__pad(a_month, True)
85 else:
86 raise TypeError(
Barry Warsaw35816e62002-08-29 16:24:50 +000087 "abbreviated month names must be a 12-item sequence")
Guido van Rossum00efe7e2002-07-19 17:04:46 +000088 if am_pm is None:
89 self.__am_pm = None
90 elif len(am_pm) == 2:
91 self.__am_pm = am_pm
92 else:
93 raise TypeError("AM/PM representation must be a 2-item sequence")
94 self.__LC_date_time = LC_date_time
95 self.__LC_time = LC_time
96 self.__LC_date = LC_date
97 self.__timezone = timezone
98 if timezone:
99 if len(timezone) != 2:
100 raise TypeError("timezone names must contain 2 items")
101 else:
102 self.__timezone = self.__pad(timezone, False)
103 self.__lang = lang
104
105 def __pad(self, seq, front):
Barry Warsaw35816e62002-08-29 16:24:50 +0000106 # Add '' to seq to either front (is True), else the back.
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000107 seq = list(seq)
Barry Warsaw35816e62002-08-29 16:24:50 +0000108 if front:
109 seq.insert(0, '')
110 else:
111 seq.append('')
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000112 return seq
113
114 def __set_nothing(self, stuff):
Barry Warsaw35816e62002-08-29 16:24:50 +0000115 # Raise TypeError when trying to set an attribute.
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000116 raise TypeError("attribute does not support assignment")
117
118 def __get_f_weekday(self):
Barry Warsaw35816e62002-08-29 16:24:50 +0000119 # Fetch self.f_weekday.
120 if not self.__f_weekday:
121 self.__calc_weekday()
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000122 return self.__f_weekday
123
124 def __get_a_weekday(self):
Barry Warsaw35816e62002-08-29 16:24:50 +0000125 # Fetch self.a_weekday.
126 if not self.__a_weekday:
127 self.__calc_weekday()
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000128 return self.__a_weekday
129
Tim Peters469cdad2002-08-08 20:19:19 +0000130 f_weekday = property(__get_f_weekday, __set_nothing,
Barry Warsaw35816e62002-08-29 16:24:50 +0000131 doc="Full weekday names")
Tim Peters469cdad2002-08-08 20:19:19 +0000132 a_weekday = property(__get_a_weekday, __set_nothing,
Barry Warsaw35816e62002-08-29 16:24:50 +0000133 doc="Abbreviated weekday names")
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000134
135 def __get_f_month(self):
Barry Warsaw35816e62002-08-29 16:24:50 +0000136 # Fetch self.f_month.
137 if not self.__f_month:
138 self.__calc_month()
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000139 return self.__f_month
140
141 def __get_a_month(self):
Barry Warsaw35816e62002-08-29 16:24:50 +0000142 # Fetch self.a_month.
143 if not self.__a_month:
144 self.__calc_month()
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000145 return self.__a_month
146
147 f_month = property(__get_f_month, __set_nothing,
Barry Warsaw35816e62002-08-29 16:24:50 +0000148 doc="Full month names (dummy value at index 0)")
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000149 a_month = property(__get_a_month, __set_nothing,
Barry Warsaw35816e62002-08-29 16:24:50 +0000150 doc="Abbreviated month names (dummy value at index 0)")
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000151
152 def __get_am_pm(self):
Barry Warsaw35816e62002-08-29 16:24:50 +0000153 # Fetch self.am_pm.
154 if not self.__am_pm:
155 self.__calc_am_pm()
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000156 return self.__am_pm
157
158 am_pm = property(__get_am_pm, __set_nothing, doc="AM/PM representation")
159
160 def __get_timezone(self):
Barry Warsaw35816e62002-08-29 16:24:50 +0000161 # Fetch self.timezone.
162 if not self.__timezone:
163 self.__calc_timezone()
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000164 return self.__timezone
165
166 timezone = property(__get_timezone, __set_nothing,
167 doc="Timezone representation (dummy value at index 2)")
168
169 def __get_LC_date_time(self):
Barry Warsaw35816e62002-08-29 16:24:50 +0000170 # Fetch self.LC_date_time.
171 if not self.__LC_date_time:
172 self.__calc_date_time()
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000173 return self.__LC_date_time
174
175 def __get_LC_date(self):
Barry Warsaw35816e62002-08-29 16:24:50 +0000176 # Fetch self.LC_date.
177 if not self.__LC_date:
178 self.__calc_date_time()
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000179 return self.__LC_date
180
181 def __get_LC_time(self):
Barry Warsaw35816e62002-08-29 16:24:50 +0000182 # Fetch self.LC_time.
183 if not self.__LC_time:
184 self.__calc_date_time()
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000185 return self.__LC_time
186
Barry Warsaw35816e62002-08-29 16:24:50 +0000187 LC_date_time = property(
188 __get_LC_date_time, __set_nothing,
189 doc=
190 "Format string for locale's date/time representation ('%c' format)")
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000191 LC_date = property(__get_LC_date, __set_nothing,
192 doc="Format string for locale's date representation ('%x' format)")
193 LC_time = property(__get_LC_time, __set_nothing,
194 doc="Format string for locale's time representation ('%X' format)")
195
196 def __get_lang(self):
Barry Warsaw35816e62002-08-29 16:24:50 +0000197 # Fetch self.lang.
198 if not self.__lang:
199 self.__calc_lang()
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000200 return self.__lang
201
Barry Warsaw35816e62002-08-29 16:24:50 +0000202 lang = property(__get_lang, __set_nothing,
203 doc="Language used for instance")
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000204
205 def __calc_weekday(self):
Barry Warsaw35816e62002-08-29 16:24:50 +0000206 # Set self.__a_weekday and self.__f_weekday using the calendar
207 # module.
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000208 a_weekday = [calendar.day_abbr[i] for i in range(7)]
209 f_weekday = [calendar.day_name[i] for i in range(7)]
Barry Warsaw35816e62002-08-29 16:24:50 +0000210 if not self.__a_weekday:
211 self.__a_weekday = a_weekday
212 if not self.__f_weekday:
213 self.__f_weekday = f_weekday
Tim Peters469cdad2002-08-08 20:19:19 +0000214
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000215 def __calc_month(self):
Barry Warsaw35816e62002-08-29 16:24:50 +0000216 # Set self.__f_month and self.__a_month using the calendar module.
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000217 a_month = [calendar.month_abbr[i] for i in range(13)]
218 f_month = [calendar.month_name[i] for i in range(13)]
Barry Warsaw35816e62002-08-29 16:24:50 +0000219 if not self.__a_month:
220 self.__a_month = a_month
221 if not self.__f_month:
222 self.__f_month = f_month
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000223
224 def __calc_am_pm(self):
Barry Warsaw35816e62002-08-29 16:24:50 +0000225 # Set self.__am_pm by using time.strftime().
Tim Peters469cdad2002-08-08 20:19:19 +0000226
Barry Warsaw35816e62002-08-29 16:24:50 +0000227 # The magic date (1999,3,17,hour,44,55,2,76,0) is not really that
228 # magical; just happened to have used it everywhere else where a
229 # static date was needed.
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000230 am_pm = []
231 for hour in (01,22):
232 time_tuple = time.struct_time((1999,3,17,hour,44,55,2,76,0))
233 am_pm.append(time.strftime("%p", time_tuple))
234 self.__am_pm = am_pm
235
236 def __calc_date_time(self):
Barry Warsaw35816e62002-08-29 16:24:50 +0000237 # Set self.__date_time, self.__date, & self.__time by using
238 # time.strftime().
Tim Peters469cdad2002-08-08 20:19:19 +0000239
Barry Warsaw35816e62002-08-29 16:24:50 +0000240 # Use (1999,3,17,22,44,55,2,76,0) for magic date because the amount of
241 # overloaded numbers is minimized. The order in which searches for
242 # values within the format string is very important; it eliminates
243 # possible ambiguity for what something represents.
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000244 time_tuple = time.struct_time((1999,3,17,22,44,55,2,76,0))
245 date_time = [None, None, None]
246 date_time[0] = time.strftime("%c", time_tuple)
247 date_time[1] = time.strftime("%x", time_tuple)
248 date_time[2] = time.strftime("%X", time_tuple)
249 for offset,directive in ((0,'%c'), (1,'%x'), (2,'%X')):
250 current_format = date_time[offset]
Barry Warsaw4d895fa2002-09-23 22:46:49 +0000251 for old, new in (
252 ('%', '%%'), (self.f_weekday[2], '%A'),
253 (self.f_month[3], '%B'), (self.a_weekday[2], '%a'),
254 (self.a_month[3], '%b'), (self.am_pm[1], '%p'),
255 (self.timezone[0], '%Z'), (self.timezone[1], '%Z'),
256 ('1999', '%Y'), ('99', '%y'), ('22', '%H'),
257 ('44', '%M'), ('55', '%S'), ('76', '%j'),
258 ('17', '%d'), ('03', '%m'), ('3', '%m'),
259 # '3' needed for when no leading zero.
260 ('2', '%w'), ('10', '%I')):
261 try:
262 # Done this way to deal with possible lack of locale info
263 # manifesting itself as the empty string (i.e., Swedish's
264 # lack of AM/PM info).
265 current_format = current_format.replace(old, new)
266 except ValueError:
267 pass
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000268 time_tuple = time.struct_time((1999,1,3,1,1,1,6,3,0))
269 if time.strftime(directive, time_tuple).find('00'):
270 U_W = '%U'
271 else:
272 U_W = '%W'
273 date_time[offset] = current_format.replace('11', U_W)
Barry Warsaw35816e62002-08-29 16:24:50 +0000274 if not self.__LC_date_time:
275 self.__LC_date_time = date_time[0]
276 if not self.__LC_date:
277 self.__LC_date = date_time[1]
278 if not self.__LC_time:
279 self.__LC_time = date_time[2]
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000280
281 def __calc_timezone(self):
Barry Warsaw35816e62002-08-29 16:24:50 +0000282 # Set self.__timezone by using time.tzname.
283 #
284 # Empty string used for matching when timezone is not used/needed such
285 # as with UTC.
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000286 self.__timezone = self.__pad(time.tzname, 0)
287
288 def __calc_lang(self):
Martin v. Löwise16e01f2002-11-27 08:30:25 +0000289 # Set self.__lang by using locale.getlocale() or
290 # locale.getdefaultlocale(). If both turn up empty, set the attribute
291 # to ''. This is to stop calls to this method and to make sure
292 # strptime() can produce an re object correctly.
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:
Martin v. Löwise16e01f2002-11-27 08:30:25 +0000297 current_lang = locale.getdefaultlocale()[0]
298 if current_lang:
299 self.__lang = current_lang
300 else:
301 self.__lang = ''
Barry Warsaw35816e62002-08-29 16:24:50 +0000302
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000303
304class TimeRE(dict):
305 """Handle conversion from format directives to regexes."""
306
307 def __init__(self, locale_time=LocaleTime()):
Barry Warsaw35816e62002-08-29 16:24:50 +0000308 """Init inst with non-locale regexes and store LocaleTime object."""
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000309 super(TimeRE,self).__init__({
Barry Warsaw35816e62002-08-29 16:24:50 +0000310 # The " \d" option is to make %c from ANSI C work
311 'd': r"(?P<d>3[0-1]|[0-2]\d|\d| \d)",
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000312 'H': r"(?P<H>2[0-3]|[0-1]\d|\d)",
313 'I': r"(?P<I>0\d|1[0-2]|\d)",
314 'j': r"(?P<j>(?:3[0-5]\d|6[0-6])|[0-2]\d\d|\d)",
315 'm': r"(?P<m>0\d|1[0-2]|\d)",
316 'M': r"(?P<M>[0-5]\d|\d)",
317 'S': r"(?P<S>6[0-1]|[0-5]\d|\d)",
318 'U': r"(?P<U>5[0-3]|[0-4]\d|\d)",
319 'w': r"(?P<w>[0-6])",
Barry Warsaw35816e62002-08-29 16:24:50 +0000320 'W': r"(?P<W>5[0-3]|[0-4]\d|\d)", # Same as U
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000321 'y': r"(?P<y>\d\d)",
322 'Y': r"(?P<Y>\d\d\d\d)"})
323 self.locale_time = locale_time
324
325 def __getitem__(self, fetch):
326 """Try to fetch regex; if it does not exist, construct it."""
327 try:
Barry Warsaw4d895fa2002-09-23 22:46:49 +0000328 return super(TimeRE, self).__getitem__(fetch)
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000329 except KeyError:
Barry Warsaw4d895fa2002-09-23 22:46:49 +0000330 constructors = {
331 'A': lambda: self.__seqToRE(self.locale_time.f_weekday, fetch),
332 'a': lambda: self.__seqToRE(self.locale_time.a_weekday, fetch),
333 'B': lambda: self.__seqToRE(self.locale_time.f_month[1:],
334 fetch),
335 'b': lambda: self.__seqToRE(self.locale_time.a_month[1:],
336 fetch),
337 'c': lambda: self.pattern(self.locale_time.LC_date_time),
338 'p': lambda: self.__seqToRE(self.locale_time.am_pm, fetch),
339 'x': lambda: self.pattern(self.locale_time.LC_date),
340 'X': lambda: self.pattern(self.locale_time.LC_time),
341 'Z': lambda: self.__seqToRE(self.locale_time.timezone, fetch),
342 '%': lambda: '%',
343 }
344 if fetch in constructors:
345 self[fetch] = constructors[fetch]()
346 return self[fetch]
347 else:
348 raise
Tim Peters469cdad2002-08-08 20:19:19 +0000349
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000350 def __seqToRE(self, to_convert, directive):
351 """Convert a list to a regex string for matching directive."""
352 def sorter(a, b):
353 """Sort based on length.
Tim Peters469cdad2002-08-08 20:19:19 +0000354
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000355 Done in case for some strange reason that names in the locale only
356 differ by a suffix and thus want the name with the suffix to match
357 first.
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000358 """
Barry Warsaw35816e62002-08-29 16:24:50 +0000359 try:
360 a_length = len(a)
361 except TypeError:
362 a_length = 0
363 try:
364 b_length = len(b)
365 except TypeError:
366 b_length = 0
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000367 return cmp(b_length, a_length)
Tim Peters469cdad2002-08-08 20:19:19 +0000368
Barry Warsaw35816e62002-08-29 16:24:50 +0000369 to_convert = to_convert[:] # Don't want to change value in-place.
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000370 to_convert.sort(sorter)
Barry Warsaw4d895fa2002-09-23 22:46:49 +0000371 regex = '|'.join(to_convert)
372 regex = '(?P<%s>%s' % (directive, regex)
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000373 return '%s)' % regex
374
375 def pattern(self, format):
376 """Return re pattern for the format string."""
377 processed_format = ''
378 for whitespace in whitespace_string:
379 format = format.replace(whitespace, r'\s*')
380 while format.find('%') != -1:
381 directive_index = format.index('%')+1
Tim Peters469cdad2002-08-08 20:19:19 +0000382 processed_format = "%s%s%s" % (processed_format,
Barry Warsaw35816e62002-08-29 16:24:50 +0000383 format[:directive_index-1],
384 self[format[directive_index]])
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000385 format = format[directive_index+1:]
386 return "%s%s" % (processed_format, format)
387
388 def compile(self, format):
389 """Return a compiled re object for the format string."""
390 format = "(?#%s)%s" % (self.locale_time.lang,format)
391 return re_compile(self.pattern(format), IGNORECASE)
392
393
394def strptime(data_string, format="%a %b %d %H:%M:%S %Y"):
Barry Warsaw35816e62002-08-29 16:24:50 +0000395 """Return a time struct based on the input data and the format string.
Tim Peters469cdad2002-08-08 20:19:19 +0000396
Barry Warsaw35816e62002-08-29 16:24:50 +0000397 The format argument may either be a regular expression object compiled by
398 strptime(), or a format string. If False is passed in for data_string
399 then the re object calculated for format will be returned. The re object
400 must be used with the same locale as was used to compile the re object.
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000401 """
402 locale_time = LocaleTime()
Barry Warsaw35816e62002-08-29 16:24:50 +0000403 if isinstance(format, RegexpType):
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000404 if format.pattern.find(locale_time.lang) == -1:
Barry Warsaw35816e62002-08-29 16:24:50 +0000405 raise TypeError("re object not created with same language as "
406 "LocaleTime instance")
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000407 else:
408 compiled_re = format
409 else:
410 compiled_re = TimeRE(locale_time).compile(format)
411 if data_string is False:
412 return compiled_re
413 else:
414 found = compiled_re.match(data_string)
415 if not found:
416 raise ValueError("time data did not match format")
Barry Warsaw35816e62002-08-29 16:24:50 +0000417 year = month = day = hour = minute = second = weekday = julian = tz =-1
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000418 found_dict = found.groupdict()
419 for group_key in found_dict.iterkeys():
Barry Warsaw35816e62002-08-29 16:24:50 +0000420 if group_key == 'y':
421 year = int("%s%s" %
422 (time.strftime("%Y")[:-2], found_dict['y']))
423 elif group_key == 'Y':
424 year = int(found_dict['Y'])
425 elif group_key == 'm':
426 month = int(found_dict['m'])
427 elif group_key == 'B':
Barry Warsaw4d895fa2002-09-23 22:46:49 +0000428 month = _insensitiveindex(locale_time.f_month, found_dict['B'])
Barry Warsaw35816e62002-08-29 16:24:50 +0000429 elif group_key == 'b':
Barry Warsaw4d895fa2002-09-23 22:46:49 +0000430 month = _insensitiveindex(locale_time.a_month, found_dict['b'])
Barry Warsaw35816e62002-08-29 16:24:50 +0000431 elif group_key == 'd':
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000432 day = int(found_dict['d'])
Barry Warsaw35816e62002-08-29 16:24:50 +0000433 elif group_key is 'H':
434 hour = int(found_dict['H'])
435 elif group_key == 'I':
436 hour = int(found_dict['I'])
Barry Warsaw4d895fa2002-09-23 22:46:49 +0000437 ampm = found_dict.get('p', '').lower()
438 # If there was no AM/PM indicator, we'll treat this like AM
439 if ampm in ('', locale_time.am_pm[0].lower()):
Barry Warsaw35816e62002-08-29 16:24:50 +0000440 # We're in AM so the hour is correct unless we're
441 # looking at 12 midnight.
442 # 12 midnight == 12 AM == hour 0
443 if hour == 12:
444 hour = 0
Barry Warsaw4d895fa2002-09-23 22:46:49 +0000445 elif ampm == locale_time.am_pm[1].lower():
Barry Warsaw35816e62002-08-29 16:24:50 +0000446 # We're in PM so we need to add 12 to the hour unless
447 # we're looking at 12 noon.
448 # 12 noon == 12 PM == hour 12
449 if hour != 12:
450 hour += 12
451 elif group_key == 'M':
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000452 minute = int(found_dict['M'])
Barry Warsaw35816e62002-08-29 16:24:50 +0000453 elif group_key == 'S':
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000454 second = int(found_dict['S'])
Barry Warsaw35816e62002-08-29 16:24:50 +0000455 elif group_key == 'A':
Barry Warsaw4d895fa2002-09-23 22:46:49 +0000456 weekday = _insensitiveindex(locale_time.f_weekday,
457 found_dict['A'])
Barry Warsaw35816e62002-08-29 16:24:50 +0000458 elif group_key == 'a':
Barry Warsaw4d895fa2002-09-23 22:46:49 +0000459 weekday = _insensitiveindex(locale_time.a_weekday,
460 found_dict['a'])
Barry Warsaw35816e62002-08-29 16:24:50 +0000461 elif group_key == 'w':
462 weekday = int(found_dict['w'])
463 if weekday == 0:
464 weekday = 6
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000465 else:
Barry Warsaw35816e62002-08-29 16:24:50 +0000466 weekday -= 1
467 elif group_key == 'j':
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000468 julian = int(found_dict['j'])
Barry Warsaw35816e62002-08-29 16:24:50 +0000469 elif group_key == 'Z':
Barry Warsaw4d895fa2002-09-23 22:46:49 +0000470 found_zone = found_dict['Z'].lower()
Martin v. Löwise16e01f2002-11-27 08:30:25 +0000471 if locale_time.timezone[0] == locale_time.timezone[1]:
472 pass #Deals with bad locale setup where timezone info is
473 # the same; first found on FreeBSD 4.4 -current
474 elif locale_time.timezone[0].lower() == found_zone:
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000475 tz = 0
Barry Warsaw4d895fa2002-09-23 22:46:49 +0000476 elif locale_time.timezone[1].lower() == found_zone:
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000477 tz = 1
Barry Warsaw4d895fa2002-09-23 22:46:49 +0000478 elif locale_time.timezone[2].lower() == found_zone:
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000479 tz = 0
Barry Warsaw4d895fa2002-09-23 22:46:49 +0000480 #XXX <bc>: If calculating fxns are never exposed to the general
481 # populous then just inline calculations.
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000482 if julian == -1 and year != -1 and month != -1 and day != -1:
483 julian = julianday(year, month, day)
484 if (month == -1 or day == -1) and julian != -1 and year != -1:
Barry Warsaw35816e62002-08-29 16:24:50 +0000485 year, month, day = gregorian(julian, year)
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000486 if weekday == -1 and year != -1 and month != -1 and day != -1:
487 weekday = dayofweek(year, month, day)
Barry Warsaw35816e62002-08-29 16:24:50 +0000488 return time.struct_time(
489 (year,month,day,hour,minute,second,weekday, julian,tz))
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000490
Barry Warsaw4d895fa2002-09-23 22:46:49 +0000491def _insensitiveindex(lst, findme):
492 # Perform a case-insensitive index search.
493
494 #XXX <bc>: If LocaleTime is not exposed, then consider removing this and
495 # just lowercase when LocaleTime sets its vars and lowercasing
496 # search values.
497 findme = findme.lower()
498 for key,item in enumerate(lst):
499 if item.lower() == findme:
500 return key
501 else:
502 raise ValueError("value not in list")
503
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000504def firstjulian(year):
505 """Calculate the Julian date up until the first of the year."""
Barry Warsaw35816e62002-08-29 16:24:50 +0000506 return ((146097 * (year + 4799)) // 400) - 31738
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000507
508def julianday(year, month, day):
Barry Warsaw35816e62002-08-29 16:24:50 +0000509 """Calculate the Julian day since the beginning of the year.
510 Calculated from the Gregorian date.
511 """
512 a = (14 - month) // 12
513 return (day - 32045
514 + (((153 * (month + (12 * a) - 3)) + 2) // 5)
515 + ((146097 * (year + 4800 - a)) // 400)) - firstjulian(year) + 1
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000516
517def gregorian(julian, year):
Barry Warsaw35816e62002-08-29 16:24:50 +0000518 """Return 3-item list containing Gregorian date based on the Julian day."""
519 a = 32043 + julian + firstjulian(year)
520 b = ((4 * a) + 3) // 146097
521 c = a - ((146097 * b) // 4)
522 d = ((4 * c) + 3) // 1461
523 e = c - ((1461 * d) // 4)
524 m = ((5 * e) + 2) // 153
525 day = 1 + e - (((153 * m) + 2) // 5)
526 month = m + 3 - (12 * (m // 10))
527 year = (100 * b) + d - 4800 + (m // 10)
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000528 return [year, month, day]
529
530def dayofweek(year, month, day):
531 """Calculate the day of the week (Monday is 0)."""
Barry Warsaw35816e62002-08-29 16:24:50 +0000532 a = (14 - month) // 12
533 y = year - a
534 weekday = (day + y + ((97 * y) // 400)
535 + ((31 * (month + (12 * a) -2 )) // 12)) % 7
Guido van Rossum00efe7e2002-07-19 17:04:46 +0000536 if weekday == 0:
537 return 6
538 else:
539 return weekday-1