commit-gnue
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[gnue] r9884 - in trunk/gnue-common/src: . lib utils


From: reinhard
Subject: [gnue] r9884 - in trunk/gnue-common/src: . lib utils
Date: Thu, 10 Jul 2008 15:00:43 -0500 (CDT)

Author: reinhard
Date: 2008-07-10 15:00:43 -0500 (Thu, 10 Jul 2008)
New Revision: 9884

Added:
   trunk/gnue-common/src/lib/
   trunk/gnue-common/src/lib/__init__.py
   trunk/gnue-common/src/lib/iso8601.py
Modified:
   trunk/gnue-common/src/utils/GDateTime.py
Log:
Added new "lib" package for code not specific to GNUe, and moved a cleaned-up 
version of the ISO date/time parsing 
routines there.



Property changes on: trunk/gnue-common/src/lib
___________________________________________________________________
Name: svn:ignore
   + *.pyc


Added: trunk/gnue-common/src/lib/__init__.py
===================================================================
--- trunk/gnue-common/src/lib/__init__.py                               (rev 0)
+++ trunk/gnue-common/src/lib/__init__.py       2008-07-10 20:00:43 UTC (rev 
9884)
@@ -0,0 +1,31 @@
+# GNU Enterprise Common Library - Gereral Library
+#
+# Copyright 2001-2008 Free Software Foundation
+#
+# This file is part of GNU Enterprise
+#
+# GNU Enterprise is free software; you can redistribute it
+# and/or modify it under the terms of the GNU General Public
+# License as published by the Free Software Foundation; either
+# version 2, or (at your option) any later version.
+#
+# GNU Enterprise is distributed in the hope that it will be
+# useful, but WITHOUT ANY WARRANTY; without even the implied
+# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+# PURPOSE. See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public
+# License along with program; see the file COPYING. If not,
+# write to the Free Software Foundation, Inc., 59 Temple Place
+# - Suite 330, Boston, MA 02111-1307, USA.
+#
+# $Id: __init__.py 9850 2008-01-03 17:21:25Z jcater $
+
+"""
+The lib package contains a number of modules providing very basic functions
+also usable independently of GNU Enterprise.
+
+None of the modules depends on any other module or on any aspect of the GNU
+Enterprise infrastructure. Each of them could be seen as a potential extension
+of the Python standard library.
+"""

