embedded/mx/DateTime/ARPA.py
changeset 0 b97547f5f1fa
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/embedded/mx/DateTime/ARPA.py	Wed Nov 05 15:52:50 2008 +0100
@@ -0,0 +1,227 @@
+""" This module provides a set of constructors and routines to convert
+    between DateTime[Delta] instances and ARPA representations of date
+    and time. The format is specified by RFC822 + RFC1123.
+
+    Note: Timezones are only interpreted by ParseDateTimeGMT(). All
+    other constructors silently ignore the time zone information.
+
+    Copyright (c) 1998-2000, Marc-Andre Lemburg; mailto:mal@lemburg.com
+    Copyright (c) 2000-2007, eGenix.com Software GmbH; mailto:info@egenix.com
+    See the documentation for further information on copyrights,
+    or contact the author. All Rights Reserved.
+
+"""
+import DateTime,Timezone
+import re,string
+
+# Grammar: RFC822 + RFC1123 + depreciated RFC850
+_litday = '(?P<litday>Mon|Tue|Wed|Thu|Fri|Sat|Sun)[a-z]*'
+_litmonth = '(?P<litmonth>Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)'\
+            '[a-z]*'
+_date = ('(?:(?P<day>\d?\d)(?: +' + _litmonth + 
+         ' +|-(?P<month>\d?\d)-)(?P<year>(?:\d\d)?\d\d))')
+_zone = Timezone.zone
+_time = ('(?:(?P<hour>\d\d):(?P<minute>\d\d)'
+         '(?::(?P<second>\d\d))?(?: +'+_zone+')?)')
+#       Timezone information is made optional because some mail apps
+#       forget to add it (most of these seem to be spamming engines, btw).
+#       It defaults to UTC.
+
+_arpadate = '(?:'+ _litday + ',? )? *' + _date
+_arpadatetime = '(?:'+ _litday + ',? )? *' + _date + ' +' + _time
+
+#       We are not strict about the extra characters: some applications
+#       add extra information to the date header field. Additional spaces
+#       between the fields and extra characters in the literal day
+#       and month fields are also silently ignored.
+
+arpadateRE = re.compile(_arpadate)
+arpadatetimeRE = re.compile(_arpadatetime)
+
+# Translation tables
+litdaytable = {'mon':0, 'tue':1, 'wed':2, 'thu':3, 'fri':4, 'sat':5, 'sun':6 }
+litmonthtable = {'jan':1, 'feb':2, 'mar':3, 'apr':4, 'may':5, 'jun':6,
+                 'jul':7, 'aug':8, 'sep':9, 'oct':10, 'nov':11, 'dec':12 }
+_days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
+_months = [None, 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
+                 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ]
+
+def ParseDate(arpastring,parse_arpadate=arpadateRE.match,
+
+              strip=string.strip,atoi=string.atoi,atof=string.atof,
+              lower=string.lower):
+
+    """ParseDate(arpastring)
+
+       Returns a DateTime instance reflecting the given ARPA
+       date. Only the date part is parsed, any time part will be
+       ignored. The instance's time is set to 0:00:00.
+
+    """
+    s = strip(arpastring)
+    date = parse_arpadate(s)
+    if not date:
+        raise ValueError,'wrong format'
+    litday,day,litmonth,month,year = date.groups()
+    if len(year) == 2:
+        year = DateTime.add_century(atoi(year))
+    else:
+        year = atoi(year)
+    if litmonth:
+        litmonth = lower(litmonth)
+        try:
+            month = litmonthtable[litmonth]
+        except KeyError:
+            raise ValueError,'wrong month format'
+    else:
+        month = atoi(month)
+    day = atoi(day)
+    # litday and timezone are ignored
+    return DateTime.DateTime(year,month,day)
+
+def ParseDateTime(arpastring,parse_arpadatetime=arpadatetimeRE.match,
+
+                  strip=string.strip,atoi=string.atoi,atof=string.atof,
+                  lower=string.lower):
+
+    """ParseDateTime(arpastring)
+
+       Returns a DateTime instance reflecting the given ARPA date assuming
+       it is local time (timezones are silently ignored).
+    """
+    s = strip(arpastring)
+    date = parse_arpadatetime(s)
+    if not date:
+        raise ValueError,'wrong format or unknown time zone'
+    litday,day,litmonth,month,year,hour,minute,second,zone = date.groups()
+    if len(year) == 2:
+        year = DateTime.add_century(atoi(year))
+    else:
+        year = atoi(year)
+    if litmonth:
+        litmonth = lower(litmonth)
+        try:
+            month = litmonthtable[litmonth]
+        except KeyError:
+            raise ValueError,'wrong month format'
+    else:
+        month = atoi(month)
+    day = atoi(day)
+    hour = atoi(hour)
+    minute = atoi(minute)
+    if second is None:
+        second = 0.0
+    else:
+        second = atof(second)
+    # litday and timezone are ignored
+    return DateTime.DateTime(year,month,day,hour,minute,second)
+
+def ParseDateTimeGMT(arpastring,parse_arpadatetime=arpadatetimeRE.match,
+
+                     strip=string.strip,atoi=string.atoi,atof=string.atof,
+                     lower=string.lower):
+
+    """ParseDateTimeGMT(arpastring)
+
+       Returns a DateTime instance reflecting the given ARPA date converting
+       it to UTC (timezones are honored).
+    """
+    s = strip(arpastring)
+    date = parse_arpadatetime(s)
+    if not date:
+        raise ValueError,'wrong format or unknown time zone'
+    litday,day,litmonth,month,year,hour,minute,second,zone = date.groups()
+    if len(year) == 2:
+        year = DateTime.add_century(atoi(year))
+    else:
+        year = atoi(year)
+    if litmonth:
+        litmonth = lower(litmonth)
+        try:
+            month = litmonthtable[litmonth]
+        except KeyError:
+            raise ValueError,'wrong month format'
+    else:
+        month = atoi(month)
+    day = atoi(day)
+    hour = atoi(hour)
+    minute = atoi(minute)
+    if second is None:
+        second = 0.0
+    else:
+        second = atof(second)
+    offset = Timezone.utc_offset(zone)
+    # litday is ignored
+    return DateTime.DateTime(year,month,day,hour,minute,second) - offset
+
+# Alias
+ParseDateTimeUTC = ParseDateTimeGMT
+
+def str(datetime,tz=None):
+
+    """str(datetime,tz=DateTime.tz_offset(datetime))
+
+    Returns the datetime instance as ARPA date string. tz can be given
+    as DateTimeDelta instance providing the time zone difference from
+    datetime's zone to UTC. It defaults to
+    DateTime.tz_offset(datetime) which assumes local time. """
+
+    if tz is None:
+        tz = datetime.gmtoffset()
+    return '%s, %02i %s %04i %02i:%02i:%02i %+03i%02i' % (
+        _days[datetime.day_of_week], datetime.day, 
+        _months[datetime.month], datetime.year,
+        datetime.hour, datetime.minute, datetime.second,
+        tz.hour,tz.minute)
+
+def strGMT(datetime):
+
+    """ strGMT(datetime)
+
+    Returns the datetime instance as ARPA date string assuming it
+    is given in GMT. """
+
+    return '%s, %02i %s %04i %02i:%02i:%02i GMT' % (
+        _days[datetime.day_of_week], datetime.day, 
+        _months[datetime.month], datetime.year,
+        datetime.hour, datetime.minute, datetime.second)
+
+def strUTC(datetime):
+
+    """ strUTC(datetime)
+
+    Returns the datetime instance as ARPA date string assuming it
+    is given in UTC. """
+
+    return '%s, %02i %s %04i %02i:%02i:%02i UTC' % (
+        _days[datetime.day_of_week], datetime.day, 
+        _months[datetime.month], datetime.year,
+        datetime.hour, datetime.minute, datetime.second)
+
+def _test():
+    import sys, os, rfc822
+    file = os.path.join(os.environ['HOME'], 'nsmail/Inbox')
+    f = open(file, 'r')
+    while 1:
+        m = rfc822.Message(f)
+        if not m:
+            break
+        print 'From:', m.getaddr('from')
+        print 'To:', m.getaddrlist('to')
+        print 'Subject:', m.getheader('subject')
+        raw = m.getheader('date')
+        try:
+            date = ParseDateTimeUTC(raw)
+            print 'Date:',strUTC(date)
+        except ValueError,why:
+            print 'PROBLEMS:',repr(raw),'-->',why
+            raw_input('...hit return to continue')
+        print
+        # Netscape mail file
+        while 1:
+            line = f.readline()
+            if line[:6] == 'From -':
+                break
+
+if __name__ == '__main__':
+    _test()