embedded/mx/DateTime/Parser.py
changeset 1808 aa09e20dd8c0
parent 1693 49075f57cf2c
parent 1807 6d541c610165
child 1810 e95e876be17c
equal deleted inserted replaced
1693:49075f57cf2c 1808:aa09e20dd8c0
     1 # -*- coding: latin-1 -*-
       
     2 
       
     3 """ Date/Time string parsing module.
       
     4 
       
     5     Note about the Y2K problems:
       
     6 
       
     7        The parser can only handle years with at least 2 digits. 2
       
     8        digit year values get expanded by adding the century using
       
     9        DateTime.add_century(), while 3 digit year get converted
       
    10        literally. To have 2 digit years also be interpreted literally,
       
    11        add leading zeros, e.g. year 99 must be written as 099 or 0099.
       
    12 
       
    13     Copyright (c) 1998-2000, Marc-Andre Lemburg; mailto:mal@lemburg.com
       
    14     Copyright (c) 2000-2007, eGenix.com Software GmbH; mailto:info@egenix.com
       
    15     See the documentation for further information on copyrights,
       
    16     or contact the author. All Rights Reserved.
       
    17 
       
    18 """
       
    19 import types,re,string
       
    20 import DateTime,ISO,ARPA,Timezone
       
    21 
       
    22 # Enable to produce debugging output
       
    23 _debug = 0
       
    24 
       
    25 # REs for matching date and time parts in a string; These REs
       
    26 # parse a superset of ARPA, ISO, American and European style dates.
       
    27 # Timezones are supported via the Timezone submodule.
       
    28 
       
    29 _year = '(?P<year>-?\d+\d(?!:))'
       
    30 _fullyear = '(?P<year>-?\d+\d\d(?!:))'
       
    31 _year_epoch = '(?:' + _year + '(?P<epoch> *[ABCDE\.]+)?)'
       
    32 _fullyear_epoch = '(?:' + _fullyear + '(?P<epoch> *[ABCDE\.]+)?)'
       
    33 _relyear = '(?:\((?P<relyear>[-+]?\d+)\))'
       
    34 
       
    35 _month = '(?P<month>\d?\d(?!:))'
       
    36 _fullmonth = '(?P<month>\d\d(?!:))'
       
    37 _litmonth = ('(?P<litmonth>'
       
    38              'jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec|'
       
    39              'mär|mae|mrz|mai|okt|dez|'
       
    40              'fev|avr|juin|juil|aou|aoû|déc|'
       
    41              'ene|abr|ago|dic|'
       
    42              'out'
       
    43              ')[a-z,\.;]*')
       
    44 litmonthtable = {
       
    45     # English
       
    46     'jan':1, 'feb':2, 'mar':3, 'apr':4, 'may':5, 'jun':6,
       
    47     'jul':7, 'aug':8, 'sep':9, 'oct':10, 'nov':11, 'dec':12,
       
    48     # German
       
    49     'mär':3, 'mae':3, 'mrz':3, 'mai':5, 'okt':10, 'dez':12,
       
    50     # French
       
    51     'fev':2, 'avr':4, 'juin':6, 'juil':7, 'aou':8, 'aoû':8,
       
    52     'déc':12,
       
    53     # Spanish
       
    54     'ene':1, 'abr':4, 'ago':8, 'dic':12,
       
    55     # Portuguese
       
    56     'out':10,
       
    57     }
       
    58 _relmonth = '(?:\((?P<relmonth>[-+]?\d+)\))'
       
    59 
       
    60 _day = '(?P<day>\d?\d(?!:))'
       
    61 _usday = '(?P<day>\d?\d(?!:))(?:st|nd|rd|th|[,\.;])?'
       
    62 _fullday = '(?P<day>\d\d(?!:))'
       
    63 _litday = ('(?P<litday>'
       
    64            'mon|tue|wed|thu|fri|sat|sun|'
       
    65            'die|mit|don|fre|sam|son|'
       
    66            'lun|mar|mer|jeu|ven|sam|dim|'
       
    67            'mie|jue|vie|sab|dom|'
       
    68            'pri|seg|ter|cua|qui'
       
    69            ')[a-z]*')
       
    70 litdaytable = {
       
    71     # English
       
    72     'mon':0, 'tue':1, 'wed':2, 'thu':3, 'fri':4, 'sat':5, 'sun':6,
       
    73     # German
       
    74     'die':1, 'mit':2, 'don':3, 'fre':4, 'sam':5, 'son':6,
       
    75     # French
       
    76     'lun':0, 'mar':1, 'mer':2, 'jeu':3, 'ven':4, 'sam':5, 'dim':6,
       
    77     # Spanish
       
    78     'mie':2, 'jue':3, 'vie':4, 'sab':5, 'dom':6,
       
    79     # Portuguese
       
    80     'pri':0, 'seg':1, 'ter':2, 'cua':3, 'qui':4,
       
    81     }
       
    82 _relday = '(?:\((?P<relday>[-+]?\d+)\))'
       
    83 
       
    84 _hour = '(?P<hour>[012]?\d)'
       
    85 _minute = '(?P<minute>[0-6]\d)'
       
    86 _second = '(?P<second>[0-6]\d(?:[.,]\d+)?)'
       
    87 
       
    88 _days = '(?P<days>\d*\d(?:[.,]\d+)?)'
       
    89 _hours = '(?P<hours>\d*\d(?:[.,]\d+)?)'
       
    90 _minutes = '(?P<minutes>\d*\d(?:[.,]\d+)?)'
       
    91 _seconds = '(?P<seconds>\d*\d(?:[.,]\d+)?)'
       
    92 
       
    93 _reldays = '(?:\((?P<reldays>[-+]?\d+(?:[.,]\d+)?)\))'
       
    94 _relhours = '(?:\((?P<relhours>[-+]?\d+(?:[.,]\d+)?)\))'
       
    95 _relminutes = '(?:\((?P<relminutes>[-+]?\d+(?:[.,]\d+)?)\))'
       
    96 _relseconds = '(?:\((?P<relseconds>[-+]?\d+(?:[.,]\d+)?)\))'
       
    97 
       
    98 _sign = '(?:(?P<sign>[-+]) *)'
       
    99 _week = 'W(?P<week>\d?\d)'
       
   100 _zone = Timezone.zone
       
   101 _ampm = '(?P<ampm>[ap][m.]+)'
       
   102 
       
   103 _time = (_hour + ':' + _minute + '(?::' + _second + '|[^:]|$) *'
       
   104          + _ampm + '? *' + _zone + '?')
       
   105 _isotime = _hour + ':?' + _minute + ':?' + _second + '? *' + _zone + '?'
       
   106 
       
   107 _weekdate = _year + '-?(?:' + _week + '-?' + _day + '?)?'
       
   108 _eurodate = _day + '\.' + _month + '\.' + _year_epoch + '?'
       
   109 _usdate = _month + '/' + _day + '(?:/' + _year_epoch + '|[^/]|$)'
       
   110 _altusdate = _month + '-' + _day + '-' + _fullyear_epoch
       
   111 _isodate = _year + '-' + _month + '-?' + _day + '?(?!:)'
       
   112 _altisodate = _year + _fullmonth + _fullday + '(?!:)'
       
   113 _usisodate = _fullyear + '/' + _fullmonth + '/' + _fullday
       
   114 _litdate = ('(?:'+ _litday + ',? )? *' + 
       
   115             _usday + ' *' + 
       
   116             '[- ] *(?:' + _litmonth + '|'+ _month +') *[- ] *' +
       
   117             _year_epoch + '?')
       
   118 _altlitdate = ('(?:'+ _litday + ',? )? *' + 
       
   119                _litmonth + '[ ,.a-z]+' +
       
   120                _usday + 
       
   121                '(?:[ a-z]+' + _year_epoch + ')?')
       
   122 _eurlitdate = ('(?:'+ _litday + ',?[ a-z]+)? *' + 
       
   123                '(?:'+ _usday + '[ a-z]+)? *' +
       
   124                _litmonth + 
       
   125                '(?:[ ,.a-z]+' + _year_epoch + ')?')
       
   126 
       
   127 _relany = '[*%?a-zA-Z]+'
       
   128 
       
   129 _relisodate = ('(?:(?:' + _relany + '|' + _year + '|' + _relyear + ')-' +
       
   130                '(?:' + _relany + '|' + _month + '|' + _relmonth + ')-' +
       
   131                '(?:' + _relany + '|' + _day + '|' + _relday + '))')
       
   132 
       
   133 _asctime = ('(?:'+ _litday + ',? )? *' + 
       
   134                 _usday + ' *' + 
       
   135                 '[- ] *(?:' + _litmonth + '|'+ _month +') *[- ]' +
       
   136                 '(?:[0-9: ]+)' + 
       
   137                 _year_epoch + '?')
       
   138 
       
   139 _relisotime = ('(?:(?:' + _relany + '|' + _hour + '|' + _relhours + '):' +
       
   140                '(?:' + _relany + '|' + _minute + '|' + _relminutes + ')' +
       
   141                '(?::(?:' + _relany + '|' + _second + '|' + _relseconds + '))?)')
       
   142 
       
   143 _isodelta1 = (_sign + '?' +
       
   144               _days + ':' + _hours + ':' + _minutes + ':' + _seconds)
       
   145 _isodelta2 = (_sign + '?' + 
       
   146               _hours + ':' + _minutes + ':' + _seconds)
       
   147 _isodelta3 = (_sign + '?' + 
       
   148               _hours + ':' + _minutes)
       
   149 _litdelta = (_sign + '?' +
       
   150              '(?:' + _days + ' *d[a-z]*[,; ]*)?' + 
       
   151              '(?:' + _hours + ' *h[a-z]*[,; ]*)?' + 
       
   152              '(?:' + _minutes + ' *m[a-z]*[,; ]*)?' +
       
   153              '(?:' + _seconds + ' *s[a-z]*[,; ]*)?')
       
   154 _litdelta2 = (_sign + '?' +
       
   155              '(?:' + _days + ' *d[a-z]*[,; ]*)?' + 
       
   156               _hours + ':' + _minutes + '(?::' + _seconds + ')?')
       
   157 
       
   158 _timeRE = re.compile(_time, re.I)
       
   159 _isotimeRE = re.compile(_isotime, re.I)
       
   160 _isodateRE = re.compile(_isodate, re.I)
       
   161 _altisodateRE = re.compile(_altisodate, re.I)
       
   162 _usisodateRE = re.compile(_usisodate, re.I)
       
   163 _eurodateRE = re.compile(_eurodate, re.I)
       
   164 _usdateRE = re.compile(_usdate, re.I)
       
   165 _altusdateRE = re.compile(_altusdate, re.I)
       
   166 _litdateRE = re.compile(_litdate, re.I)
       
   167 _altlitdateRE = re.compile(_altlitdate, re.I)
       
   168 _eurlitdateRE = re.compile(_eurlitdate, re.I)
       
   169 _relisodateRE = re.compile(_relisodate, re.I)
       
   170 _asctimeRE = re.compile(_asctime, re.I)
       
   171 _isodelta1RE = re.compile(_isodelta1)
       
   172 _isodelta2RE = re.compile(_isodelta2)
       
   173 _isodelta3RE = re.compile(_isodelta3)
       
   174 _litdeltaRE = re.compile(_litdelta)
       
   175 _litdelta2RE = re.compile(_litdelta2)
       
   176 _relisotimeRE = re.compile(_relisotime, re.I)
       
   177 
       
   178 # Available date parsers
       
   179 _date_formats = ('euro',
       
   180                  'usiso', 'us', 'altus',
       
   181                  'iso', 'altiso', 
       
   182                  'lit', 'altlit', 'eurlit',
       
   183                  'unknown')
       
   184 
       
   185 # Available time parsers
       
   186 _time_formats = ('standard',
       
   187                  'iso',
       
   188                  'unknown')
       
   189 
       
   190 def _parse_date(text, formats=_date_formats, defaultdate=None,
       
   191 
       
   192                 int=int,float=float,lower=string.lower,
       
   193                 add_century=DateTime.add_century,
       
   194                 now=DateTime.now,us_formats=('us', 'altus'),
       
   195                 iso_formats=('iso', 'altiso', 'usiso')):
       
   196 
       
   197     """ Parses the date part given in text and returns a tuple
       
   198         (text,day,month,year,style) with the following
       
   199         meanings:
       
   200 
       
   201         * text gives the original text without the date part
       
   202 
       
   203         * day,month,year give the parsed date
       
   204 
       
   205         * style gives information about which parser was successful:
       
   206           'euro' - the European date parser
       
   207           'us' - the US date parser
       
   208           'altus' - the alternative US date parser (with '-' instead of '/')
       
   209           'iso' - the ISO date parser
       
   210           'altiso' - the alternative ISO date parser (without '-')
       
   211           'usiso' - US style ISO date parser (yyyy/mm/dd)
       
   212           'lit' - the US literal date parser
       
   213           'altlit' - the alternative US literal date parser
       
   214           'eurlit' - the Eurpean literal date parser
       
   215           'unknown' - no date part was found, defaultdate was used
       
   216 
       
   217         formats may be set to a tuple of style strings specifying
       
   218         which of the above parsers to use and in which order to try
       
   219         them. Default is to try all of them in the above order.
       
   220 
       
   221         defaultdate provides the defaults to use in case no date part
       
   222         is found. Most other parsers default to the current year
       
   223         January 1 if some of these date parts are missing.
       
   224 
       
   225         If 'unknown' is not given in formats and the date cannot be
       
   226         parsed, a ValueError is raised.
       
   227 
       
   228     """
       
   229     match = None
       
   230     style = ''
       
   231     
       
   232     # Apply parsers in the order given in formats
       
   233     for format in formats:
       
   234 
       
   235         if format == 'euro':
       
   236             # European style date
       
   237             match = _eurodateRE.search(text)
       
   238             if match is not None:
       
   239                 day,month,year,epoch = match.groups()
       
   240                 if year:
       
   241                     if len(year) == 2:
       
   242                         # Y2K problem:
       
   243                         year = add_century(int(year))
       
   244                     else:
       
   245                         year = int(year)
       
   246                 else:
       
   247                     if defaultdate is None:
       
   248                         defaultdate = now()
       
   249                     year = defaultdate.year
       
   250                 if epoch and 'B' in epoch:
       
   251                     year = -year + 1
       
   252                 month = int(month)
       
   253                 day = int(day)
       
   254                 # Could have mistaken euro format for us style date
       
   255                 # which uses month, day order
       
   256                 if month > 12 or month == 0:
       
   257                     match = None
       
   258                     continue
       
   259                 break
       
   260 
       
   261         elif format in iso_formats:
       
   262             # ISO style date
       
   263             if format == 'iso':
       
   264                 match = _isodateRE.search(text)
       
   265             elif format == 'altiso':
       
   266                 match = _altisodateRE.search(text)
       
   267                 # Avoid mistaking ISO time parts ('Thhmmss') for dates
       
   268                 if match is not None:
       
   269                     left, right = match.span()
       
   270                     if left > 0 and \
       
   271                        text[left - 1:left] == 'T':
       
   272                         match = None
       
   273                         continue
       
   274             else:
       
   275                 match = _usisodateRE.search(text)
       
   276             if match is not None:
       
   277                 year,month,day = match.groups()
       
   278                 if len(year) == 2:
       
   279                     # Y2K problem:
       
   280                     year = add_century(int(year))
       
   281                 else:
       
   282                     year = int(year)
       
   283                 # Default to January 1st
       
   284                 if not month:
       
   285                     month = 1
       
   286                 else:
       
   287                     month = int(month)
       
   288                 if not day:
       
   289                     day = 1
       
   290                 else:
       
   291                     day = int(day)
       
   292                 break
       
   293 
       
   294         elif format in us_formats:
       
   295             # US style date
       
   296             if format == 'us':
       
   297                 match = _usdateRE.search(text)
       
   298             else:
       
   299                 match = _altusdateRE.search(text)
       
   300             if match is not None:
       
   301                 month,day,year,epoch = match.groups()
       
   302                 if year:
       
   303                     if len(year) == 2:
       
   304                         # Y2K problem:
       
   305                         year = add_century(int(year))
       
   306                     else:
       
   307                         year = int(year)
       
   308                 else:
       
   309                     if defaultdate is None:
       
   310                         defaultdate = now()
       
   311                     year = defaultdate.year
       
   312                 if epoch and 'B' in epoch:
       
   313                     year = -year + 1
       
   314                 # Default to 1 if no day is given
       
   315                 if day:
       
   316                     day = int(day)
       
   317                 else:
       
   318                     day = 1
       
   319                 month = int(month)
       
   320                 # Could have mistaken us format for euro style date
       
   321                 # which uses day, month order
       
   322                 if month > 12 or month == 0:
       
   323                     match = None
       
   324                     continue
       
   325                 break
       
   326 
       
   327         elif format == 'lit':
       
   328             # US style literal date
       
   329             match = _litdateRE.search(text)
       
   330             if match is not None:
       
   331                 litday,day,litmonth,month,year,epoch = match.groups()
       
   332                 break
       
   333 
       
   334         elif format == 'altlit':
       
   335             # Alternative US style literal date
       
   336             match = _altlitdateRE.search(text)
       
   337             if match is not None: 
       
   338                 litday,litmonth,day,year,epoch = match.groups()
       
   339                 month = '<missing>'
       
   340                 break
       
   341 
       
   342         elif format == 'eurlit':
       
   343             # European style literal date
       
   344             match = _eurlitdateRE.search(text)
       
   345             if match is not None: 
       
   346                 litday,day,litmonth,year,epoch = match.groups()
       
   347                 month = '<missing>'
       
   348                 break
       
   349 
       
   350         elif format == 'unknown':
       
   351             # No date part: use defaultdate
       
   352             if defaultdate is None:
       
   353                 defaultdate = now()
       
   354             year = defaultdate.year
       
   355             month = defaultdate.month
       
   356             day = defaultdate.day
       
   357             style = format
       
   358             break
       
   359 
       
   360     # Check success
       
   361     if match is not None:
       
   362         # Remove date from text
       
   363         left, right = match.span()
       
   364         if 0 and _debug:
       
   365             print 'parsed date:',repr(text[left:right]),\
       
   366                   'giving:',year,month,day
       
   367         text = text[:left] + text[right:]
       
   368         style = format
       
   369         
       
   370     elif not style:
       
   371         # Not recognized: raise an error
       
   372         raise ValueError, 'unknown date format: "%s"' % text
       
   373 
       
   374     # Literal date post-processing
       
   375     if style in ('lit', 'altlit', 'eurlit'):
       
   376         if 0 and _debug: print match.groups()
       
   377         # Default to current year, January 1st
       
   378         if not year:
       
   379             if defaultdate is None:
       
   380                 defaultdate = now()
       
   381             year = defaultdate.year
       
   382         else:
       
   383             if len(year) == 2:
       
   384                 # Y2K problem:
       
   385                 year = add_century(int(year))
       
   386             else:
       
   387                 year = int(year)
       
   388         if epoch and 'B' in epoch:
       
   389             year = -year + 1
       
   390         if litmonth:
       
   391             litmonth = lower(litmonth)
       
   392             try:
       
   393                 month = litmonthtable[litmonth]
       
   394             except KeyError:
       
   395                 raise ValueError,\
       
   396                       'wrong month name: "%s"' % litmonth
       
   397         elif month:
       
   398             month = int(month)
       
   399         else:
       
   400             month = 1
       
   401         if day:
       
   402             day = int(day)
       
   403         else:
       
   404             day = 1
       
   405 
       
   406     #print '_parse_date:',text,day,month,year,style
       
   407     return text,day,month,year,style
       
   408 
       
   409 def _parse_time(text, formats=_time_formats,
       
   410 
       
   411                 int=int,float=float,replace=string.replace):
       
   412 
       
   413     """ Parses a time part given in text and returns a tuple
       
   414         (text,hour,minute,second,offset,style) with the following
       
   415         meanings:
       
   416 
       
   417         * text gives the original text without the time part
       
   418         * hour,minute,second give the parsed time
       
   419         * offset gives the time zone UTC offset
       
   420         * style gives information about which parser was successful:
       
   421           'standard' - the standard parser
       
   422           'iso' - the ISO time format parser
       
   423           'unknown' - no time part was found
       
   424 
       
   425         formats may be set to a tuple specifying the parsers to use:
       
   426           'standard' - standard time format with ':' delimiter
       
   427           'iso' - ISO time format (superset of 'standard')
       
   428           'unknown' - default to 0:00:00, 0 zone offset
       
   429 
       
   430         If 'unknown' is not given in formats and the time cannot be
       
   431         parsed, a ValueError is raised.
       
   432 
       
   433     """
       
   434     match = None
       
   435     style = ''
       
   436 
       
   437     # Apply parsers in the order given in formats
       
   438     for format in formats:
       
   439 
       
   440         # Standard format
       
   441         if format == 'standard':
       
   442             match = _timeRE.search(text)
       
   443             if match is not None:
       
   444                 hour,minute,second,ampm,zone = match.groups()
       
   445                 style = 'standard'
       
   446                 break
       
   447 
       
   448         # ISO format
       
   449         if format == 'iso':
       
   450             match =  _isotimeRE.search(text)
       
   451             if match is not None:
       
   452                 hour,minute,second,zone = match.groups()
       
   453                 ampm = None
       
   454                 style = 'iso'
       
   455                 break
       
   456 
       
   457         # Default handling
       
   458         elif format == 'unknown':
       
   459             hour,minute,second,offset = 0,0,0.0,0
       
   460             style = 'unknown'
       
   461             break
       
   462 
       
   463     if not style:
       
   464         # If no default handling should be applied, raise an error
       
   465         raise ValueError, 'unknown time format: "%s"' % text
       
   466 
       
   467     # Post-processing
       
   468     if match is not None:
       
   469         if zone:
       
   470             # Convert to UTC offset
       
   471             offset = Timezone.utc_offset(zone)
       
   472         else:
       
   473             offset = 0
       
   474         hour = int(hour)
       
   475         if ampm:
       
   476             if ampm[0] in ('p', 'P'):
       
   477                 # 12pm = midday
       
   478                 if hour < 12:
       
   479                     hour = hour + 12
       
   480             else:
       
   481                 # 12am = midnight 
       
   482                 if hour >= 12:
       
   483                     hour = hour - 12
       
   484         if minute:
       
   485             minute = int(minute)
       
   486         else:
       
   487             minute = 0
       
   488         if not second:
       
   489             second = 0.0
       
   490         else:
       
   491             if ',' in second:
       
   492                 second = replace(second, ',', '.')
       
   493             second = float(second)
       
   494 
       
   495         # Remove time from text
       
   496         left,right = match.span()
       
   497         if 0 and _debug: 
       
   498             print 'parsed time:',repr(text[left:right]),\
       
   499                   'giving:',hour,minute,second,offset
       
   500         text = text[:left] + text[right:]
       
   501 
       
   502     #print '_parse_time:',text,hour,minute,second,offset,style
       
   503     return text,hour,minute,second,offset,style
       
   504 
       
   505 ###
       
   506 
       
   507 def DateTimeFromString(text, formats=_date_formats, defaultdate=None,
       
   508                        time_formats=_time_formats,
       
   509 
       
   510                        DateTime=DateTime):
       
   511 
       
   512     """ DateTimeFromString(text, [formats, defaultdate])
       
   513     
       
   514         Returns a DateTime instance reflecting the date and time given
       
   515         in text. In case a timezone is given, the returned instance
       
   516         will point to the corresponding UTC time value. Otherwise, the
       
   517         value is set as given in the string.
       
   518 
       
   519         formats may be set to a tuple of strings specifying which of
       
   520         the following parsers to use and in which order to try
       
   521         them. Default is to try all of them in the order given below:
       
   522 
       
   523           'euro' - the European date parser
       
   524           'us' - the US date parser
       
   525           'altus' - the alternative US date parser (with '-' instead of '/')
       
   526           'iso' - the ISO date parser
       
   527           'altiso' - the alternative ISO date parser (without '-')
       
   528           'usiso' - US style ISO date parser (yyyy/mm/dd)
       
   529           'lit' - the US literal date parser
       
   530           'altlit' - the alternative US literal date parser
       
   531           'eurlit' - the Eurpean literal date parser
       
   532           'unknown' - if no date part is found, use defaultdate
       
   533 
       
   534         defaultdate provides the defaults to use in case no date part
       
   535         is found. Most of the parsers default to the current year
       
   536         January 1 if some of these date parts are missing.
       
   537 
       
   538         If 'unknown' is not given in formats and the date cannot
       
   539         be parsed, a ValueError is raised.
       
   540 
       
   541         time_formats may be set to a tuple of strings specifying which
       
   542         of the following parsers to use and in which order to try
       
   543         them. Default is to try all of them in the order given below:
       
   544 
       
   545           'standard' - standard time format HH:MM:SS (with ':' delimiter)
       
   546           'iso' - ISO time format (superset of 'standard')
       
   547           'unknown' - default to 00:00:00 in case the time format
       
   548                       cannot be parsed
       
   549 
       
   550         Defaults to 00:00:00.00 for time parts that are not included
       
   551         in the textual representation.
       
   552 
       
   553         If 'unknown' is not given in time_formats and the time cannot
       
   554         be parsed, a ValueError is raised.
       
   555 
       
   556     """
       
   557     origtext = text
       
   558     formats = tuple(formats)
       
   559 
       
   560     if formats is _date_formats or \
       
   561        'iso' in formats or \
       
   562        'altiso' in formats:
       
   563 
       
   564         # First try standard order (parse time, then date)
       
   565         if formats[0] not in ('iso', 'altiso'):
       
   566             text,hour,minute,second,offset,timestyle = _parse_time(
       
   567                 origtext,
       
   568                 time_formats)
       
   569             text,day,month,year,datestyle = _parse_date(
       
   570                 text,
       
   571                 formats + ('unknown',),
       
   572                 defaultdate)
       
   573             if 0 and _debug:
       
   574                 print 'tried time/date on %s, date=%s, time=%s' % (origtext,
       
   575                                                                    datestyle,
       
   576                                                                    timestyle)
       
   577         else:
       
   578             timestyle = 'iso'
       
   579             
       
   580         # If this fails, try the ISO order (date, then time)
       
   581         if timestyle in ('iso', 'unknown'):
       
   582             text,day,month,year,datestyle = _parse_date(
       
   583                 origtext,
       
   584                 formats,
       
   585                 defaultdate)
       
   586             text,hour,minute,second,offset,timestyle = _parse_time(
       
   587                 text,
       
   588                 time_formats)
       
   589             if 0 and _debug:
       
   590                 print 'tried ISO on %s, date=%s, time=%s' % (origtext,
       
   591                                                              datestyle,
       
   592                                                              timestyle)
       
   593     else:
       
   594         # Standard order: time part, then date part
       
   595         text,hour,minute,second,offset,timestyle = _parse_time(
       
   596             origtext,
       
   597             time_formats)
       
   598         text,day,month,year,datestyle = _parse_date(
       
   599             text,
       
   600             formats,
       
   601             defaultdate)
       
   602 
       
   603     if (datestyle == 'unknown' and 'unknown' not in formats) or \
       
   604        (timestyle == 'unknown' and 'unknown' not in time_formats):
       
   605         raise ValueError,\
       
   606               'Failed to parse "%s": found "%s" date, "%s" time' % \
       
   607               (origtext, datestyle, timestyle)
       
   608     
       
   609     try:
       
   610         return DateTime.DateTime(year,month,day,hour,minute,second) - offset
       
   611     except DateTime.RangeError, why:
       
   612         raise DateTime.RangeError,\
       
   613               'Failed to parse "%s": %s' % (origtext, why)
       
   614 
       
   615 def DateFromString(text, formats=_date_formats, defaultdate=None,
       
   616 
       
   617                    DateTime=DateTime):
       
   618 
       
   619     """ DateFromString(text, [formats, defaultdate])
       
   620     
       
   621         Returns a DateTime instance reflecting the date given in
       
   622         text. A possibly included time part is ignored.
       
   623 
       
   624         formats and defaultdate work just like for
       
   625         DateTimeFromString().
       
   626 
       
   627     """
       
   628     _text,day,month,year,datestyle = _parse_date(text, formats, defaultdate)
       
   629 
       
   630     if datestyle == 'unknown' and \
       
   631        'unknown' not in formats:
       
   632         raise ValueError,\
       
   633               'Failed to parse "%s": found "%s" date' % \
       
   634               (origtext, datestyle)
       
   635 
       
   636     try:
       
   637         return DateTime.DateTime(year,month,day)
       
   638     except DateTime.RangeError, why:
       
   639         raise DateTime.RangeError,\
       
   640               'Failed to parse "%s": %s' % (text, why)
       
   641 
       
   642 def validateDateTimeString(text, formats=_date_formats):
       
   643 
       
   644     """ validateDateTimeString(text, [formats, defaultdate])
       
   645 
       
   646         Validates the given text and returns 1/0 depending on whether
       
   647         text includes parseable date and time values or not.
       
   648 
       
   649         formats works just like for DateTimeFromString() and defines
       
   650         the order of date/time parsers to apply. It defaults to the
       
   651         same list of parsers as for DateTimeFromString().
       
   652 
       
   653         XXX Undocumented !
       
   654     
       
   655     """
       
   656     formats = list(formats)
       
   657     if 'unknown' in formats:
       
   658         formats.remove('unknown')
       
   659     try:
       
   660         DateTimeFromString(text, formats)
       
   661     except (DateTime.RangeError, ValueError), why:
       
   662         return 0
       
   663     return 1
       
   664 
       
   665 def validateDateString(text, formats=_date_formats):
       
   666 
       
   667     """ validateDateString(text, [formats, defaultdate])
       
   668 
       
   669         Validates the given text and returns 1/0 depending on whether
       
   670         text includes a parseable date value or not.
       
   671 
       
   672         formats works just like for DateTimeFromString() and defines
       
   673         the order of date/time parsers to apply. It defaults to the
       
   674         same list of parsers as for DateTimeFromString().
       
   675     
       
   676         XXX Undocumented !
       
   677     
       
   678     """
       
   679     formats = list(formats)
       
   680     if 'unknown' in formats:
       
   681         formats.remove('unknown')
       
   682     try:
       
   683         DateFromString(text, formats)
       
   684     except (DateTime.RangeError, ValueError), why:
       
   685         return 0
       
   686     return 1
       
   687 
       
   688 def TimeFromString(text, formats=_time_formats,
       
   689 
       
   690                    DateTime=DateTime):
       
   691 
       
   692     """ TimeFromString(text, [formats])
       
   693     
       
   694         Returns a DateTimeDelta instance reflecting the time given in
       
   695         text. A possibly included date part is ignored.
       
   696 
       
   697         formats may be set to a tuple of strings specifying which of
       
   698         the following parsers to use and in which order to try
       
   699         them. Default is to try all of them in the order given below:
       
   700 
       
   701           'standard' - standard time format with ':' delimiter
       
   702           'iso' - ISO time format (superset of 'standard')
       
   703           'unknown' - default to 00:00:00 in case the time format
       
   704                       cannot be parsed
       
   705 
       
   706         Defaults to 00:00:00.00 for parts that are not included in the
       
   707         textual representation.
       
   708         
       
   709     """
       
   710     _text,hour,minute,second,offset,timestyle = _parse_time(
       
   711         text,
       
   712         formats)
       
   713 
       
   714     if timestyle == 'unknown' and \
       
   715        'unknown' not in formats:
       
   716         raise ValueError,\
       
   717               'Failed to parse "%s": found "%s" time' % \
       
   718               (text, timestyle)
       
   719 
       
   720     try:
       
   721         dtd = DateTime.DateTimeDelta(0.0, hour, minute, second)
       
   722     except DateTime.RangeError, why:
       
   723         raise DateTime.RangeError,\
       
   724               'Failed to parse "%s": %s' % (text, why)
       
   725     else:
       
   726         # XXX What to do with offset ?
       
   727         return dtd
       
   728 
       
   729 #
       
   730 # XXX Still missing: validateTimeString(), validateDateTimeDeltaString()
       
   731 #                    and validateTimeDeltaString()
       
   732 #
       
   733 
       
   734 def DateTimeDeltaFromString(text,
       
   735 
       
   736                             float=float,DateTime=DateTime):
       
   737 
       
   738     """ DateTimeDeltaFromString(text)
       
   739     
       
   740         Returns a DateTimeDelta instance reflecting the delta given in
       
   741         text. Defaults to 0:00:00:00.00 for parts that are not
       
   742         included in the textual representation or cannot be parsed.
       
   743 
       
   744     """
       
   745     match = _isodelta1RE.search(text)
       
   746     if match is not None:
       
   747         sign, days, hours, minutes, seconds = match.groups()
       
   748     else:
       
   749         match = _litdelta2RE.search(text)
       
   750         if match is not None:
       
   751             sign, days, hours, minutes, seconds = match.groups()
       
   752         else:
       
   753             match = _isodelta2RE.search(text)
       
   754             if match is not None:
       
   755                 sign, hours, minutes, seconds = match.groups()
       
   756                 days = None
       
   757             else:
       
   758                 match = _isodelta3RE.search(text)
       
   759                 if match is not None:
       
   760                     sign, hours, minutes = match.groups()
       
   761                     days = None
       
   762                     seconds = None
       
   763                 else:
       
   764                     match = _litdeltaRE.search(text)
       
   765                     if match is not None:
       
   766                         sign, days, hours, minutes, seconds = match.groups()
       
   767 
       
   768                     else:
       
   769                         # Not matched:
       
   770                         return DateTime.DateTimeDelta(0.0)
       
   771 
       
   772     # Conversions
       
   773     if days:
       
   774         days = float(days)
       
   775     else:
       
   776         days = 0.0
       
   777     if hours:
       
   778         hours = float(hours)
       
   779     else:
       
   780         hours = 0.0
       
   781     if minutes:
       
   782         minutes = float(minutes)
       
   783     else:
       
   784         minutes = 0.0
       
   785     if seconds:
       
   786         seconds = float(seconds)
       
   787     else:
       
   788         seconds = 0.0
       
   789     if sign != '-':
       
   790         sign = 1
       
   791     else:
       
   792         sign = -1
       
   793 
       
   794     try:
       
   795         dtd = DateTime.DateTimeDelta(days,hours,minutes,seconds)
       
   796     except DateTime.RangeError, why:
       
   797         raise DateTime.RangeError,\
       
   798               'Failed to parse "%s": %s' % (text, why)
       
   799     else:
       
   800         if sign < 0:
       
   801             return -dtd
       
   802         else:
       
   803             return dtd
       
   804 
       
   805 # Aliases
       
   806 TimeDeltaFromString = DateTimeDeltaFromString
       
   807 
       
   808 ###
       
   809 
       
   810 def _parse_reldate(text,
       
   811 
       
   812                    int=int,float=float):
       
   813 
       
   814     match = _relisodateRE.search(text)
       
   815     if match is not None:
       
   816         groups = match.groups()
       
   817         if 0 and _debug: print groups
       
   818         year,years,month,months,day,days = groups
       
   819         if year:
       
   820             year = int(year)
       
   821         if years:
       
   822             years = float(years)
       
   823         else:
       
   824             years = 0
       
   825         if month:
       
   826             month = int(month)
       
   827         if months:
       
   828             months = float(months)
       
   829         else:
       
   830             months = 0
       
   831         if day:
       
   832             day = int(day)
       
   833         if days:
       
   834             days = float(days)
       
   835         else:
       
   836             days = 0
       
   837         return year,years,month,months,day,days
       
   838     else:
       
   839         return None,0,None,0,None,0
       
   840 
       
   841 def _parse_reltime(text,
       
   842 
       
   843                    int=int,float=float):
       
   844 
       
   845     match = _relisotimeRE.search(text)
       
   846     if match is not None:
       
   847         groups = match.groups()
       
   848         if 0 and _debug: print groups
       
   849         hour,hours,minute,minutes,second,seconds = groups
       
   850         if hour:
       
   851             hour = int(hour)
       
   852         if hours:
       
   853             hours = float(hours)
       
   854         else:
       
   855             hours = 0
       
   856         if minute:
       
   857             minute = int(minute)
       
   858         if minutes:
       
   859             minutes = float(minutes)
       
   860         else:
       
   861             minutes = 0
       
   862         if second:
       
   863             second = int(second)
       
   864         if seconds:
       
   865             seconds = float(seconds)
       
   866         else:
       
   867             seconds = 0
       
   868         return hour,hours,minute,minutes,second,seconds
       
   869     else:
       
   870         return None,0,None,0,None,0
       
   871 
       
   872 def RelativeDateTimeFromString(text,
       
   873 
       
   874                                RelativeDateTime=DateTime.RelativeDateTime):
       
   875 
       
   876     """ RelativeDateTimeFromString(text)
       
   877     
       
   878         Returns a RelativeDateTime instance reflecting the relative
       
   879         date and time given in text.
       
   880 
       
   881         Defaults to wildcards for parts or values which are not
       
   882         included in the textual representation or cannot be parsed.
       
   883 
       
   884         The format used in text must adhere to the following syntax:
       
   885 
       
   886                         [YYYY-MM-DD] [HH:MM[:SS]]
       
   887 
       
   888         with the usual meanings. Values which should not be altered
       
   889         may be replaced with '*', '%', '?' or any combination of
       
   890         letters, e.g. 'YYYY'. Relative settings must be enclosed in
       
   891         parenthesis if given and should include a sign, e.g. '(+0001)'
       
   892         for the year part. All other settings are interpreted as
       
   893         absolute values.
       
   894 
       
   895         Date and time parts are both optional as a whole. Seconds in
       
   896         the time part are optional too. Everything else (including the
       
   897         hyphens and colons) is mandatory.
       
   898 
       
   899     """
       
   900     year,years,month,months,day,days = _parse_reldate(text)
       
   901     hour,hours,minute,minutes,second,seconds = _parse_reltime(text)
       
   902     return RelativeDateTime(year=year,years=years,
       
   903                             month=month,months=months,
       
   904                             day=day,days=days,
       
   905                             hour=hour,hours=hours,
       
   906                             minute=minute,minutes=minutes,
       
   907                             second=second,seconds=seconds)
       
   908 
       
   909 def RelativeDateFromString(text,
       
   910 
       
   911                            RelativeDateTime=DateTime.RelativeDateTime):
       
   912 
       
   913     """ RelativeDateFromString(text)
       
   914     
       
   915         Same as RelativeDateTimeFromString(text) except that only the
       
   916         date part of text is taken into account.
       
   917 
       
   918     """
       
   919     year,years,month,months,day,days = _parse_reldate(text)
       
   920     return RelativeDateTime(year=year,years=years,
       
   921                             month=month,months=months,
       
   922                             day=day,days=days)
       
   923 
       
   924 def RelativeTimeFromString(text,
       
   925 
       
   926                            RelativeDateTime=DateTime.RelativeDateTime):
       
   927 
       
   928     """ RelativeTimeFromString(text)
       
   929     
       
   930         Same as RelativeDateTimeFromString(text) except that only the
       
   931         time part of text is taken into account.
       
   932 
       
   933     """
       
   934     hour,hours,minute,minutes,second,seconds = _parse_reltime(text)
       
   935     return RelativeDateTime(hour=hour,hours=hours,
       
   936                             minute=minute,minutes=minutes,
       
   937                             second=second,seconds=seconds)
       
   938 
       
   939 ### Tests
       
   940 
       
   941 def _test():
       
   942 
       
   943     import sys
       
   944 
       
   945     t = DateTime.now()
       
   946 
       
   947     print 'Testing DateTime Parser...'
       
   948 
       
   949     l = [
       
   950 
       
   951         # Literal formats
       
   952         ('Sun Nov  6 08:49:37 1994', '1994-11-06 08:49:37.00'),
       
   953         ('sun nov  6 08:49:37 1994', '1994-11-06 08:49:37.00'),
       
   954         ('sUN NOV  6 08:49:37 1994', '1994-11-06 08:49:37.00'),
       
   955         ('Sunday, 06-Nov-94 08:49:37 GMT', '1994-11-06 08:49:37.00'),
       
   956         ('Sun, 06 Nov 1994 08:49:37 GMT', '1994-11-06 08:49:37.00'),
       
   957         ('06-Nov-94 08:49:37', '1994-11-06 08:49:37.00'),
       
   958         ('06-Nov-94', '1994-11-06 00:00:00.00'),
       
   959         ('06-NOV-94', '1994-11-06 00:00:00.00'),
       
   960         ('November 19 08:49:37', '%s-11-19 08:49:37.00' % t.year),
       
   961         ('Nov. 9', '%s-11-09 00:00:00.00' % t.year),
       
   962         ('Sonntag, der 6. November 1994, 08:49:37 GMT', '1994-11-06 08:49:37.00'),
       
   963         ('6. November 2001, 08:49:37', '2001-11-06 08:49:37.00'),
       
   964         ('sep 6', '%s-09-06 00:00:00.00' % t.year),
       
   965         ('sep 6 2000', '2000-09-06 00:00:00.00'),
       
   966         ('September 29', '%s-09-29 00:00:00.00' % t.year),
       
   967         ('Sep. 29', '%s-09-29 00:00:00.00' % t.year),
       
   968         ('6 sep', '%s-09-06 00:00:00.00' % t.year),
       
   969         ('29 September', '%s-09-29 00:00:00.00' % t.year),
       
   970         ('29 Sep.', '%s-09-29 00:00:00.00' % t.year),
       
   971         ('sep 6 2001', '2001-09-06 00:00:00.00'),
       
   972         ('Sep 6, 2001', '2001-09-06 00:00:00.00'),
       
   973         ('September 6, 2001', '2001-09-06 00:00:00.00'),
       
   974         ('sep 6 01', '2001-09-06 00:00:00.00'),
       
   975         ('Sep 6, 01', '2001-09-06 00:00:00.00'),
       
   976         ('September 6, 01', '2001-09-06 00:00:00.00'),
       
   977         ('30 Apr 2006 20:19:00', '2006-04-30 20:19:00.00'),
       
   978         
       
   979         # ISO formats
       
   980         ('1994-11-06 08:49:37', '1994-11-06 08:49:37.00'),
       
   981         ('010203', '2001-02-03 00:00:00.00'),
       
   982         ('2001-02-03 00:00:00.00', '2001-02-03 00:00:00.00'),
       
   983         ('2001-02 00:00:00.00', '2001-02-01 00:00:00.00'),
       
   984         ('2001-02-03', '2001-02-03 00:00:00.00'),
       
   985         ('2001-02', '2001-02-01 00:00:00.00'),
       
   986         ('20000824/2300', '2000-08-24 23:00:00.00'),
       
   987         ('20000824/0102', '2000-08-24 01:02:00.00'),
       
   988         ('20000824', '2000-08-24 00:00:00.00'),
       
   989         ('20000824/020301', '2000-08-24 02:03:01.00'),
       
   990         ('20000824 020301', '2000-08-24 02:03:01.00'),
       
   991         ('-20000824 020301', '-2000-08-24 02:03:01.00'),
       
   992         ('20000824T020301', '2000-08-24 02:03:01.00'),
       
   993         ('20000824 020301', '2000-08-24 02:03:01.00'),
       
   994         ('2000-08-24 02:03:01.00', '2000-08-24 02:03:01.00'),
       
   995         ('T020311', '%s 02:03:11.00' % t.date),
       
   996         ('2003-12-9', '2003-12-09 00:00:00.00'),
       
   997         ('03-12-9', '2003-12-09 00:00:00.00'),
       
   998         ('003-12-9', '0003-12-09 00:00:00.00'),
       
   999         ('0003-12-9', '0003-12-09 00:00:00.00'),
       
  1000         ('2003-1-9', '2003-01-09 00:00:00.00'),
       
  1001         ('03-1-9', '2003-01-09 00:00:00.00'),
       
  1002         ('003-1-9', '0003-01-09 00:00:00.00'),
       
  1003         ('0003-1-9', '0003-01-09 00:00:00.00'),
       
  1004 
       
  1005         # US formats
       
  1006         ('06/11/94 08:49:37', '1994-06-11 08:49:37.00'),
       
  1007         ('11/06/94 08:49:37', '1994-11-06 08:49:37.00'),
       
  1008         ('9/23/2001', '2001-09-23 00:00:00.00'),
       
  1009         ('9-23-2001', '2001-09-23 00:00:00.00'),
       
  1010         ('9/6', '%s-09-06 00:00:00.00' % t.year),
       
  1011         ('09/6', '%s-09-06 00:00:00.00' % t.year),
       
  1012         ('9/06', '%s-09-06 00:00:00.00' % t.year),
       
  1013         ('09/06', '%s-09-06 00:00:00.00' % t.year),
       
  1014         ('9/6/2001', '2001-09-06 00:00:00.00'),
       
  1015         ('09/6/2001', '2001-09-06 00:00:00.00'),
       
  1016         ('9/06/2001', '2001-09-06 00:00:00.00'),
       
  1017         ('09/06/2001', '2001-09-06 00:00:00.00'),
       
  1018         ('9-6-2001', '2001-09-06 00:00:00.00'),
       
  1019         ('09-6-2001', '2001-09-06 00:00:00.00'),
       
  1020         ('9-06-2001', '2001-09-06 00:00:00.00'),
       
  1021         ('09-06-2001', '2001-09-06 00:00:00.00'),
       
  1022         ('2002/05/28 13:10:56.1147 GMT+2', '2002-05-28 13:10:56.11'),
       
  1023         ('1970/01/01', '1970-01-01 00:00:00.00'),
       
  1024         ('20021025 12:00 PM', '2002-10-25 12:00:00.00'),
       
  1025         ('20021025 12:30 PM', '2002-10-25 12:30:00.00'),
       
  1026         ('20021025 12:00 AM', '2002-10-25 00:00:00.00'),
       
  1027         ('20021025 12:30 AM', '2002-10-25 00:30:00.00'),
       
  1028         ('20021025 1:00 PM', '2002-10-25 13:00:00.00'),
       
  1029         ('20021025 2:00 AM', '2002-10-25 02:00:00.00'),
       
  1030         ('Thursday, February 06, 2003 12:40 PM', '2003-02-06 12:40:00.00'),
       
  1031         ('Mon, 18 Sep 2006 23:03:00', '2006-09-18 23:03:00.00'),
       
  1032 
       
  1033         # European formats
       
  1034         ('6.11.2001, 08:49:37', '2001-11-06 08:49:37.00'),
       
  1035         ('06.11.2001, 08:49:37', '2001-11-06 08:49:37.00'),
       
  1036         ('06.11. 08:49:37', '%s-11-06 08:49:37.00' % t.year),
       
  1037         #('21/12/2002', '2002-12-21 00:00:00.00'),
       
  1038         #('21/08/2002', '2002-08-21 00:00:00.00'),
       
  1039         #('21-08-2002', '2002-08-21 00:00:00.00'),
       
  1040         #('13/01/03', '2003-01-13 00:00:00.00'),
       
  1041         #('13/1/03', '2003-01-13 00:00:00.00'),
       
  1042         #('13/1/3', '2003-01-13 00:00:00.00'),
       
  1043         #('13/01/3', '2003-01-13 00:00:00.00'),
       
  1044 
       
  1045         # Time only formats
       
  1046         ('01:03', '%s 01:03:00.00' % t.date),
       
  1047         ('01:03:11', '%s 01:03:11.00' % t.date),
       
  1048         ('01:03:11.50', '%s 01:03:11.50' % t.date),
       
  1049         ('01:03:11.50 AM', '%s 01:03:11.50' % t.date),
       
  1050         ('01:03:11.50 PM', '%s 13:03:11.50' % t.date),
       
  1051         ('01:03:11.50 a.m.', '%s 01:03:11.50' % t.date),
       
  1052         ('01:03:11.50 p.m.', '%s 13:03:11.50' % t.date),
       
  1053 
       
  1054         # Invalid formats
       
  1055         ('6..2001, 08:49:37', '%s 08:49:37.00' % t.date),
       
  1056         ('9//2001', 'ignore'),
       
  1057         ('06--94 08:49:37', 'ignore'),
       
  1058         ('20000824020301', 'ignore'),
       
  1059         ('20-03 00:00:00.00', 'ignore'),
       
  1060         ('9/2001', 'ignore'),
       
  1061         ('9-6', 'ignore'),
       
  1062         ('09-6', 'ignore'),
       
  1063         ('9-06', 'ignore'),
       
  1064         ('09-06', 'ignore'),
       
  1065         ('20000824/23', 'ignore'),
       
  1066         ('November 1994 08:49:37', 'ignore'),
       
  1067         ('Nov. 94', 'ignore'),
       
  1068         ('Mon, 18 Sep 2006 23:03:00 +1234567890', 'ignore'),
       
  1069 
       
  1070         ]
       
  1071 
       
  1072     # Add Unicode versions
       
  1073     try:
       
  1074         unicode
       
  1075     except NameError:
       
  1076         pass
       
  1077     else:
       
  1078         k = []
       
  1079         for text, result in l:
       
  1080             k.append((unicode(text), result))
       
  1081         l.extend(k)
       
  1082 
       
  1083     for text, reference in l:
       
  1084         try:
       
  1085             value = DateTimeFromString(text)
       
  1086         except:
       
  1087             if reference is None:
       
  1088                 continue
       
  1089             else:
       
  1090                 value = str(sys.exc_info()[1])
       
  1091         valid_datetime = validateDateTimeString(text)
       
  1092         valid_date = validateDateString(text)
       
  1093         if str(value) != reference and \
       
  1094            not reference == 'ignore':
       
  1095             print 'Failed to parse "%s"' % text
       
  1096             print '  expected: %s' % (reference or '<exception>')
       
  1097             print '  parsed:   %s' % value
       
  1098         elif _debug:
       
  1099             print 'Parsed "%s" successfully' % text
       
  1100         if _debug:
       
  1101             if not valid_datetime:
       
  1102                 print '  "%s" failed date/time validation' % text
       
  1103             if not valid_date:
       
  1104                 print '  "%s" failed date validation' % text
       
  1105 
       
  1106     et = DateTime.now()
       
  1107     print 'done. (after %f seconds)' % ((et-t).seconds)
       
  1108 
       
  1109     ###
       
  1110 
       
  1111     print 'Testing DateTimeDelta Parser...'
       
  1112 
       
  1113     t = DateTime.now()
       
  1114     l = [
       
  1115 
       
  1116         # Literal formats
       
  1117         ('Sun Nov  6 08:49:37 1994', '08:49:37.00'),
       
  1118         ('1 day, 8 hours, 49 minutes, 37 seconds', '1:08:49:37.00'),
       
  1119         ('10 days, 8 hours, 49 minutes, 37 seconds', '10:08:49:37.00'),
       
  1120         ('8 hours, 49 minutes, 37 seconds', '08:49:37.00'),
       
  1121         ('49 minutes, 37 seconds', '00:49:37.00'),
       
  1122         ('37 seconds', '00:00:37.00'),
       
  1123         ('37.5 seconds', '00:00:37.50'),
       
  1124         ('8 hours later', '08:00:00.00'),
       
  1125         ('2 days', '2:00:00:00.00'),
       
  1126         ('2 days 23h', '2:23:00:00.00'),
       
  1127         ('2 days 23:57', '2:23:57:00.00'),
       
  1128         ('2 days 23:57:13', '2:23:57:13.00'),
       
  1129         ('', '00:00:00.00'),
       
  1130         
       
  1131         # ISO formats
       
  1132         ('1994-11-06 08:49:37', '08:49:37.00'),
       
  1133         ('10:08:49:37', '10:08:49:37.00'),
       
  1134         ('08:49:37', '08:49:37.00'),
       
  1135         ('08:49', '08:49:00.00'),
       
  1136         ('-10:08:49:37', '-10:08:49:37.00'),
       
  1137         ('-08:49:37', '-08:49:37.00'),
       
  1138         ('-08:49', '-08:49:00.00'),
       
  1139         ('- 10:08:49:37', '-10:08:49:37.00'),
       
  1140         ('- 08:49:37', '-08:49:37.00'),
       
  1141         ('- 08:49', '-08:49:00.00'),
       
  1142         ('10:08:49:37.5', '10:08:49:37.50'),
       
  1143         ('08:49:37.5', '08:49:37.50'),
       
  1144         ('10:8:49:37', '10:08:49:37.00'),
       
  1145         ('8:9:37', '08:09:37.00'),
       
  1146         ('8:9', '08:09:00.00'),
       
  1147         ('8', '00:00:00.00'),
       
  1148 
       
  1149         # Invalid formats
       
  1150         #('', None),
       
  1151         #('8', None),
       
  1152 
       
  1153         ]
       
  1154 
       
  1155     for text, reference in l:
       
  1156         try:
       
  1157             value = DateTimeDeltaFromString(text)
       
  1158         except:
       
  1159             if reference is None:
       
  1160                 continue
       
  1161             else:
       
  1162                 value = str(sys.exc_info()[1])
       
  1163         if str(value) != reference and \
       
  1164            not reference == 'ignore':
       
  1165             print 'Failed to parse "%s"' % text
       
  1166             print '  expected: %s' % (reference or '<exception>')
       
  1167             print '  parsed:   %s' % value
       
  1168         elif _debug:
       
  1169             print 'Parsed "%s" successfully' % text
       
  1170 
       
  1171     et = DateTime.now()
       
  1172     print 'done. (after %f seconds)' % ((et-t).seconds)
       
  1173 
       
  1174     ###
       
  1175 
       
  1176     print 'Testing Time Parser...'
       
  1177 
       
  1178     t = DateTime.now()
       
  1179     l = [
       
  1180 
       
  1181         # Standard formats
       
  1182         ('08:49:37 AM', '08:49:37.00'),
       
  1183         ('08:49:37 PM', '20:49:37.00'),
       
  1184         ('12:00:00 AM', '00:00:00.00'),
       
  1185         ('12:00:00 PM', '12:00:00.00'),
       
  1186         ('8:09:37', '08:09:37.00'),
       
  1187         ('8:09', '08:09:00.00'),
       
  1188         
       
  1189         # ISO formats
       
  1190         ('08:49:37', '08:49:37.00'),
       
  1191         ('08:49', '08:49:00.00'),
       
  1192         ('08:49:37.5', '08:49:37.50'),
       
  1193         ('08:49:37,5', '08:49:37.50'),
       
  1194         ('08:09', '08:09:00.00'),
       
  1195 
       
  1196         # Invalid formats
       
  1197         ('', None),
       
  1198         ('8:9:37', 'XXX Should give an exception'),
       
  1199         ('08:9:37', 'XXX Should give an exception'),
       
  1200         ('8:9', None),
       
  1201         ('8', None),
       
  1202 
       
  1203         ]
       
  1204 
       
  1205     for text, reference in l:
       
  1206         try:
       
  1207             value = TimeFromString(text, formats=('standard', 'iso'))
       
  1208         except:
       
  1209             if reference is None:
       
  1210                 continue
       
  1211             else:
       
  1212                 value = str(sys.exc_info()[1])
       
  1213         if str(value) != reference and \
       
  1214            not reference == 'ignore':
       
  1215             print 'Failed to parse "%s"' % text
       
  1216             print '  expected: %s' % (reference or '<exception>')
       
  1217             print '  parsed:   %s' % value
       
  1218         elif _debug:
       
  1219             print 'Parsed "%s" successfully' % text
       
  1220 
       
  1221     et = DateTime.now()
       
  1222     print 'done. (after %f seconds)' % ((et-t).seconds)
       
  1223 
       
  1224 if __name__ == '__main__':
       
  1225     _test()