~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-14 11:27:20 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-20050914112720-c66a21de86eafa6e
trim fai cribbage

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