home *** CD-ROM | disk | FTP | other *** search
/ Enter 2004 April / enter-2004-04.iso / files / EVE_1424_100181.exe / _HTTPDate.py < prev    next >
Encoding:
Python Source  |  2004-04-20  |  12.2 KB  |  379 lines

  1. """_HTTPDate - date/time conversion routines.
  2.  
  3. from ClientCookie_HTTPDate import time2str, str2time
  4.  
  5.  string = time2str(time)  # Format as GMT ASCII time
  6.  time = str2time(string)  # convert ASCII date to machine time
  7.  
  8. This module provides functions that deal the with date/time formats used by the
  9. HTTP protocol (and then some more).
  10.  
  11. Copyright 1995-1999, Gisle Aas
  12. Copyright 2002 John J Lee <jjl@pobox.com> (The Python port)
  13.  
  14. This code is free software; you can redistribute it and/or modify it under
  15. the terms of the MIT License (see the file COPYING included with the
  16. distribution).
  17.  
  18. """
  19.  
  20. # from Gisle Aas's CVS revision 1.43, libwww-perl 5.64
  21.  
  22. import re, string
  23. from time import time, gmtime, localtime
  24. from _Util import timegm
  25. _timegm = timegm
  26. del timegm
  27.  
  28. EPOCH = 1970
  29. def timegm(tt):
  30.     year, month, mday, hour, min, sec = tt[:6]
  31.     if ((year >= EPOCH) and (1 <= month <= 12) and (1 <= mday <= 31) and
  32.         (0 <= hour <= 24) and (0 <= min <= 59) and (0 <= sec <= 61)):
  33.         return _timegm(tt)
  34.     else:
  35.         return None
  36.  
  37. days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
  38. months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
  39.           "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
  40. months_lower = []
  41. for month in months: months_lower.append(string.lower(month))
  42.  
  43.  
  44. GMT_ZONE = {"GMT": 1, "UTC": 1, "UT": 1, "Z": 1}
  45.  
  46.  
  47. def tz_offset(tz):
  48.     return None
  49.  
  50. # XXX this is utterly broken, what on earth was I thinking of??
  51. ## try:
  52. ##     from mx import DateTime
  53. ## except ImportError:
  54. ##     def tz_offset(tz):
  55. ##         return None
  56. ## else:
  57. ##     def tz_offset(tz):
  58. ##         return mxDateTime.DateTimeFromString("2002-04-01 00:00:00 "+tz)
  59.  
  60.  
  61. def time2str(t=None):
  62.     """Return a string representing time in seconds since epoch, t.
  63.  
  64.     If the function is called without an argument, it will use the current
  65.     time.
  66.  
  67.     The string returned is in the format preferred for the HTTP protocol.  This
  68.     is a fixed length subset of the format defined by RFC 1123, represented in
  69.     Universal Time (GMT, aka UTC).  An example of a time stamp in this format
  70.     is:
  71.  
  72.        Sun, 06 Nov 1994 08:49:37 GMT
  73.  
  74.     """
  75.     if t is None:
  76.         t = time()
  77.     year, month, day, hour, min, sec, wkday, julian_day, dst = gmtime(t)
  78.     return "%s, %02d %s %04d %02d:%02d:%02d GMT" % (
  79.         days[wkday], day, months[month-1], year, hour, min, sec)
  80.  
  81. def offset_from_tz_string(tz):
  82.     if GMT_ZONE.has_key(tz):
  83.         offset = 0
  84.     else:
  85.         m = timezone_re.search(tz)
  86.         if m:
  87.             offset = 3600 * int(m.group(2))
  88.             if m.group(3):
  89.                 offset = offset + 60 * int(m.group(3))
  90.             if m.group(1) == '-':
  91.                 offset = -offset
  92.         else:
  93.             offset = tz_offset(string.upper(string.strip(tz)))
  94.     return offset
  95.  
  96. timezone_re = re.compile(r"^([-+])?(\d\d?):?(\d\d)?$")
  97. strict_re = re.compile(r"^[SMTWF][a-z][a-z], (\d\d) ([JFMASOND][a-z][a-z]) (\d\d\d\d) (\d\d):(\d\d):(\d\d) GMT$")
  98. def str2time(str, tz=None):
  99.     """Returns time in seconds since epoch of time represented by a string.
  100.  
  101.     None is returned if the format of str is unrecognized, or the time is
  102.     outside the representable range.  The time formats recognized are the same
  103.     as for parse_date.
  104.  
  105.     The function also takes an optional second argument that specifies the
  106.     default time zone to use when converting the date.  This parameter is
  107.     ignored if the zone is found in the date string itself.  If this parameter
  108.     is missing, and the date string format does not contain any zone
  109.     specification, then the local time zone is assumed.
  110.  
  111.     If the zone is not UTC or numerical (like "-0800" or "+0100"), then
  112.     mxDateTime must be installed in order to get the date recognized.
  113.  
  114.     """
  115.     # fast exit for strictly conforming string
  116.     m = strict_re.search(str)
  117.     if m:
  118.         g = m.groups()
  119.         mon = months_lower.index(string.lower(g[1])) + 1
  120.         tt = (int(g[2]), mon, int(g[0]),
  121.               int(g[3]), int(g[4]), float(g[5]))
  122.         return timegm(tt)
  123.  
  124.     d = parse_date(str);
  125.     if d is None:
  126.         return None
  127.     #d[1] = d[1] - 1  # month
  128.  
  129.     tz = d.pop()
  130.     if tz is None: tz = "UTC"
  131.     tz = string.upper(tz)
  132.  
  133.     t = timegm(d)
  134.     if t is not None:
  135.         t = t - offset_from_tz_string(tz)
  136.     return t
  137.  
  138.  
  139. wkday_re = re.compile(
  140.     r"^(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)[a-z]*,?\s*", re.I)
  141. general_re = re.compile(
  142.     r"""^
  143.     (\d\d?)            # day
  144.        (?:\s+|[-\/])
  145.     (\w+)              # month
  146.         (?:\s+|[-\/])
  147.     (\d+)              # year
  148.     (?:
  149.       (?:\s+|:)    # separator before clock
  150.        (\d\d?):(\d\d)  # hour:min
  151.        (?::(\d\d))?    # optional seconds
  152.     )?                 # optional clock
  153.        \s*
  154.     ([-+]?\d{2,4}|(?![APap][Mm]\b)[A-Za-z]+)? # timezone
  155.        \s*
  156.     (?:\(\w+\))?       # ASCII representation of timezone in parens.
  157.        \s*$""", re.X)
  158. ctime_re = re.compile(
  159.     r"""^
  160.     (\w{1,3})             # month
  161.        \s+
  162.     (\d\d?)               # day
  163.        \s+
  164.     (\d\d?):(\d\d)        # hour:min
  165.     (?::(\d\d))?          # optional seconds
  166.        \s+
  167.     (?:([A-Za-z]+)\s+)?   # optional timezone
  168.     (\d+)                 # year
  169.        \s*$               # allow trailing whitespace
  170.     """, re.X)
  171. ls_l_re = re.compile(
  172.     """^
  173.     (\w{3})               # month
  174.        \s+
  175.     (\d\d?)               # day
  176.        \s+
  177.     (?:
  178.        (\d\d\d\d) |       # year
  179.        (\d{1,2}):(\d{2})  # hour:min
  180.        (?::(\d\d))?       # optional seconds
  181.     )
  182.     \s*$""", re.X)
  183. iso_re = re.compile(
  184.     """^
  185.     (\d{4})              # year
  186.        [-\/]?
  187.     (\d\d?)              # numerical month
  188.        [-\/]?
  189.     (\d\d?)              # day
  190.    (?:
  191.          (?:\s+|[-:Tt])  # separator before clock
  192.       (\d\d?):?(\d\d)    # hour:min
  193.       (?::?(\d\d(?:\.\d*)?))?  # optional seconds (and fractional)
  194.    )?                    # optional clock
  195.       \s*
  196.    ([-+]?\d\d?:?(:?\d\d)?
  197.     |Z|z)?               # timezone  (Z is "zero meridian", i.e. GMT)
  198.       \s*$""", re.X)
  199. windows_re = re.compile(
  200.     """^
  201.     (\d{2})                # numerical month
  202.        -
  203.     (\d{2})                # day
  204.        -
  205.     (\d{2})                # year
  206.        \s+
  207.     (\d\d?):(\d\d)([APap][Mm])  # hour:min AM or PM
  208.        \s*$""", re.X)
  209.  
  210. tz_re = re.compile(r"^(GMT|UTC?|[-+]?0+)$")
  211.  
  212. def parse_date(text):
  213.     """
  214.     This function will try to parse a date string, and then return it as a list
  215.     of numerical values followed by a (possibly None) time zone specifier;
  216.     [year, month, day, hour, min, sec, tz).  The year returned will not have
  217.     the number 1900 subtracted from it and the month numbers start with 1.
  218.  
  219.     In scalar context the numbers are interpolated in a string of the
  220.     "YYYY-MM-DD hh:mm:ss TZ"-format and returned.
  221.  
  222.     If the date is unrecognized, then the empty list is returned.
  223.  
  224.     The function is able to parse the following formats:
  225.  
  226.      "Wed, 09 Feb 1994 22:23:32 GMT"       -- HTTP format
  227.      "Thu Feb  3 17:03:55 GMT 1994"        -- ctime(3) format
  228.      "Thu Feb  3 00:00:00 1994",           -- ANSI C asctime() format
  229.      "Tuesday, 08-Feb-94 14:15:29 GMT"     -- old rfc850 HTTP format
  230.      "Tuesday, 08-Feb-1994 14:15:29 GMT"   -- broken rfc850 HTTP format
  231.  
  232.      "03/Feb/1994:17:03:55 -0700"   -- common logfile format
  233.      "09 Feb 1994 22:23:32 GMT"     -- HTTP format (no weekday)
  234.      "08-Feb-94 14:15:29 GMT"       -- rfc850 format (no weekday)
  235.      "08-Feb-1994 14:15:29 GMT"     -- broken rfc850 format (no weekday)
  236.  
  237.      "1994-02-03 14:15:29 -0100"    -- ISO 8601 format
  238.      "1994-02-03 14:15:29"          -- zone is optional
  239.      "1994-02-03"                   -- only date
  240.      "1994-02-03T14:15:29"          -- Use T as separator
  241.      "19940203T141529Z"             -- ISO 8601 compact format
  242.      "19940203"                     -- only date
  243.  
  244.      "08-Feb-94"         -- old rfc850 HTTP format    (no weekday, no time)
  245.      "08-Feb-1994"       -- broken rfc850 HTTP format (no weekday, no time)
  246.      "09 Feb 1994"       -- proposed new HTTP format  (no weekday, no time)
  247.      "03/Feb/1994"       -- common logfile format     (no time, no offset)
  248.  
  249.      "Feb  3  1994"      -- Unix 'ls -l' format
  250.      "Feb  3 17:03"      -- Unix 'ls -l' format
  251.  
  252.      "11-15-96  03:52PM" -- Windows 'dir' format
  253.  
  254.     The parser ignores leading and trailing whitespace.  It also allow the seconds
  255.     to be missing and the month to be numerical in most formats.
  256.  
  257.     If the year is missing, then we assume that the date is the first matching
  258.     date *before* current month.  If the year is given with only 2 digits, then
  259.     parse_date will select the century that makes the year closest to the
  260.     current date.
  261.  
  262.     """
  263.     assert text is not None
  264.     # More lax parsing below
  265.     text = string.lstrip(text)
  266.     text = wkday_re.sub("", text, 1)  # Useless weekday
  267.  
  268.     day, mon, yr, hr, min, sec, tz, ampm = [None]*8
  269.  
  270.     failed = 1
  271.     # check for most of the formats with this regexp
  272.     m = general_re.search(text)
  273.     if m is not None:
  274.         failed = 0
  275.         day, mon, yr, hr, min, sec, tz = m.groups()
  276.     if failed:
  277.         # Try the ctime and asctime format
  278.         m = ctime_re.search(text)
  279.         if m is not None:
  280.             failed = 0
  281.             mon, day, hr, min, sec, tz, yr = m.groups()
  282.     if failed:
  283.         # Then the Unix 'ls -l' date format
  284.         m = ls_l_re.search(text)
  285.         if m is not None:
  286.             failed = 0
  287.             mon, day, yr, hr, min, sec = m.groups()
  288.     if failed:
  289.         # ISO 8601 format '1996-02-29 12:00:00 -0100' and variants
  290.         m = iso_re.search(text)
  291.         if m is not None:
  292.             failed = 0
  293.             # XXX there's an extra bit of the timezone I'm ignoring here: is
  294.             #   this the right thing to do?
  295.             yr, mon, day, hr, min, sec, tz, _ = m.groups()
  296.     if failed:
  297.         # Windows 'dir' 11-12-96  03:52PM
  298.         m = windows_re.search(text)
  299.         if m is not None:
  300.             failed = 0
  301.             mon, day, yr, hr, min, ampm = m.groups()
  302.     if failed:
  303.         return None  # unrecognized format
  304.  
  305.     # Translate month name to number
  306.     try:
  307.         mon = months_lower.index(string.lower(mon))+1
  308.     except ValueError:
  309.         #mon = months["\u\L%s" % (mon,)]
  310.         if 1:#not mon:
  311.             try:
  312.                 imon = int(mon)
  313.             except ValueError:
  314.                 return None
  315.             else:
  316.                 if (imon >= 1 and imon <= 12):
  317.                     mon = imon
  318.                 else:
  319.                     return None
  320.  
  321.     # Make sure clock elements are defined
  322.     if hr is None: hr = 0
  323.     if min is None: min = 0
  324.     if sec is None: sec = 0
  325.  
  326.     day = int(day)
  327.     hr = int(hr)
  328.     min = int(min)
  329.     sec = float(sec)
  330.  
  331.     # If the year is missing, we assume first date before the current,
  332.     # because, of the formats we support, such dates are mostly present
  333.     # on "ls -l" listings.
  334.     if yr is not None:
  335.         yr = int(yr)
  336.     if yr is None:
  337.         yr, cur_mon = localtime(time())[0:2]
  338.         if mon > cur_mon: yr = yr - 1
  339.     elif yr < 1000:
  340.     # Find "obvious" year
  341.     cur_yr = localtime(time())[0]
  342.     m = cur_yr % 100
  343.     tmp = yr
  344.     yr = yr + cur_yr - m
  345.     m = m - tmp
  346.         if abs(m) > 50:
  347.             if m > 0: yr = yr + 100
  348.             else: yr = yr - 100
  349.  
  350.     # Compensate for AM/PM
  351.     if ampm:
  352.     ampm = string.upper(ampm)
  353.         if hr == 12 and ampm == "AM":
  354.             hr = 0
  355.         if ampm == "PM" and hr != 12:
  356.             hr = hr + 12
  357.  
  358.     return [yr, mon, day, hr, min, sec, tz]
  359.  
  360. def time2iso(t=None):
  361.     """
  362.     Same as time2str(), but returns a "YYYY-MM-DD hh:mm:ss"-formatted string
  363.     representing time in the local time zone.
  364.     """
  365.     if t is None: t = time()
  366.     year, mon, mday, hour, min, sec = localtime(t)[:6]
  367.     return "%04d-%02d-%02d %02d:%02d:%02d" % (
  368.         year, mon, mday, hour, min, sec)
  369.  
  370. def time2isoz(t=None):
  371.     """
  372.     Same as time2str(), but returns a "YYYY-MM-DD hh:mm:ssZ"-formatted
  373.     string representing Universal Time.
  374.     """
  375.     if t is None: t = time()
  376.     year, mon, mday, hour, min, sec = gmtime(t)[:6]
  377.     return "%04d-%02d-%02d %02d:%02d:%02dZ" % (
  378.         year, mon, mday, hour, min, sec)
  379.