[Top][All Lists]
[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
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [gnue] r9884 - in trunk/gnue-common/src: . lib utils,
reinhard <=