1"""Somewhat Looser ISO date format YYYY-MM-DD HH:mm:SS +HH:mm
2
3    ISO_date_loose -- YYYY-MM-DD format, with a month and day optional,
4        month or day may be specified without leading 0
5    ISO_time_loose -- HH:mm:SS format, with minutes and seconds optional
6        all numbers may be specified without leading 0
7    ISO_date_time_loose -- YYYY-MM-DD HH:mm:SS +HH:mm format,
8        with time optional and TimeZone offset optional,
9        same format for date and time as above
10
11Interpreter:
12    MxInterpreter
13        Interprets the parse tree as mx.DateTime values
14        Date and DateTime -> DateTime objects
15        Time only -> RelativeDateTime
16"""
17try:
18    from mx import DateTime
19    haveMX = 1
20except ImportError:
21    haveMX = 0
22from simpleparse.parser import Parser
23from simpleparse import common, objectgenerator
24from simpleparse.common import chartypes, numbers
25from simpleparse.dispatchprocessor import *
26
27c = {}
28declaration = """
29<date_separator> := [-]
30<time_separator> := ':'
31offset_sign := [-+]
32
33year    := int
34month   := int
35day     := int
36hour    := int
37minute  := int
38second  := float/int
39ISO_date_loose := year, (date_separator, month, (date_separator, day)?)?
40ISO_time_loose := hour, (time_separator, minute, (time_separator, second)?)?
41offset    := offset_sign, offset_hour, time_separator?, offset_minute?
42offset_hour := digit, digit
43offset_minute := digit, digit
44
45ISO_date_time_loose  := ISO_date_loose, ([T ], ISO_time_loose)?, [ ]?, offset?
46"""
47
48_p = Parser( declaration )
49for name in ["ISO_time_loose","ISO_date_time_loose", "ISO_date_loose"]:
50    c[ name ] = objectgenerator.LibraryElement(
51        generator = _p._generator,
52        production = name,
53    )
54common.share( c )
55
56if haveMX:
57    class MxInterpreter(DispatchProcessor):
58        """Interpret a parsed ISO_date_time_loose in GMT/UTC time or localtime
59        """
60        int = numbers.IntInterpreter()
61        offset_minute = offset_hour = year = month = day = hour = minute = int
62
63        float = numbers.FloatInterpreter()
64        second =  float
65
66        def __init__(
67            self,
68            inputLocal = 1,
69            returnLocal = 1,
70        ):
71            self.inputLocal = inputLocal
72            self.returnLocal = returnLocal
73        dateName = 'ISO_date_loose'
74        timeName = 'ISO_time_loose'
75        def ISO_date_time_loose( self, info, buffer):
76            """Interpret the loose ISO date + time format"""
77            (tag, left, right, sublist) = info
78            set = singleMap( sublist, self, buffer )
79            base, time, offset = (
80                set.get(self.dateName),
81                set.get(self.timeName) or DateTime.RelativeDateTime(hour=0,minute=0,second=0),
82                set.get( "offset" ),
83            )
84            base = base + time
85            offset = set.get( "offset" )
86            if offset is not None:
87                # an explicit timezone was entered, convert to gmt and return as appropriate...
88                gmt = base - offset
89                if self.returnLocal:
90                    return gmt.localtime()
91                else:
92                    return gmt
93            # was in the default input locale (either gmt or local)
94            if self.inputLocal and self.returnLocal:
95                return base
96            elif not self.inputLocal and not self.returnLocal:
97                return base
98            elif self.inputLocal and not self.returnLocal:
99                # return gmt from local...
100                return base.gmtime()
101            else:
102                return base.localtime()
103        def ISO_date_loose( self, info, buffer):
104            """Interpret the loose ISO date format"""
105            (tag, left, right, sublist) = info
106            set = singleMap( sublist, self, buffer )
107            return DateTime.DateTime(
108                set.get("year") or now().year,
109                set.get("month") or 1,
110                set.get("day") or 1,
111            )
112        def ISO_time_loose( self, info, buffer):
113            """Interpret the loose ISO time format"""
114            (tag, left, right, sublist) = info
115            set = singleMap( sublist, self, buffer )
116            return DateTime.RelativeDateTime(
117                hour = set.get("hour") or 0,
118                minute = set.get("minute") or 0,
119                second = set.get("second") or 0,
120            )
121
122
123        def offset( self, info, buffer):
124            """Calculate the time zone offset as a date-time delta"""
125            (tag, left, right, sublist) = info
126            set = singleMap( sublist, self, buffer )
127            direction = set.get('offset_sign',1)
128            hour = set.get( "offset_hour", 0)
129            minute = set.get( "offset_minute", 0)
130            delta = DateTime.DateTimeDelta( 0, hour*direction, minute*direction)
131            return delta
132
133        def offset_sign( self , info, buffer):
134            """Interpret the offset sign as a multiplier"""
135            (tag, left, right, sublist) = info
136            v = buffer [left: right]
137            if v in ' +':
138                return 1
139            else:
140                return -1
141
142