Copied: trunk/gnue-common/src/lib/iso8601.py (from rev 9881, 
trunk/gnue-common/src/utils/GDateTime.py)
===================================================================
--- trunk/gnue-common/src/lib/iso8601.py                                (rev 0)
+++ trunk/gnue-common/src/lib/iso8601.py        2008-07-10 20:00:43 UTC (rev 
9884)
@@ -0,0 +1,508 @@
+# GNU Enterprise Common Library - ISO 8601 Parser
+#
+# Copyright 2001-2008 Free Software Foundation
+#
+# This file is part of GNU Enterprise
+#
+# GNU Enterprise is free software; you can redistribute it
+# and/or modify it under the terms of the GNU General Public
+# License as published by the Free Software Foundation; either
+# version 2, or (at your option) any later version.
+#
+# GNU Enterprise is distributed in the hope that it will be
+# useful, but WITHOUT ANY WARRANTY; without even the implied
+# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+# PURPOSE. See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public
+# License along with program; see the file COPYING. If not,
+# write to the Free Software Foundation, Inc., 59 Temple Place
+# - Suite 330, Boston, MA 02111-1307, USA.
+#
+# $Id$
+
+"""
+Functions to parse ISO 8601 conformant date and time strings.
+
+This module extends the standard Python datetime module.
+"""
+
+import calendar
+import datetime
+import re
+
+__all__ = ['parse_iso', 'date_parse_iso', 'time_parse_iso', 'date_fromisoweek',
+        'date_fromdaynumber']
+
+
+# =============================================================================
+# Implementation of tzinfo with a fixed offset to UTC
+# =============================================================================
+
+class Timezone(datetime.tzinfo):
+    """
+    Timezone defined through an offset from UTC.
+
+    This class implements the abstract datetime.tzinfo class for a timezone
+    defined through a fixed offset to UTC.  Instances of this class yield a DST
+    difference of 0, and a name like "-02:00".
+    """
+
+    # -------------------------------------------------------------------------
+    # Constructor
+    # -------------------------------------------------------------------------
+
+    def __init__(self, offset = 0):
+
+        datetime.tzinfo.__init__(self)
+        self.__offset = datetime.timedelta(minutes = offset)
+
+
+    # -------------------------------------------------------------------------
+    # Return the offset from UTC
+    # -------------------------------------------------------------------------
+
+    def utcoffset(self, dt):
+        """
+        Implementation of the abstract "utcoffset" method of the base class.
+
+        Simply returns the offset given at initialization.
+        """
+
+        return self.__offset
+
+
+    # -------------------------------------------------------------------------
+    # Return the name of the timezone
+    # -------------------------------------------------------------------------
+
+    def tzname(self, dt):
+        """
+        Implementation of the abstract "tzname" method of the base class.
+
+        This implementation returns a name based on the UTC offset like
+        "+0200".
+        """
+
+        if self.__offset:
+            minutes = self.__offset.seconds / 60 + self.__offset.days * 1440
+            if minutes > 0:
+                mm = minutes % 60
+                hh = (minutes - mm) / 60
+            else:
+                mm = -minutes % 60
+                hh = -((-minutes - mm) / 60)
+            return "%+03d:%02d" % (hh, mm)
+        else:
+            return "Z"
+
+
+    # -------------------------------------------------------------------------
+    # Return the daylight saving time adjustment
+    # -------------------------------------------------------------------------
+
+    def dst(self, dt):
+        """
+        Implementation of the abstract "dst" method of the base class.
+
+        This implementation returns a zero DST offset.
+        """
+
+        return datetime.timedelta(0)
+
+
+# =============================================================================
+# Parsing routines
+# =============================================================================
+
+# -----------------------------------------------------------------------------
+# Parse an arbitary ISO 8601 conformant date/time representation
+# -----------------------------------------------------------------------------
+
+def parse_iso(isostring):
+    """
+    Parse a string containing any ISO 8601 conformant representation of a date
+    and/or time.
+
+    @param isostring: string to be parsed
+    @type isostring: string
+    @return: value of the date and/or time
+    @rtype: datetime.date or datetime.time or datetime.datetime
+
+    @raises ValueError: The datestring parameter is no ISO 8601 conformant
+        representation of a valid date and/or time.
+    """
+
+    s = isostring.strip()
+    match = re.match('^(.*?)[ T](.*)$', s)
+    if match is not None:
+        (datepart, timepart) = match.groups()
+        date = date_parse_iso(datepart)
+        time = time_parse_iso(timepart)
+        return datetime.datetime.combine(date, time)
+    else:
+        # This doesn't look very elegant, but it probably is the most
+        # performant way to find out whether we have a date or a time.
+        try:
+            return time_parse_iso(s)
+        except ValueError:
+            return date_parse_iso(s)
+
+
+# -----------------------------------------------------------------------------
+# Parse an ISO 8601 conformant date representation
+# -----------------------------------------------------------------------------
+
+def date_parse_iso(datestring):
+    """
+    Parse a string containing any ISO 8601 conformant representation of a date.
+
+    Basic formats:
+        * yyyymmdd (year, month, day)
+        * yyyymm (assumes first day of the month)
+        * yyyyWwwd (year, week number, day of week)
+        * yyyyWww (assumes first day of the week, that is Monday)
+        * yyyyddd (year, ordinal day in year)
+
+    Extended formats:
+        * yyyy-mm-dd (year, month, day)
+        * yyyy-mm (assumes first day of the month)
+        * yyyy-Www-d (year, week number, day of week)
+        * yyyy-Www (assumes first day of the week, that is Monday)
+        * yyyy-ddd (year, ordinal day in year)
+
+    The extended formats tolerate missing leading zeroes.
+
+    @param datestring: string to be parsed
+    @type datestring: string
+    @return: value of the date
+    @rtype: datetime.date
+
+    @raises ValueError: The datestring parameter is no ISO 8601 conformant
+        representation of a valid date.
+    """
+
+    # This could certainly be done with regular expressions, too, but doing a
+    # re.match().groups() takes around 5 times more time than doing a split()
+    if "-" in datestring:
+        parts = datestring.split('-')
+
+        if len(parts) > 3:
+            raise ValueError("invalid date format: %s" % datestring)
+
+        year = int(parts[0])
+
+        if parts[1][0] == 'W':
+            week = int(parts[1][1:])
+            if len(parts) == 3:
+                # yyyy-Www-d
+                day = int(parts[2])
+            else:
+                # yyyy-Www
+                day = 1
+            return date_fromisoweek(year, week, day)
+
+        if len(parts) == 2 and len(parts[1]) == 3:
+            # yyyy-ddd
+            return date_fromdaynumber(year, int(parts[1]))
+
+        month = int(parts[1])
+        if len(parts) == 3:
+            # yyyy-mm-dd
+            day = int(parts[2])
+        else:
+            # yyyy-mm
+            day = 1
+
+        return datetime.date(year, month, day)
+
+    if datestring[4] == 'W':
+        if len(datestring) < 7 or len(datestring) > 8:
+            raise ValueError("invalid date format: %s" % datestring)
+
+        year = int(datestring[:4])
+        week = int(datestring[5:7])
+
+        if len(datestring) == 8:
+            # yyyyWwwd
+            day = int(datestring[7])
+        else:
+            # yyyyWww
+            day = 1
+
+        return date_fromisoweek(year, week, day)
+
+    if len(datestring) == 6:
+        # yyyymm
+        return datetime.date(int(datestring[:4]), int(datestring[4:6]), 1)
+
+    if len(datestring) == 7:
+        # yyyyddd
+        return date_fromdaynumber(int(datestring[:4]), int(datestring[4:7]))
+
+    if len(datestring) == 8:
+        # yyyymmdd
+        return datetime.date(int(datestring[:4]), int(datestring[4:6]),
+            int(datestring[6:8]))
+
+    raise ValueError("invalid date format: %s" % datestring)
+
+
+# -----------------------------------------------------------------------------
+# Parse an ISO 8601 conformant time representation
+# -----------------------------------------------------------------------------
+
+def time_parse_iso(timestring):
+    """
+    Parse a string containing any ISO 8601 conformant representation of a time.
+
+    Basic formats:
+        * hhmm (hour, minute)
+        * hhmmss (hour, minute, seconds)
+        * hhmmss.fff (arbitary number of fractional digits)
+        * hhmmss,fff (arbitary number of fractional digits)
+
+    Extended formats:
+        * hh:mm (hour, minute)
+        * hh:mm:ss (hour, minute, seconds)
+        * hh:mm:ss.fff (arbitary number of fractional digits)
+        * hh:mm:ss,fff (arbitary number of fractional digits)
+
+    The extended formats tolerate missing leading zeroes.
+
+    To each of the formats, one of the following timezone indicators can be
+    appended:
+        * Z (for UTC)
+        * +hh or -hh
+        * +hhmm or -hhmm
+        * +hh:mm or -hh:mm
+
+    If a timezone indicator is used, the function yields a timezone aware time
+    object, otherwise a naive time object.
+
+    @param timestring: string to be parsed
+    @type timestring: string
+    @return: value of the time
+    @rtype: datetime.time
+
+    @raises ValueError: The timestring parameter is no ISO 8601 conformant
+        representation of a valid time.
+    """
+
+    # Split into time and timezone.
+    match = re.match('^(.*?)(?:([Z+-])(.*)){0,1}$', timestring)
+    if match is None:
+        raise ValueError("invalid time format: %s" % timestring)
+    (timepart, zone, offset) = match.groups()
+
+    # Part 1: parse time without timezone
+
+    if ':' in timepart:
+        parts = timepart.split(':')
+
+        if len(parts) > 3:
+            raise ValueError("invalid time format: %s" % timestring)
+
+        hour = int(parts[0])
+        minute = int(parts[1])
+
+        if len(parts) == 3:
+            # hh:mm:ss or hh:mm:ss.ffffff or hh:mm:ss,ffffff
+            (second, micro) = divmod(float(parts[2].replace(',', '.')), 1)
+            second = int(second)
+            micro = int(round(micro * 1000000))
+        else:
+            # hh:mm
+            second = 0
+            micro = 0
+
+    else:
+        if len(timepart) < 4 or len(timepart) == 5:
+            raise ValueError("invalid time format: %s" % timestring)
+
+        hour = int(timepart[:2])
+        minute = int(timepart[2:4])
+
+        if len(timepart) == 4:
+            # hhmm
+            second = 0
+            micro = 0
+        else:
+            second = int(timepart[4:6])
+
+            if len(timepart) == 6:
+                # hhmmss
+                micro = 0
+            else:
+                # hhmmss.ffffff or hhmmss,ffffff
+                if not timepart[6] in '.,':
+                    raise ValueError("invalid time format: %s" % timestring)
+                micro = int(round(float('.' + timepart[7:]) * 1000000))
+
+    # Part 2: parse timezone
+
+    if zone is None and not offset:
+        # No timezone given
+        tzinfo = None
+
+    elif zone == 'Z' and not offset:
+        # UTC time given
+        tzinfo = Timezone(0)
+
+    # otherwise if a timezone offset is defined, transform it into a tzinfo
+    # instance
+    elif offset:
+        match = re.match('^(\d\d):?(\d\d)?$', offset)
+        if match is None:
+            raise ValueError("invalid timezone format: %s" % timestring)
+
+        parts = match.groups()
+
+        zhour = int(parts[0])
+        if parts[1] is None:
+            zminute = 0
+        else:
+            zminute = int(parts[1])
+
+        if zone == '+':
+            tzinfo = Timezone(zhour * 60 + zminute)
+        elif zone == '-':
+            tzinfo = Timezone(- zhour * 60 - zminute)
+
+    else:
+        raise ValueError("invalid timezone format: %s" % timestring)
+
+    return datetime.time(hour, minute, second, micro, tzinfo)
+
+
+# =============================================================================
+# New datetime.date constructors
+# =============================================================================
+
+# -----------------------------------------------------------------------------
+# Create a date from year, week number, and day in the week
+# -----------------------------------------------------------------------------
+
+def date_fromisoweek(year, week, day):
+    """
+    Create a date from year, week number, and day in week.
+
+    @param year: year
+    @type year: int
+    @param week: week number
+    @type week: int
+    @param day: day in week (1 = Monday, 7 = Sunday)
+    @type day: int
+    @return: resulting date
+    @rtype: datetime.date
+
+    @raises TypeError: One of the parameters is not an integer.
+    @raises ValueError: The year or the day number is invalid.
+    """
+
+    if week < 1 or week > 53:
+        raise ValueError('week must be in 1..53')
+    if week == 53 and datetime.date(year, 12, 28).isocalendar()[1] == 52:
+        raise ValueError('this year does not have 53 weeks')
+    if day < 1 or day > 7:
+        raise ValueError('day must be in 1..7')
+
+    # First create a date for the first day in the given year ...
+    firstday = datetime.date(year, 1, 1)
+
+    # ... and calculate an offset depending on wether or not January 1 is
+    # in this year's week 1, or in last year's week 52 or 53.
+    if firstday.isocalendar()[0] == year:
+        offset = 7 * (week - 1) + day - firstday.isoweekday()
+    else:
+        offset = 7 * week + day - firstday.isoweekday()
+
+    return firstday + datetime.timedelta(offset)
+
+
+# -----------------------------------------------------------------------------
+# Create a date from year and the day number within year
+# -----------------------------------------------------------------------------
+
+def date_fromdaynumber(year, day):
+    """
+    Create a date from year and day number within year.
+
+    @param year: year
+    @type year: int
+    @param day: day in year (1 = January 1)
+    @type day: int
+    @return: resulting date
+    @rtype: datetime.date
+
+    @raises TypeError: One of the parameters is not an integer.
+    @raises ValueError: The year number is not within 1..9999 or the day number
+        is not within 1..366 or the day number is 366 but the year is not a
+        leap year.
+    """
+
+    if day < 1 or day > 366:
+        raise ValueError('day must be in 1..366')
+    if day == 366 and not calendar.isleap(year):
+        raise ValueError('this year does not have 366 days')
+
+    return datetime.date(year, 1, 1) + datetime.timedelta(day - 1)
+
+
+# =============================================================================
+# Module self test code
+# =============================================================================
+
+if __name__ == '__main__':
+
+    # -------------------------------------------------------------------------
+    # Timezone class
+    # -------------------------------------------------------------------------
+
+    def __test_timezone():
+        now = datetime.datetime.now()
+        tz = Timezone(140)
+        print tz.utcoffset(now)
+        print tz.tzname(now)
+        tz = Timezone(-140)
+        print tz.utcoffset(now)
+        print tz.tzname(now)
+
+    # -------------------------------------------------------------------------
+    # parse function
+    # -------------------------------------------------------------------------
+
+    def __test_parse():
+        def __check(value):
+            try:
+                print "%-20s: %s" % (value, parse_iso(value).isoformat())
+            except ValueError, e:
+                print "%-20s: %s: %s" % (value, type(e).__name__, e)
+
+        __check('14230301')               # 1st March 1423
+        __check('1423-03-01')
+        __check('1981-04-05')
+        __check('14:23:21.233')
+        __check('19810405 17:23:15')
+        __check('1981-W14-7 17:23:15')
+        __check('1981-W14')
+        __check('1981W147')
+        __check('1981W14')
+        __check('2005-W01-1')
+        __check('2005-W53-6')
+        __check('1981-095 121314Z')
+        __check('1321')
+        __check('132123.5')
+        __check('132123.3+0312')
+        __check('1321-12:34')
+        __check('2005-07-27 23:59+00')
+        __check('6:2:1.233')
+        __check('2004-366')
+        __check('2005-366')
+
+    # -------------------------------------------------------------------------
+    # Call all tests
+    # -------------------------------------------------------------------------
+
+    __test_timezone()
+    __test_parse()

