embedded/mx/DateTime/ISO.py
changeset 0 b97547f5f1fa
equal deleted inserted replaced
-1:000000000000 0:b97547f5f1fa
       
     1 """ This module provides a set of constructors and routines to convert
       
     2     between DateTime[Delta] instances and ISO representations of date
       
     3     and time.
       
     4 
       
     5     Note: Timezones are only interpreted by ParseDateTimeGMT(). All
       
     6     other constructors silently ignore the time zone information.
       
     7 
       
     8     Copyright (c) 1998-2000, Marc-Andre Lemburg; mailto:mal@lemburg.com
       
     9     Copyright (c) 2000-2007, eGenix.com Software GmbH; mailto:info@egenix.com
       
    10     See the documentation for further information on copyrights,
       
    11     or contact the author.
       
    12 
       
    13 """
       
    14 import DateTime,Timezone
       
    15 import re,string
       
    16 
       
    17 # Grammar: ISO 8601 (not all, but what we need from it)
       
    18 _year = '(?P<year>\d?\d\d\d)'
       
    19 _month = '(?P<month>\d?\d)'
       
    20 _day = '(?P<day>\d?\d)'
       
    21 _hour = '(?P<hour>\d?\d)'
       
    22 _minute = '(?P<minute>\d?\d)'
       
    23 _second = '(?P<second>\d?\d(?:\.\d+)?)'
       
    24 _sign = '(?P<sign>[-+])'
       
    25 _week = 'W(?P<week>\d?\d)'
       
    26 _zone = Timezone.isozone
       
    27 
       
    28 _weekdate = _year + '-?(?:' + _week + '-?' + _day + '?)?'
       
    29 _date = _year + '-?' + '(?:' + _month + '-?' + _day + '?)?'
       
    30 _time = _hour + ':?' + _minute + ':?' + _second + '?(?:' + _zone + ')?'
       
    31 
       
    32 isodatetimeRE = re.compile(_date + '(?:[ T]' + _time + ')?$')
       
    33 isodateRE = re.compile(_date + '$')
       
    34 isotimeRE = re.compile(_time + '$')
       
    35 isodeltaRE = re.compile(_sign + '?' + _time + '$')
       
    36 isoweekRE = re.compile(_weekdate + '$')
       
    37 isoweektimeRE = re.compile(_weekdate + '(?:[ T]' + _time + ')?$')
       
    38 
       
    39 def WeekTime(year,isoweek=1,isoday=1,hour=0,minute=0,second=0.0):
       
    40 
       
    41     """Week(year,isoweek=1,isoday=1,hour=0,minute=0,second=0.0)
       
    42 
       
    43        Returns a DateTime instance pointing to the given ISO week and
       
    44        day.  isoday defaults to 1, which corresponds to Monday in the
       
    45        ISO numbering. The time part is set as given.
       
    46 
       
    47     """
       
    48     d = DateTime.DateTime(year,1,1,hour,minute,second)
       
    49     if d.iso_week[0] == year:
       
    50         # 1.1. belongs to year (backup to Monday)
       
    51         return d + (-d.day_of_week + 7 * (isoweek-1) + isoday-1)
       
    52     else:
       
    53         # 1.1. belongs to year-1 (advance to next Monday)
       
    54         return d + (7-d.day_of_week + 7 * (isoweek-1) + isoday-1)
       
    55 
       
    56 # Alias
       
    57 Week = WeekTime
       
    58 
       
    59 # Aliases for the other constructors (they all happen to already use
       
    60 # ISO format)
       
    61 Date = DateTime.Date
       
    62 Time = DateTime.Time
       
    63 TimeDelta = DateTime.TimeDelta
       
    64 
       
    65 def ParseDateTime(isostring,parse_isodatetime=isodatetimeRE.match,
       
    66 
       
    67                   strip=string.strip,atoi=string.atoi,atof=string.atof):
       
    68 
       
    69     """ParseDateTime(isostring)
       
    70 
       
    71        Returns a DateTime instance reflecting the given ISO date. A
       
    72        time part is optional and must be delimited from the date by a
       
    73        space or 'T'.
       
    74 
       
    75        Time zone information is parsed, but not evaluated.
       
    76 
       
    77     """
       
    78     s = strip(isostring)
       
    79     date = parse_isodatetime(s)
       
    80     if not date:
       
    81         raise ValueError,'wrong format, use YYYY-MM-DD HH:MM:SS'
       
    82     year,month,day,hour,minute,second,zone = date.groups()
       
    83     year = atoi(year)
       
    84     if month is None:
       
    85         month = 1
       
    86     else:
       
    87         month = atoi(month)
       
    88     if day is None:
       
    89         day = 1
       
    90     else:
       
    91         day = atoi(day)
       
    92     if hour is None:
       
    93         hour = 0
       
    94     else:
       
    95         hour = atoi(hour)
       
    96     if minute is None:
       
    97         minute = 0
       
    98     else:
       
    99         minute = atoi(minute)
       
   100     if second is None:
       
   101         second = 0.0
       
   102     else:
       
   103         second = atof(second)
       
   104     return DateTime.DateTime(year,month,day,hour,minute,second)
       
   105 
       
   106 def ParseDateTimeGMT(isostring,parse_isodatetime=isodatetimeRE.match,
       
   107 
       
   108                      strip=string.strip,atoi=string.atoi,atof=string.atof):
       
   109 
       
   110     """ParseDateTimeGMT(isostring)
       
   111 
       
   112        Returns a DateTime instance in UTC reflecting the given ISO
       
   113        date. A time part is optional and must be delimited from the
       
   114        date by a space or 'T'. Timezones are honored.
       
   115 
       
   116     """
       
   117     s = strip(isostring)
       
   118     date = parse_isodatetime(s)
       
   119     if not date:
       
   120         raise ValueError,'wrong format, use YYYY-MM-DD HH:MM:SS'
       
   121     year,month,day,hour,minute,second,zone = date.groups()
       
   122     year = atoi(year)
       
   123     if month is None:
       
   124         month = 1
       
   125     else:
       
   126         month = atoi(month)
       
   127     if day is None:
       
   128         day = 1
       
   129     else:
       
   130         day = atoi(day)
       
   131     if hour is None:
       
   132         hour = 0
       
   133     else:
       
   134         hour = atoi(hour)
       
   135     if minute is None:
       
   136         minute = 0
       
   137     else:
       
   138         minute = atoi(minute)
       
   139     if second is None:
       
   140         second = 0.0
       
   141     else:
       
   142         second = atof(second)
       
   143     offset = Timezone.utc_offset(zone)
       
   144     return DateTime.DateTime(year,month,day,hour,minute,second) - offset
       
   145 
       
   146 # Alias
       
   147 ParseDateTimeUTC = ParseDateTimeGMT
       
   148 
       
   149 def ParseDate(isostring,parse_isodate=isodateRE.match,
       
   150 
       
   151               strip=string.strip,atoi=string.atoi,atof=string.atof):
       
   152 
       
   153     """ParseDate(isostring)
       
   154 
       
   155        Returns a DateTime instance reflecting the given ISO date. A
       
   156        time part may not be included.
       
   157 
       
   158     """
       
   159     s = strip(isostring)
       
   160     date = parse_isodate(s)
       
   161     if not date:
       
   162         raise ValueError,'wrong format, use YYYY-MM-DD'
       
   163     year,month,day = date.groups()
       
   164     year = atoi(year)
       
   165     if month is None:
       
   166         month = 1
       
   167     else:
       
   168         month = atoi(month)
       
   169     if day is None:
       
   170         day = 1
       
   171     else:
       
   172         day = atoi(day)
       
   173     return DateTime.DateTime(year,month,day)
       
   174 
       
   175 def ParseWeek(isostring,parse_isoweek=isoweekRE.match,
       
   176 
       
   177               strip=string.strip,atoi=string.atoi,atof=string.atof):
       
   178 
       
   179     """ParseWeek(isostring)
       
   180 
       
   181        Returns a DateTime instance reflecting the given ISO date. A
       
   182        time part may not be included.
       
   183 
       
   184     """
       
   185     s = strip(isostring)
       
   186     date = parse_isoweek(s)
       
   187     if not date:
       
   188         raise ValueError,'wrong format, use yyyy-Www-d, e.g. 1998-W01-1'
       
   189     year,week,day = date.groups()
       
   190     year = atoi(year)
       
   191     if week is None:
       
   192         week = 1
       
   193     else:
       
   194         week = atoi(week)
       
   195     if day is None:
       
   196         day = 1
       
   197     else:
       
   198         day = atoi(day)
       
   199     return Week(year,week,day)
       
   200 
       
   201 def ParseWeekTime(isostring,parse_isoweektime=isoweektimeRE.match,
       
   202 
       
   203                   strip=string.strip,atoi=string.atoi,atof=string.atof):
       
   204 
       
   205     """ParseWeekTime(isostring)
       
   206 
       
   207        Returns a DateTime instance reflecting the given ISO date. A
       
   208        time part is optional and must be delimited from the date by a
       
   209        space or 'T'.
       
   210 
       
   211     """
       
   212     s = strip(isostring)
       
   213     date = parse_isoweektime(s)
       
   214     if not date:
       
   215         raise ValueError,'wrong format, use e.g. "1998-W01-1 12:00:30"'
       
   216     year,week,day,hour,minute,second,zone = date.groups()
       
   217     year = atoi(year)
       
   218     if week is None:
       
   219         week = 1
       
   220     else:
       
   221         week = atoi(week)
       
   222     if day is None:
       
   223         day = 1
       
   224     else:
       
   225         day = atoi(day)
       
   226     if hour is None:
       
   227         hour = 0
       
   228     else:
       
   229         hour = atoi(hour)
       
   230     if minute is None:
       
   231         minute = 0
       
   232     else:
       
   233         minute = atoi(minute)
       
   234     if second is None:
       
   235         second = 0.0
       
   236     else:
       
   237         second = atof(second)
       
   238     return WeekTime(year,week,day,hour,minute,second)
       
   239 
       
   240 def ParseTime(isostring,parse_isotime=isotimeRE.match,
       
   241 
       
   242               strip=string.strip,atoi=string.atoi,atof=string.atof):
       
   243 
       
   244     """ParseTime(isostring)
       
   245 
       
   246        Returns a DateTimeDelta instance reflecting the given ISO time.
       
   247        Hours and minutes must be given, seconds are
       
   248        optional. Fractions of a second may also be used,
       
   249        e.g. 12:23:12.34.
       
   250 
       
   251     """
       
   252     s = strip(isostring)
       
   253     time = parse_isotime(s)
       
   254     if not time:
       
   255         raise ValueError,'wrong format, use HH:MM:SS'
       
   256     hour,minute,second,zone = time.groups()
       
   257     hour = atoi(hour)
       
   258     minute = atoi(minute)
       
   259     if second is not None:
       
   260         second = atof(second)
       
   261     else:
       
   262         second = 0.0
       
   263     return DateTime.TimeDelta(hour,minute,second)
       
   264 
       
   265 def ParseTimeDelta(isostring,parse_isodelta=isodeltaRE.match,
       
   266 
       
   267                    strip=string.strip,atoi=string.atoi,atof=string.atof):
       
   268 
       
   269     """ParseTimeDelta(isostring)
       
   270 
       
   271        Returns a DateTimeDelta instance reflecting the given ISO time
       
   272        as delta. Hours and minutes must be given, seconds are
       
   273        optional. Fractions of a second may also be used,
       
   274        e.g. 12:23:12.34. In addition to the ISO standard a sign may be
       
   275        prepended to the time, e.g. -12:34.
       
   276 
       
   277     """
       
   278     s = strip(isostring)
       
   279     time = parse_isodelta(s)
       
   280     if not time:
       
   281         raise ValueError,'wrong format, use [-]HH:MM:SS'
       
   282     sign,hour,minute,second,zone = time.groups()
       
   283     hour = atoi(hour)
       
   284     minute = atoi(minute)
       
   285     if second is not None:
       
   286         second = atof(second)
       
   287     else:
       
   288         second = 0.0
       
   289     if sign and sign == '-':
       
   290         return -DateTime.TimeDelta(hour,minute,second)
       
   291     else:
       
   292         return DateTime.TimeDelta(hour,minute,second)
       
   293 
       
   294 def ParseAny(isostring):
       
   295 
       
   296     """ParseAny(isostring)
       
   297 
       
   298        Parses the given string and tries to convert it to a
       
   299        DateTime[Delta] instance.
       
   300 
       
   301     """
       
   302     try:
       
   303         return ParseDateTime(isostring)
       
   304     except ValueError:
       
   305         pass
       
   306     try:
       
   307         return ParseWeekTime(isostring)
       
   308     except ValueError:
       
   309         pass
       
   310     try:
       
   311         return ParseTimeDelta(isostring)
       
   312     except ValueError:
       
   313         raise ValueError,'unsupported format: "%s"' % isostring
       
   314 
       
   315 def str(datetime,tz=None):
       
   316 
       
   317     """str(datetime,tz=DateTime.tz_offset(datetime))
       
   318 
       
   319        Returns the datetime instance as ISO date string. tz can be
       
   320        given as DateTimeDelta instance providing the time zone
       
   321        difference from datetime's zone to UTC. It defaults to
       
   322        DateTime.tz_offset(datetime) which assumes local time.
       
   323 
       
   324     """
       
   325     if tz is None:
       
   326         tz = datetime.gmtoffset()
       
   327     return '%04i-%02i-%02i %02i:%02i:%02i%+03i%02i' % (
       
   328         datetime.year, datetime.month, datetime.day, 
       
   329         datetime.hour, datetime.minute, datetime.second,
       
   330         tz.hour,tz.minute)
       
   331 
       
   332 def strGMT(datetime):
       
   333 
       
   334     """strGMT(datetime)
       
   335 
       
   336        Returns the datetime instance as ISO date string assuming it is
       
   337        given in GMT.
       
   338 
       
   339     """
       
   340     return '%04i-%02i-%02i %02i:%02i:%02i+0000' % (
       
   341         datetime.year, datetime.month, datetime.day, 
       
   342         datetime.hour, datetime.minute, datetime.second)
       
   343 
       
   344 def strUTC(datetime):
       
   345 
       
   346     """strUTC(datetime)
       
   347 
       
   348        Returns the datetime instance as ISO date string assuming it is
       
   349        given in UTC.
       
   350 
       
   351     """
       
   352     return '%04i-%02i-%02i %02i:%02i:%02i+0000' % (
       
   353         datetime.year, datetime.month, datetime.day, 
       
   354         datetime.hour, datetime.minute, datetime.second)
       
   355 
       
   356 # Testing
       
   357 if __name__ == '__main__':
       
   358     e = DateTime.Date(1900,1,1)
       
   359     for i in range(100000):
       
   360         d = e + i
       
   361         year,week,day = d.iso_week
       
   362         c = WeekTime(year,week,day)
       
   363         if d != c:
       
   364             print ' Check %s (given; %i) != %s (parsed)' % (d,d.day_of_week,c)
       
   365         elif i % 1000 == 0:
       
   366             print d,'ok'