Skip to content
Newer
Older
100644 887 lines (796 sloc) 31.7 KB
8260b86 @niemeyer Snapshot of python-dateutil-1.0
niemeyer authored
1 # -*- coding:iso-8859-1 -*-
2 """
7c5ea8a @niemeyer Snapshot of python-dateutil-1.3
niemeyer authored
3 Copyright (c) 2003-2007 Gustavo Niemeyer <gustavo@niemeyer.net>
8260b86 @niemeyer Snapshot of python-dateutil-1.0
niemeyer authored
4
5 This module offers extensions to the standard python 2.3+
6 datetime module.
7 """
8 __author__ = "Gustavo Niemeyer <gustavo@niemeyer.net>"
9 __license__ = "PSF License"
10
7c5ea8a @niemeyer Snapshot of python-dateutil-1.3
niemeyer authored
11 import datetime
8260b86 @niemeyer Snapshot of python-dateutil-1.0
niemeyer authored
12 import string
13 import time
7c5ea8a @niemeyer Snapshot of python-dateutil-1.3
niemeyer authored
14 import sys
15 import os
16
17 try:
18 from cStringIO import StringIO
19 except ImportError:
20 from StringIO import StringIO
8260b86 @niemeyer Snapshot of python-dateutil-1.0
niemeyer authored
21
22 import relativedelta
23 import tz
24
7c5ea8a @niemeyer Snapshot of python-dateutil-1.3
niemeyer authored
25
8260b86 @niemeyer Snapshot of python-dateutil-1.0
niemeyer authored
26 __all__ = ["parse", "parserinfo"]
27
7c5ea8a @niemeyer Snapshot of python-dateutil-1.3
niemeyer authored
28
8260b86 @niemeyer Snapshot of python-dateutil-1.0
niemeyer authored
29 # Some pointers:
30 #
31 # http://www.cl.cam.ac.uk/~mgk25/iso-time.html
32 # http://www.iso.ch/iso/en/prods-services/popstds/datesandtime.html
33 # http://www.w3.org/TR/NOTE-datetime
34 # http://ringmaster.arc.nasa.gov/tools/time_formats.html
35 # http://search.cpan.org/author/MUIR/Time-modules-2003.0211/lib/Time/ParseDate.pm
36 # http://stein.cshl.org/jade/distrib/docs/java.text.SimpleDateFormat.html
37
38
7c5ea8a @niemeyer Snapshot of python-dateutil-1.3
niemeyer authored
39 class _timelex(object):
40
8260b86 @niemeyer Snapshot of python-dateutil-1.0
niemeyer authored
41 def __init__(self, instream):
42 if isinstance(instream, basestring):
43 instream = StringIO(instream)
44 self.instream = instream
45 self.wordchars = ('abcdfeghijklmnopqrstuvwxyz'
46 'ABCDEFGHIJKLMNOPQRSTUVWXYZ_'
47 'ßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ'
48 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ')
49 self.numchars = '0123456789'
50 self.whitespace = ' \t\r\n'
51 self.charstack = []
52 self.tokenstack = []
53 self.eof = False
54
55 def get_token(self):
56 if self.tokenstack:
57 return self.tokenstack.pop(0)
58 seenletters = False
59 token = None
60 state = None
61 wordchars = self.wordchars
62 numchars = self.numchars
63 whitespace = self.whitespace
64 while not self.eof:
65 if self.charstack:
66 nextchar = self.charstack.pop(0)
67 else:
68 nextchar = self.instream.read(1)
69 while nextchar == '\x00':
70 nextchar = self.instream.read(1)
71 if not nextchar:
72 self.eof = True
73 break
74 elif not state:
75 token = nextchar
76 if nextchar in wordchars:
77 state = 'a'
78 elif nextchar in numchars:
79 state = '0'
80 elif nextchar in whitespace:
81 token = ' '
82 break # emit token
83 else:
84 break # emit token
85 elif state == 'a':
86 seenletters = True
87 if nextchar in wordchars:
88 token += nextchar
89 elif nextchar == '.':
90 token += nextchar
91 state = 'a.'
92 else:
93 self.charstack.append(nextchar)
94 break # emit token
95 elif state == '0':
96 if nextchar in numchars:
97 token += nextchar
98 elif nextchar == '.':
99 token += nextchar
100 state = '0.'
101 else:
102 self.charstack.append(nextchar)
103 break # emit token
104 elif state == 'a.':
105 seenletters = True
106 if nextchar == '.' or nextchar in wordchars:
107 token += nextchar
108 elif nextchar in numchars and token[-1] == '.':
109 token += nextchar
110 state = '0.'
111 else:
112 self.charstack.append(nextchar)
113 break # emit token
114 elif state == '0.':
115 if nextchar == '.' or nextchar in numchars:
116 token += nextchar
117 elif nextchar in wordchars and token[-1] == '.':
118 token += nextchar
119 state = 'a.'
120 else:
121 self.charstack.append(nextchar)
122 break # emit token
123 if (state in ('a.', '0.') and
124 (seenletters or token.count('.') > 1 or token[-1] == '.')):
125 l = token.split('.')
126 token = l[0]
127 for tok in l[1:]:
128 self.tokenstack.append('.')
129 if tok:
130 self.tokenstack.append(tok)
131 return token
132
133 def __iter__(self):
134 return self
135
136 def next(self):
137 token = self.get_token()
138 if token is None:
139 raise StopIteration
140 return token
141
142 def split(cls, s):
143 return list(cls(s))
144 split = classmethod(split)
145
7c5ea8a @niemeyer Snapshot of python-dateutil-1.3
niemeyer authored
146
8260b86 @niemeyer Snapshot of python-dateutil-1.0
niemeyer authored
147 class _resultbase(object):
148
149 def __init__(self):
150 for attr in self.__slots__:
151 setattr(self, attr, None)
152
153 def _repr(self, classname):
154 l = []
155 for attr in self.__slots__:
156 value = getattr(self, attr)
157 if value is not None:
158 l.append("%s=%s" % (attr, `value`))
159 return "%s(%s)" % (classname, ", ".join(l))
160
161 def __repr__(self):
162 return self._repr(self.__class__.__name__)
163
7c5ea8a @niemeyer Snapshot of python-dateutil-1.3
niemeyer authored
164
165 class parserinfo(object):
8260b86 @niemeyer Snapshot of python-dateutil-1.0
niemeyer authored
166
167 # m from a.m/p.m, t from ISO T separator
168 JUMP = [" ", ".", ",", ";", "-", "/", "'",
169 "at", "on", "and", "ad", "m", "t", "of",
170 "st", "nd", "rd", "th"]
171
172 WEEKDAYS = [("Mon", "Monday"),
173 ("Tue", "Tuesday"),
174 ("Wed", "Wednesday"),
175 ("Thu", "Thursday"),
176 ("Fri", "Friday"),
177 ("Sat", "Saturday"),
178 ("Sun", "Sunday")]
179 MONTHS = [("Jan", "January"),
180 ("Feb", "February"),
181 ("Mar", "March"),
182 ("Apr", "April"),
183 ("May", "May"),
184 ("Jun", "June"),
185 ("Jul", "July"),
186 ("Aug", "August"),
187 ("Sep", "September"),
188 ("Oct", "October"),
189 ("Nov", "November"),
190 ("Dec", "December")]
191 HMS = [("h", "hour", "hours"),
192 ("m", "minute", "minutes"),
193 ("s", "second", "seconds")]
194 AMPM = [("am", "a"),
195 ("pm", "p")]
196 UTCZONE = ["UTC", "GMT", "Z"]
197 PERTAIN = ["of"]
198 TZOFFSET = {}
199
200 def __init__(self, dayfirst=False, yearfirst=False):
201 self._jump = self._convert(self.JUMP)
202 self._weekdays = self._convert(self.WEEKDAYS)
203 self._months = self._convert(self.MONTHS)
204 self._hms = self._convert(self.HMS)
205 self._ampm = self._convert(self.AMPM)
206 self._utczone = self._convert(self.UTCZONE)
207 self._pertain = self._convert(self.PERTAIN)
208
209 self.dayfirst = dayfirst
210 self.yearfirst = yearfirst
211
212 self._year = time.localtime().tm_year
7c5ea8a @niemeyer Snapshot of python-dateutil-1.3
niemeyer authored
213 self._century = self._year//100*100
8260b86 @niemeyer Snapshot of python-dateutil-1.0
niemeyer authored
214
215 def _convert(self, lst):
216 dct = {}
217 for i in range(len(lst)):
218 v = lst[i]
219 if isinstance(v, tuple):
220 for v in v:
221 dct[v.lower()] = i
222 else:
223 dct[v.lower()] = i
224 return dct
225
226 def jump(self, name):
227 return name.lower() in self._jump
228
229 def weekday(self, name):
230 if len(name) >= 3:
231 try:
232 return self._weekdays[name.lower()]
233 except KeyError:
234 pass
235 return None
236
237 def month(self, name):
238 if len(name) >= 3:
239 try:
240 return self._months[name.lower()]+1
241 except KeyError:
242 pass
243 return None
244
245 def hms(self, name):
246 try:
247 return self._hms[name.lower()]
248 except KeyError:
249 return None
250
251 def ampm(self, name):
252 try:
253 return self._ampm[name.lower()]
254 except KeyError:
255 return None
256
257 def pertain(self, name):
258 return name.lower() in self._pertain
259
260 def utczone(self, name):
261 return name.lower() in self._utczone
262
263 def tzoffset(self, name):
264 if name in self._utczone:
265 return 0
266 return self.TZOFFSET.get(name)
267
268 def convertyear(self, year):
269 if year < 100:
270 year += self._century
271 if abs(year-self._year) >= 50:
272 if year < self._year:
273 year += 100
274 else:
275 year -= 100
276 return year
277
278 def validate(self, res):
279 # move to info
280 if res.year is not None:
281 res.year = self.convertyear(res.year)
282 if res.tzoffset == 0 and not res.tzname or res.tzname == 'Z':
283 res.tzname = "UTC"
284 res.tzoffset = 0
285 elif res.tzoffset != 0 and res.tzname and self.utczone(res.tzname):
286 res.tzoffset = 0
287 return True
288
289
7c5ea8a @niemeyer Snapshot of python-dateutil-1.3
niemeyer authored
290 class parser(object):
8260b86 @niemeyer Snapshot of python-dateutil-1.0
niemeyer authored
291
7c5ea8a @niemeyer Snapshot of python-dateutil-1.3
niemeyer authored
292 def __init__(self, info=None):
293 self.info = info or parserinfo()
8260b86 @niemeyer Snapshot of python-dateutil-1.0
niemeyer authored
294
295 def parse(self, timestr, default=None,
296 ignoretz=False, tzinfos=None,
297 **kwargs):
298 if not default:
299 default = datetime.datetime.now().replace(hour=0, minute=0,
300 second=0, microsecond=0)
301 res = self._parse(timestr, **kwargs)
302 if res is None:
303 raise ValueError, "unknown string format"
304 repl = {}
305 for attr in ["year", "month", "day", "hour",
306 "minute", "second", "microsecond"]:
307 value = getattr(res, attr)
308 if value is not None:
309 repl[attr] = value
310 ret = default.replace(**repl)
311 if res.weekday is not None and not res.day:
312 ret = ret+relativedelta.relativedelta(weekday=res.weekday)
313 if not ignoretz:
314 if callable(tzinfos) or tzinfos and res.tzname in tzinfos:
315 if callable(tzinfos):
316 tzdata = tzinfos(res.tzname, res.tzoffset)
317 else:
318 tzdata = tzinfos.get(res.tzname)
319 if isinstance(tzdata, datetime.tzinfo):
320 tzinfo = tzdata
321 elif isinstance(tzdata, basestring):
322 tzinfo = tz.tzstr(tzdata)
323 elif isinstance(tzdata, int):
324 tzinfo = tz.tzoffset(res.tzname, tzdata)
325 else:
326 raise ValueError, "offset must be tzinfo subclass, " \
327 "tz string, or int offset"
328 ret = ret.replace(tzinfo=tzinfo)
329 elif res.tzname and res.tzname in time.tzname:
330 ret = ret.replace(tzinfo=tz.tzlocal())
331 elif res.tzoffset == 0:
332 ret = ret.replace(tzinfo=tz.tzutc())
333 elif res.tzoffset:
334 ret = ret.replace(tzinfo=tz.tzoffset(res.tzname, res.tzoffset))
335 return ret
336
337 class _result(_resultbase):
338 __slots__ = ["year", "month", "day", "weekday",
339 "hour", "minute", "second", "microsecond",
340 "tzname", "tzoffset"]
341
342 def _parse(self, timestr, dayfirst=None, yearfirst=None, fuzzy=False):
343 info = self.info
344 if dayfirst is None:
345 dayfirst = info.dayfirst
346 if yearfirst is None:
347 yearfirst = info.yearfirst
348 res = self._result()
349 l = _timelex.split(timestr)
350 try:
351
352 # year/month/day list
353 ymd = []
354
355 # Index of the month string in ymd
356 mstridx = -1
357
358 len_l = len(l)
359 i = 0
360 while i < len_l:
361
362 # Check if it's a number
363 try:
a48bcfe @niemeyer Snapshot of python-dateutil-1.4
niemeyer authored
364 value_repr = l[i]
365 value = float(value_repr)
8260b86 @niemeyer Snapshot of python-dateutil-1.0
niemeyer authored
366 except ValueError:
367 value = None
a48bcfe @niemeyer Snapshot of python-dateutil-1.4
niemeyer authored
368
8260b86 @niemeyer Snapshot of python-dateutil-1.0
niemeyer authored
369 if value is not None:
370 # Token is a number
371 len_li = len(l[i])
372 i += 1
373 if (len(ymd) == 3 and len_li in (2, 4)
374 and (i >= len_l or (l[i] != ':' and
375 info.hms(l[i]) is None))):
376 # 19990101T23[59]
377 s = l[i-1]
378 res.hour = int(s[:2])
379 if len_li == 4:
380 res.minute = int(s[2:])
381 elif len_li == 6 or (len_li > 6 and l[i-1].find('.') == 6):
382 # YYMMDD or HHMMSS[.ss]
383 s = l[i-1]
384 if not ymd and l[i-1].find('.') == -1:
385 ymd.append(info.convertyear(int(s[:2])))
386 ymd.append(int(s[2:4]))
387 ymd.append(int(s[4:]))
388 else:
389 # 19990101T235959[.59]
390 res.hour = int(s[:2])
391 res.minute = int(s[2:4])
a48bcfe @niemeyer Snapshot of python-dateutil-1.4
niemeyer authored
392 res.second, res.microsecond = _parsems(s[4:])
8260b86 @niemeyer Snapshot of python-dateutil-1.0
niemeyer authored
393 elif len_li == 8:
394 # YYYYMMDD
395 s = l[i-1]
396 ymd.append(int(s[:4]))
397 ymd.append(int(s[4:6]))
398 ymd.append(int(s[6:]))
399 elif len_li in (12, 14):
400 # YYYYMMDDhhmm[ss]
401 s = l[i-1]
402 ymd.append(int(s[:4]))
403 ymd.append(int(s[4:6]))
404 ymd.append(int(s[6:8]))
405 res.hour = int(s[8:10])
406 res.minute = int(s[10:12])
407 if len_li == 14:
408 res.second = int(s[12:])
409 elif ((i < len_l and info.hms(l[i]) is not None) or
410 (i+1 < len_l and l[i] == ' ' and
411 info.hms(l[i+1]) is not None)):
412 # HH[ ]h or MM[ ]m or SS[.ss][ ]s
413 if l[i] == ' ':
414 i += 1
415 idx = info.hms(l[i])
416 while True:
417 if idx == 0:
418 res.hour = int(value)
419 if value%1:
420 res.minute = int(60*(value%1))
421 elif idx == 1:
422 res.minute = int(value)
423 if value%1:
424 res.second = int(60*(value%1))
425 elif idx == 2:
a48bcfe @niemeyer Snapshot of python-dateutil-1.4
niemeyer authored
426 res.second, res.microsecond = \
427 _parsems(value_repr)
8260b86 @niemeyer Snapshot of python-dateutil-1.0
niemeyer authored
428 i += 1
429 if i >= len_l or idx == 2:
430 break
431 # 12h00
432 try:
a48bcfe @niemeyer Snapshot of python-dateutil-1.4
niemeyer authored
433 value_repr = l[i]
434 value = float(value_repr)
8260b86 @niemeyer Snapshot of python-dateutil-1.0
niemeyer authored
435 except ValueError:
436 break
437 else:
438 i += 1
439 idx += 1
440 if i < len_l:
441 newidx = info.hms(l[i])
442 if newidx is not None:
443 idx = newidx
444 elif i+1 < len_l and l[i] == ':':
445 # HH:MM[:SS[.ss]]
446 res.hour = int(value)
447 i += 1
448 value = float(l[i])
449 res.minute = int(value)
450 if value%1:
451 res.second = int(60*(value%1))
452 i += 1
453 if i < len_l and l[i] == ':':
a48bcfe @niemeyer Snapshot of python-dateutil-1.4
niemeyer authored
454 res.second, res.microsecond = _parsems(l[i+1])
8260b86 @niemeyer Snapshot of python-dateutil-1.0
niemeyer authored
455 i += 2
456 elif i < len_l and l[i] in ('-', '/', '.'):
457 sep = l[i]
458 ymd.append(int(value))
459 i += 1
460 if i < len_l and not info.jump(l[i]):
461 try:
462 # 01-01[-01]
463 ymd.append(int(l[i]))
464 except ValueError:
465 # 01-Jan[-01]
466 value = info.month(l[i])
467 if value is not None:
468 ymd.append(value)
469 assert mstridx == -1
470 mstridx = len(ymd)-1
471 else:
472 return None
473 i += 1
474 if i < len_l and l[i] == sep:
475 # We have three members
476 i += 1
477 value = info.month(l[i])
478 if value is not None:
479 ymd.append(value)
480 mstridx = len(ymd)-1
481 assert mstridx == -1
482 else:
483 ymd.append(int(l[i]))
484 i += 1
485 elif i >= len_l or info.jump(l[i]):
486 if i+1 < len_l and info.ampm(l[i+1]) is not None:
487 # 12 am
488 res.hour = int(value)
489 if res.hour < 12 and info.ampm(l[i+1]) == 1:
490 res.hour += 12
491 elif res.hour == 12 and info.ampm(l[i+1]) == 0:
492 res.hour = 0
493 i += 1
494 else:
495 # Year, month or day
496 ymd.append(int(value))
497 i += 1
498 elif info.ampm(l[i]) is not None:
499 # 12am
500 res.hour = int(value)
501 if res.hour < 12 and info.ampm(l[i]) == 1:
502 res.hour += 12
503 elif res.hour == 12 and info.ampm(l[i]) == 0:
504 res.hour = 0
505 i += 1
506 elif not fuzzy:
507 return None
508 else:
509 i += 1
510 continue
511
512 # Check weekday
513 value = info.weekday(l[i])
514 if value is not None:
515 res.weekday = value
516 i += 1
517 continue
518
519 # Check month name
520 value = info.month(l[i])
521 if value is not None:
522 ymd.append(value)
523 assert mstridx == -1
524 mstridx = len(ymd)-1
525 i += 1
526 if i < len_l:
527 if l[i] in ('-', '/'):
528 # Jan-01[-99]
529 sep = l[i]
530 i += 1
531 ymd.append(int(l[i]))
532 i += 1
533 if i < len_l and l[i] == sep:
534 # Jan-01-99
535 i += 1
536 ymd.append(int(l[i]))
537 i += 1
538 elif (i+3 < len_l and l[i] == l[i+2] == ' '
539 and info.pertain(l[i+1])):
540 # Jan of 01
541 # In this case, 01 is clearly year
542 try:
543 value = int(l[i+3])
544 except ValueError:
545 # Wrong guess
546 pass
547 else:
548 # Convert it here to become unambiguous
549 ymd.append(info.convertyear(value))
550 i += 4
551 continue
552
553 # Check am/pm
554 value = info.ampm(l[i])
555 if value is not None:
556 if value == 1 and res.hour < 12:
557 res.hour += 12
558 elif value == 0 and res.hour == 12:
559 res.hour = 0
560 i += 1
561 continue
562
563 # Check for a timezone name
564 if (res.hour is not None and len(l[i]) <= 5 and
565 res.tzname is None and res.tzoffset is None and
566 not [x for x in l[i] if x not in string.ascii_uppercase]):
567 res.tzname = l[i]
568 res.tzoffset = info.tzoffset(res.tzname)
569 i += 1
570
571 # Check for something like GMT+3, or BRST+3. Notice
572 # that it doesn't mean "I am 3 hours after GMT", but
573 # "my time +3 is GMT". If found, we reverse the
574 # logic so that timezone parsing code will get it
575 # right.
576 if i < len_l and l[i] in ('+', '-'):
577 l[i] = ('+', '-')[l[i] == '+']
578 res.tzoffset = None
579 if info.utczone(res.tzname):
580 # With something like GMT+3, the timezone
581 # is *not* GMT.
582 res.tzname = None
583
584 continue
585
586 # Check for a numbered timezone
587 if res.hour is not None and l[i] in ('+', '-'):
588 signal = (-1,1)[l[i] == '+']
589 i += 1
590 len_li = len(l[i])
591 if len_li == 4:
592 # -0300
593 res.tzoffset = int(l[i][:2])*3600+int(l[i][2:])*60
594 elif i+1 < len_l and l[i+1] == ':':
595 # -03:00
596 res.tzoffset = int(l[i])*3600+int(l[i+2])*60
597 i += 2
598 elif len_li <= 2:
599 # -[0]3
600 res.tzoffset = int(l[i][:2])*3600
601 else:
602 return None
603 i += 1
604 res.tzoffset *= signal
605
606 # Look for a timezone name between parenthesis
607 if (i+3 < len_l and
608 info.jump(l[i]) and l[i+1] == '(' and l[i+3] == ')' and
609 3 <= len(l[i+2]) <= 5 and
610 not [x for x in l[i+2]
611 if x not in string.ascii_uppercase]):
612 # -0300 (BRST)
613 res.tzname = l[i+2]
614 i += 4
615 continue
616
617 # Check jumps
618 if not (info.jump(l[i]) or fuzzy):
619 return None
620
621 i += 1
622
623 # Process year/month/day
624 len_ymd = len(ymd)
625 if len_ymd > 3:
626 # More than three members!?
627 return None
628 elif len_ymd == 1 or (mstridx != -1 and len_ymd == 2):
629 # One member, or two members with a month string
630 if mstridx != -1:
631 res.month = ymd[mstridx]
632 del ymd[mstridx]
633 if len_ymd > 1 or mstridx == -1:
634 if ymd[0] > 31:
635 res.year = ymd[0]
636 else:
637 res.day = ymd[0]
638 elif len_ymd == 2:
639 # Two members with numbers
640 if ymd[0] > 31:
641 # 99-01
642 res.year, res.month = ymd
643 elif ymd[1] > 31:
644 # 01-99
645 res.month, res.year = ymd
646 elif dayfirst and ymd[1] <= 12:
647 # 13-01
648 res.day, res.month = ymd
649 else:
650 # 01-13
651 res.month, res.day = ymd
652 if len_ymd == 3:
653 # Three members
654 if mstridx == 0:
655 res.month, res.day, res.year = ymd
656 elif mstridx == 1:
657 if ymd[0] > 31 or (yearfirst and ymd[2] <= 31):
658 # 99-Jan-01
659 res.year, res.month, res.day = ymd
660 else:
661 # 01-Jan-01
662 # Give precendence to day-first, since
663 # two-digit years is usually hand-written.
664 res.day, res.month, res.year = ymd
665 elif mstridx == 2:
666 # WTF!?
667 if ymd[1] > 31:
668 # 01-99-Jan
669 res.day, res.year, res.month = ymd
670 else:
671 # 99-01-Jan
672 res.year, res.day, res.month = ymd
673 else:
674 if ymd[0] > 31 or \
675 (yearfirst and ymd[1] <= 12 and ymd[2] <= 31):
676 # 99-01-01
677 res.year, res.month, res.day = ymd
678 elif ymd[0] > 12 or (dayfirst and ymd[1] <= 12):
679 # 13-01-01
680 res.day, res.month, res.year = ymd
681 else:
682 # 01-13-01
683 res.month, res.day, res.year = ymd
684
685 except (IndexError, ValueError, AssertionError):
686 return None
687
688 if not info.validate(res):
689 return None
690 return res
691
692 DEFAULTPARSER = parser()
693 def parse(timestr, parserinfo=None, **kwargs):
694 if parserinfo:
695 return parser(parserinfo).parse(timestr, **kwargs)
696 else:
697 return DEFAULTPARSER.parse(timestr, **kwargs)
698
7c5ea8a @niemeyer Snapshot of python-dateutil-1.3
niemeyer authored
699
700 class _tzparser(object):
8260b86 @niemeyer Snapshot of python-dateutil-1.0
niemeyer authored
701
702 class _result(_resultbase):
703
704 __slots__ = ["stdabbr", "stdoffset", "dstabbr", "dstoffset",
705 "start", "end"]
706
707 class _attr(_resultbase):
708 __slots__ = ["month", "week", "weekday",
709 "yday", "jyday", "day", "time"]
710
711 def __repr__(self):
712 return self._repr("")
713
714 def __init__(self):
715 _resultbase.__init__(self)
716 self.start = self._attr()
717 self.end = self._attr()
718
719 def parse(self, tzstr):
720 res = self._result()
721 l = _timelex.split(tzstr)
722 try:
723
724 len_l = len(l)
725
726 i = 0
727 while i < len_l:
728 # BRST+3[BRDT[+2]]
729 j = i
730 while j < len_l and not [x for x in l[j]
731 if x in "0123456789:,-+"]:
732 j += 1
733 if j != i:
734 if not res.stdabbr:
735 offattr = "stdoffset"
736 res.stdabbr = "".join(l[i:j])
737 else:
738 offattr = "dstoffset"
739 res.dstabbr = "".join(l[i:j])
740 i = j
741 if (i < len_l and
742 (l[i] in ('+', '-') or l[i][0] in "0123456789")):
743 if l[i] in ('+', '-'):
a48bcfe @niemeyer Snapshot of python-dateutil-1.4
niemeyer authored
744 # Yes, that's right. See the TZ variable
745 # documentation.
8260b86 @niemeyer Snapshot of python-dateutil-1.0
niemeyer authored
746 signal = (1,-1)[l[i] == '+']
747 i += 1
748 else:
749 signal = -1
750 len_li = len(l[i])
751 if len_li == 4:
752 # -0300
753 setattr(res, offattr,
754 (int(l[i][:2])*3600+int(l[i][2:])*60)*signal)
755 elif i+1 < len_l and l[i+1] == ':':
756 # -03:00
757 setattr(res, offattr,
758 (int(l[i])*3600+int(l[i+2])*60)*signal)
759 i += 2
760 elif len_li <= 2:
761 # -[0]3
762 setattr(res, offattr,
763 int(l[i][:2])*3600*signal)
764 else:
765 return None
766 i += 1
767 if res.dstabbr:
768 break
769 else:
770 break
771
772 if i < len_l:
773 for j in range(i, len_l):
774 if l[j] == ';': l[j] = ','
775
776 assert l[i] == ','
777
778 i += 1
779
780 if i >= len_l:
781 pass
782 elif (8 <= l.count(',') <= 9 and
783 not [y for x in l[i:] if x != ','
784 for y in x if y not in "0123456789"]):
785 # GMT0BST,3,0,30,3600,10,0,26,7200[,3600]
786 for x in (res.start, res.end):
787 x.month = int(l[i])
788 i += 2
789 if l[i] == '-':
790 value = int(l[i+1])*-1
791 i += 1
792 else:
793 value = int(l[i])
794 i += 2
795 if value:
796 x.week = value
797 x.weekday = (int(l[i])-1)%7
798 else:
799 x.day = int(l[i])
800 i += 2
801 x.time = int(l[i])
802 i += 2
803 if i < len_l:
804 if l[i] in ('-','+'):
805 signal = (-1,1)[l[i] == "+"]
806 i += 1
807 else:
808 signal = 1
809 res.dstoffset = (res.stdoffset+int(l[i]))*signal
810 elif (l.count(',') == 2 and l[i:].count('/') <= 2 and
811 not [y for x in l[i:] if x not in (',','/','J','M',
812 '.','-',':')
813 for y in x if y not in "0123456789"]):
814 for x in (res.start, res.end):
815 if l[i] == 'J':
816 # non-leap year day (1 based)
817 i += 1
818 x.jyday = int(l[i])
819 elif l[i] == 'M':
820 # month[-.]week[-.]weekday
821 i += 1
822 x.month = int(l[i])
823 i += 1
824 assert l[i] in ('-', '.')
825 i += 1
826 x.week = int(l[i])
827 if x.week == 5:
828 x.week = -1
829 i += 1
830 assert l[i] in ('-', '.')
831 i += 1
832 x.weekday = (int(l[i])-1)%7
833 else:
834 # year day (zero based)
835 x.yday = int(l[i])+1
836
837 i += 1
838
839 if i < len_l and l[i] == '/':
840 i += 1
841 # start time
842 len_li = len(l[i])
843 if len_li == 4:
844 # -0300
845 x.time = (int(l[i][:2])*3600+int(l[i][2:])*60)
846 elif i+1 < len_l and l[i+1] == ':':
847 # -03:00
848 x.time = int(l[i])*3600+int(l[i+2])*60
849 i += 2
850 if i+1 < len_l and l[i+1] == ':':
851 i += 2
852 x.time += int(l[i])
853 elif len_li <= 2:
854 # -[0]3
855 x.time = (int(l[i][:2])*3600)
856 else:
857 return None
858 i += 1
859
860 assert i == len_l or l[i] == ','
861
862 i += 1
863
864 assert i >= len_l
865
866 except (IndexError, ValueError, AssertionError):
867 return None
a48bcfe @niemeyer Snapshot of python-dateutil-1.4
niemeyer authored
868
8260b86 @niemeyer Snapshot of python-dateutil-1.0
niemeyer authored
869 return res
870
7c5ea8a @niemeyer Snapshot of python-dateutil-1.3
niemeyer authored
871
8260b86 @niemeyer Snapshot of python-dateutil-1.0
niemeyer authored
872 DEFAULTTZPARSER = _tzparser()
873 def _parsetz(tzstr):
874 return DEFAULTTZPARSER.parse(tzstr)
875
7c5ea8a @niemeyer Snapshot of python-dateutil-1.3
niemeyer authored
876
877 def _parsems(value):
a48bcfe @niemeyer Snapshot of python-dateutil-1.4
niemeyer authored
878 """Parse a I[.F] seconds value into (seconds, microseconds)."""
879 if "." not in value:
880 return int(value), 0
881 else:
882 i, f = value.split(".")
883 return int(i), int(f.ljust(6, "0")[:6])
7c5ea8a @niemeyer Snapshot of python-dateutil-1.3
niemeyer authored
884
885
8260b86 @niemeyer Snapshot of python-dateutil-1.0
niemeyer authored
886 # vim:ts=4:sw=4:et
Something went wrong with that request. Please try again.