Modified: trunk/gnue-common/src/utils/GDateTime.py
===================================================================
--- trunk/gnue-common/src/utils/GDateTime.py    2008-07-10 12:16:34 UTC (rev 
9883)
+++ trunk/gnue-common/src/utils/GDateTime.py    2008-07-10 20:00:43 UTC (rev 
9884)
@@ -21,403 +21,14 @@
 #
 # $Id$
 
-import datetime
-import calendar
-import re
+"""
+Deprecated module. Use gnue.common.lib.iso8601 instead.
+"""
 
-from gnue.common.apps import errors
+# TODO: Deprecate with 0.8, remove with 0.9
 
+from gnue.common.lib import iso8601
 
-# =============================================================================
-# Exceptions
-# =============================================================================
-
-class InvalidDateError (errors.ApplicationError):
-  def __init__ (self, datestring):
-    msg = "'%s' is not valid literal for a date" % datestring
-    errors.ApplicationError.__init__ (self, msg)
-
-class InvalidTimeError (errors.ApplicationError):
-  def __init__ (self, timestring):
-    msg = "'%s' is not valid literal for a time" % timestring
-    errors.ApplicationError.__init__ (self, msg)
-
-class NotSupportedError (errors.ApplicationError):
-  pass
-
-# =============================================================================
-# tzinfo implementation for timezones with a fixed offset
-# =============================================================================
-
-class FixedOffsetZone (datetime.tzinfo):
-
-  # ---------------------------------------------------------------------------
-  # Create a new tzinfo instance with the given offset and an optional name
-  # ---------------------------------------------------------------------------
-
-  def __init__ (self, offset = 0, name = None):
-
-    self.__offset = datetime.timedelta (minutes = offset)
-    self.__name   = name
-
-    # A timezone with a zero offset is always 'UTC'
-    if not offset and name is None:
-      self.__name = 'UTC'
-
-  # ---------------------------------------------------------------------------
-  # return the offset of this timezone to UTC
-  # ---------------------------------------------------------------------------
-
-  def utcoffset (self, dt):
-    """
-    Return offset of local time from UTC, in minutes east of UTC. If local time
-    is west of UTC, this should be negative.
-    """
-    return self.__offset
-
-
-  # ---------------------------------------------------------------------------
-  # Return the name of the timezone
-  # ---------------------------------------------------------------------------
-
-  def tzname (self, dt):
-    return self.__name
-
-
-  # ---------------------------------------------------------------------------
-  # Return the daylight saving time adjustment 
-  # ---------------------------------------------------------------------------
-
-  def dst (self, dt):
-    """
-    Return the daylight saving time (DST) adjustment, in minutes east of UTC,
-    or None if DST information isn't known.
-    """
-    return datetime.timedelta (0)
-
-
-# =============================================================================
-# Parse a string in ISO 8601 format into a date-, time- or datetime instance
-# =============================================================================
-
-def parseISO (isostring):
-  """
-  Parse a given string with an ISO-date, -time or -datetime and create an
-  apropriate datetime.* type. The ISO string migth be given in the extended
-  format (including separators) or the basic format as well.
-
-  @param isostring: string with the date, time or datetime to be parsed
-  @return: datetime.date/time/datetime instance representing the given string
-
-  @raises InvalidDateError: if the given string cannot be parsed
-  """
-
-  match = re.match ('^(.*?)[ T](.*)$', isostring.strip ())
-  if match is not None:
-    (datepart, timepart) = match.groups ()
-    date = parseISODate (datepart)
-    (time, date) = parseISOTime (timepart, date)
-    return datetime.datetime.combine (date, time)
-
-  else:
-    try:
-      return parseISOTime (isostring.strip ())
-
-    except InvalidTimeError:
-      return parseISODate (isostring.strip ())
-
-
-# =============================================================================
-# Parse a date given as ISO string into a datetime.date instance
-# =============================================================================
-
-def parseISODate (datestring):
-  """
-  Parse a date given as string in ISO 8601 format into a datetime.date
-  instance. The date might be given in the basic- or extended format.
-  Possible forms are: ordinal dates (1981-095, 1981095), weeks (1981-W14-7,
-  1981W147) or full dates (1981-05-03, 19810503). If a week is given without a
-  day of the week, Monday of that week will be used (see L{parseISOWeek}).
-  NOTE: datetime library does not support dates earlier than 1st January 1 AD
-
-  @param datestring: string conforming to ISO 8601 date format
-  @return: datetime.date instance with the given date
-
-  @raises NotSupportedError: if the given string represents a date before 1st
-    january 1 AD.
-  @raises InvalidDateError: if the given datestring cannot be transformed into
-    a datetime.date instance
-  """
-
-  result = None
-
-  if datestring [0] == '-':
-    raise NotSupportedError, \
-        u_("Dates before 0001/01/01 are not supported by datetime library")
-
-  if "W" in datestring:
-    result = parseISOWeek (datestring)
-
-  elif "-" in datestring:
-    parts = datestring.split ('-')
-    year  = int (parts [0])
-
-    if len (parts) == 2 and len (parts [1]) == 3:
-      result = parseOrdinal (year, int (parts [1]))
-    else:
-      month = int (parts [1])
-      day   = len (parts) > 2 and int (parts [2]) or 1
-
-      result = datetime.date (year, month, day)
-
-  elif len (datestring) == 7:
-    result = parseOrdinal (int (datestring [:4]), int (datestring [4:7]))
-
-  elif len (datestring) == 8:
-    parts = map (int, [datestring [:4], datestring [4:6], datestring [6:8]])
-    result = datetime.date (*parts)
-
-  if result is None:
-    raise InvalidDateError, datestring
-
-  return result
-
-
-# =============================================================================
-# Parse an ISO string representing a week
-# =============================================================================
-
-def parseISOWeek (weekstring):
-  """
-  Parses an ISO week string given as 'YYYY-Www-d' (extended) or 'YYYYWwwd'
-  (basic) into a datetime.date instance reflecting the given date. The day of
-  the week part is optional and defaults to Monday of the week if omitted. The
-  ISO week day is defined as 1 for Monday and 7 for Sunday.
-
-  @param weekstring: ISO string with the week to be parsed
-  @returns: datetime.date instance reflecting the given date
-  """
-
-  if '-' in weekstring:
-    parts = weekstring.split ('-')
-    parts [1] = parts [1][1:]
-  else:
-    parts = weekstring [:4], weekstring [5:7], weekstring [7:]
-
-  parts = map (int, filter (None, parts))
-  year, week, day = parts [0], parts [1], len (parts) > 2 and parts [2] or 1
-
-  # First create a date for the first day in the given year ...
-  january = datetime.date (year, 1, 1)
-
-  # ... and calculate an offset depending on wether the first january is within
-  # the same year or not
-  if january.isocalendar () [0] == year:
-    offset = -january.isoweekday () + 7 * (week - 1) + day
-
-  else:
-    offset = 7 - january.isoweekday () + 7 * (week - 1) + day
-
-  return january + datetime.timedelta (offset)
-
-
-# =============================================================================
-# Build a date instance for a given date within a given year
-# =============================================================================
-
-def parseOrdinal (year, day):
-  """
-  Return a datetime.date instance for the given date within the given year,
-  where day 1 is the first of January, day 32 is the first of February and so
-  on.
-
-  @param year: the year
-  @param day: the day within the year to create a datetime.date instance for
-  @return: datetime.date reflecting the requested day within the year
-  """
-
-  return datetime.date (year, 1, 1) + datetime.timedelta (day - 1)
-
-
-# =============================================================================
-# Parse a time given as ISO string into a datetime.time instance
-# =============================================================================
-
-def parseISOTime (timestring, date = None):
-  """
-  Return a datetime.time instance for the given string in ISO 8601 format. The
-  timestring could be given in basic- or extended format and might contain a
-  timezone. Examples: 14:30:21Z, 14:30:21+02:00, 13:20:12.1234-0130, 14.3+02
-
-  @param timestring: string in ISO 8601 format to parse
-  @param date: datetime.date instance for which the timestring should be
-    parsed. This argument is optional. If the time is '24:00:00' one day will
-    be added to this date.
-  @return: If not date arguemnt is given the result is a datetime.time
-    reflecting the given string. If a date is given, the result is a tuple
-    (datetime.time, datetime.date) reflecting the requested time and the given
-    date (optionally incremented by one day)
-
-  @raises InvalidTimeError: if the given timestring cannot be transformed into
-    a datetime.time instance
-  """
-
-  timezone = None
-
-  parts = re.match ('^(.*?)(?:([Z+-])(.*)){0,1}$', timestring)
-  if parts is None:
-    raise InvalidTimeError, timestring
-
-  (timepart, zone, offset) = parts.groups ()
-
-  # Make sure to have the timepart in basic format
-  if ':' in timepart:
-    items = timepart.split (':')
-    for (ix, i) in enumerate (items):
-      if '.' in i:
-        full, frac = i.split ('.')
-        items [ix] = "%02d.%s" % (int (full), frac)
-      else:
-        items [ix] = "%02d" % int (i)
-        
-    timepart = ''.join (items)
-
-  # Use UTC timezone if the string contains a Z (=Zulu time)
-  if zone == 'Z':
-    timezone = FixedOffsetZone (0, 'UTC')
-
-  # otherwise if a timezone offset is defined, transform it into a tzinfo
-  # instance
-  if offset:
-    zoneMatch = re.match ('(\d\d)?:?(\d\d)$', offset)
-    if zoneMatch is None:
-      raise InvalidTimeError, timestring
-
-    items = filter (None, zoneMatch.groups ())
-    zhour = int (items [0])
-    zmin  = len (items) > 1 and int (items [1]) or 0
-    mult  = zone == '-' and -1 or 1
-
-    timezone = FixedOffsetZone (mult * (zhour * 60 + zmin))
-
-  # If the timestring contains a fractional part (e.g. 14.3 which means 14
-  # hours and 20 minutes) split the string into the full and the fractional
-  # part
-  match = re.match ('^(\d+)(?:[\.,](\d+)){0,1}$', timepart)
-  if match is None:
-    raise InvalidTimeError, timepart
-
-  # The full part cannot contain more than 6 characters (=HHMMSS)
-  (full, fractions) = match.groups ()
-  if len (full) > 6:
-    raise InvalidTimeError, timestring
-
-  elements = []
-  while len (full):
-    elements.append (int (full [:2]))
-    full = full [2:]
-
-  # Get an apropriate factor for the given fractions, which is 60 for hours,
-  # and minutes, and 1000000 (microseconds) for seconds.
-  if fractions:
-    factor = len (elements) < 3 and 60 or 1000000
-    elements.append (int (float ("0.%s" % fractions) * factor))
-
-  # Finally make sure to have 4 elements (where missing items are 0)
-  while len (elements) < 4:
-    elements.append (0)
-
-  (hour, minute, second, micro) = elements
-  if hour > 24 or minute > 59 or second > 60:
-    raise InvalidTimeError, timestring
-
-  # 24 in an hour is only valid as 24:00:00
-  if hour == 24 and (minute + second + micro) != 0:
-    raise InvalidTimeError, timestring
-
-  # 24:00:00 is the same as 00:00:00 of the next day
-  if hour == 24:
-    hour = minute = second = micro = 0
-    if date is not None:
-      date += datetime.timedelta (days = 1)
-
-  result = datetime.time (hour, minute, second, micro, tzinfo = timezone)
-
-  if date is not None:
-    return (result, date)
-  else:
-    return result
-
-
-# 
-def isLeapYear (year):
-  return calendar.isleap (year)
-
-class InvalidDate (errors.UserError):
-  pass
-
-class GDateTime:
-  def __init__(self):
-    self.month = 0
-    self.day = 0
-    self.year = 0
-    self.hour = 0
-    self.minute = 0
-    self.second = 0
-
-  def __repr__(self):
-    return "%04d/%02d/%02d %02d:%02d:%02d" % \
-      (self.year, self.month, self.day, self.hour, self.minute, self.second)
-
-  def getDayOfWeek(self):
-    # from the Calendar FAQ (http://www.pauahtun.org/CalendarFAQ/)
-    # 0 = Sunday
-    a = int((14 - self.month) / 12)
-    y = self.year - a
-    m = self.month + 12*a - 2
-    return divmod(self.day + y + int(y/4) - int(y/100) + int(y/400) + 
(31*m)/12,7)[1]
-
-
-  def validate(self):
-    if not (\
-        self.month >= 1 and self.month <= 12 and \
-        self.year >= 0 and \
-        self.day >= 1 and self.day <= ( \
-             (self.month in (1,3,5,7,8,10,12) and 31) or \
-             (self.month == 2 and (28 + isLeapYear(self.year))) \
-             or 30) and \
-        self.hour >= 0 and self.hour <= 23 and \
-        self.minute >= 0 and self.minute <= 59 and \
-        self.second >= 0 and self.second <= 59 ):
-      raise InvalidDate, u_("Not a valid date")
-
-
-
-# =============================================================================
-# Module self test code
-# =============================================================================
-
-def check (value):
-  print "%-20s: %s" % (value, parseISO (value).isoformat ())
-
-if __name__ == '__main__':
-
-  check ('14230301')           # 1st March 1423
-  check ('1423-03-01')           # 1st March 1423
-  check ('1981-04-05')
-  check ('14:23:21.233')
-  check ('19810405 17:23:15')
-  check ('1981-W14-7 17:23:15')
-  check ('1981-W14')
-  check ('1981W147')
-  check ('1981W14')
-  check ('2005-W01-1')
-  check ('2005-W53-6')
-  check ('1981-095 121314Z')
-  check ('1321')
-  check ('1321.5')
-  check ('1321.3+0312')
-  check ('1321-12:34')
-  check ('2005-07-27 24:00+02')
-  check ('6:2:1.233')
-
+parseISO = iso8601.parse_iso
+parseISODate = iso8601.date_parse_iso
+parseISOTime = iso8601.time_parse_iso





reply via email to

[Prev in Thread] Current Thread [Next in Thread]