"""Canonical ISO date format YYYY-MM-DDTHH:mm:SS+HH:mm

This parser is _extremely_ strict, and the dates that match it,
though really easy to work with for the computer, are not particularly
readable.  See the iso_date_loose module for a slightly relaxed
definition which allows the "T" character to be replaced by a
" " character, and allows a space before the timezone offset, as well
as allowing the integer values to use non-0-padded integers.


    ISO_date -- YYYY-MM-DD format, with a month and date optional
    ISO_time -- HH:mm:SS format, with minutes and seconds optional
    ISO_date_time -- YYYY-MM-DD HH:mm:SS+HH:mm format,
        with time optional and TimeZone offset optional

Interpreter:
    MxInterpreter
        Interprets the parse tree as mx.DateTime values
        ISO_date and ISO_time
            returns DateTime objects
        Time only
            returns RelativeDateTime object which, when
            added to a DateTime gives you the given time
            within that day
"""
try:
    from mx import DateTime
    haveMX = 1
except ImportError:
    haveMX = 0
from simpleparse.parser import Parser
from simpleparse import common, objectgenerator
from simpleparse.common import chartypes, numbers
from simpleparse.dispatchprocessor import *

c = {}

declaration ="""
year      := digit,digit,digit,digit
month     := digit,digit
day       := digit,digit

hour      := digit,digit
minute    := digit,digit
second    := digit,digit
offset_sign := [-+]
offset    := offset_sign, hour, time_separator?, minute

<date_separator> := '-'
<time_separator> := ':'

ISO_date   := year, (date_separator, month, (date_separator, day)?)?
ISO_time   := hour, (time_separator, minute, (time_separator, second)?)?
ISO_date_time  := ISO_date, ([T], ISO_time)?, offset?
"""




_p = Parser( declaration )
for name in ["ISO_time","ISO_date", "ISO_date_time"]:
    c[ name ] = objectgenerator.LibraryElement(
        generator = _p._generator,
        production = name,
    )
common.share( c )

if haveMX:
    class MxInterpreter(DispatchProcessor):
        """Interpret a parsed ISO_date_time_loose in GMT/UTC time or localtime
        """
        def __init__(
            self,
            inputLocal = 1,
            returnLocal = 1,
        ):
            self.inputLocal = inputLocal
            self.returnLocal = returnLocal
        dateName = 'ISO_date'
        timeName = 'ISO_time'
        def ISO_date_time( self, info, buffer):
            """Interpret the loose ISO date + time format"""
            (tag, left, right, sublist) = info
            set = singleMap( sublist, self, buffer )
            base, time, offset = (
                set.get(self.dateName),
                set.get(self.timeName) or DateTime.RelativeDateTime(hour=0,minute=0,second=0),
                set.get( "offset" ),
            )
            base = base + time
            offset = set.get( "offset" )
            if offset is not None:
                # an explicit timezone was entered, convert to gmt and return as appropriate...
                gmt = base - offset
                if self.returnLocal:
                    return gmt.localtime()
                else:
                    return gmt
            # was in the default input locale (either gmt or local)
            if self.inputLocal and self.returnLocal:
                return base
            elif not self.inputLocal and not self.returnLocal:
                return base
            elif self.inputLocal and not self.returnLocal:
                # return gmt from local...
                return base.gmtime()
            else:
                return base.localtime()
        def ISO_date( self, info, buffer):
            """Interpret the ISO date format"""
            (tag, left, right, sublist) = info
            set = {}
            for item in sublist:
                set[ item[0] ] = dispatch( self, item, buffer)
            return DateTime.DateTime(
                set.get("year") or now().year,
                set.get("month") or 1,
                set.get("day") or 1,
            )
        def ISO_time( self, info, buffer):
            """Interpret the ISO time format"""
            (tag, left, right, sublist) = info
            set = {}
            for item in sublist:
                set[ item[0] ] = dispatch( self, item, buffer)
            return DateTime.RelativeDateTime(
                hour = set.get("hour") or 0,
                minute = set.get("minute") or 0,
                second = set.get("second") or 0,
            )

        integer = numbers.IntInterpreter()
        second =  offset_minute = offset_hour = year = month = day = hour =minute =integer
        
        def offset( self, info, buffer):
            """Calculate the time zone offset as a date-time delta"""
            (tag, left, right, sublist) = info
            set = singleMap( sublist, self, buffer )
            direction = set.get('offset_sign',1)
            hour = set.get( "hour", 0)
            minute = set.get( "minute", 0)
            delta = DateTime.DateTimeDelta( 0, hour*direction, minute*direction)
            return delta
            
        def offset_sign( self , info, buffer):
            """Interpret the offset sign as a multiplier"""
            (tag, left, right, sublist) = info
            v = buffer [left: right]
            if v in ' +':
                return 1
            else:
                return -1
                
