~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to fai/python-dateutil-0.4/dateutil/parser.py

  • Committer: Robert Collins
  • Date: 2005-09-13 10:46:27 UTC
  • mto: (147.2.6) (364.1.3 bzrtools)
  • mto: This revision was merged to the branch mainline in revision 324.
  • Revision ID: robertc@robertcollins.net-20050913104627-51f938950a907475
handle inaccessible sibling archives somewhat - note version-0 is still not handled

Show diffs side-by-side

added added

removed removed

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