[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[gnue] r7754 - in trunk/gnue-common/src: datasources rpc/drivers/xmlrpc/
From: |
johannes |
Subject: |
[gnue] r7754 - in trunk/gnue-common/src: datasources rpc/drivers/xmlrpc/pw_xmlrpc utils |
Date: |
Wed, 27 Jul 2005 09:38:18 -0500 (CDT) |
Author: johannes
Date: 2005-07-27 09:38:16 -0500 (Wed, 27 Jul 2005)
New Revision: 7754
Modified:
trunk/gnue-common/src/datasources/GConditions.py
trunk/gnue-common/src/datasources/readgsd.py
trunk/gnue-common/src/rpc/drivers/xmlrpc/pw_xmlrpc/typeconv.py
trunk/gnue-common/src/utils/GDateTime.py
Log:
added our own datetime-ISO parser; added datetime.* to pw_xmlrpc; values of
GCConst are now in native python types
Modified: trunk/gnue-common/src/datasources/GConditions.py
===================================================================
--- trunk/gnue-common/src/datasources/GConditions.py 2005-07-27 08:52:03 UTC
(rev 7753)
+++ trunk/gnue-common/src/datasources/GConditions.py 2005-07-27 14:38:16 UTC
(rev 7754)
@@ -29,6 +29,7 @@
from gnue.common.apps import errors
from gnue.common.definitions import GObjects
from gnue.common.formatting import GTypecast
+from gnue.common.utils import GDateTime
# =============================================================================
@@ -360,9 +361,12 @@
class GCConst (GConditionElement):
def __init__ (self, parent, value = None, datatype = "char"):
GConditionElement.__init__ (self, parent, 'GCCConst')
- self.type = datatype
- self.value = value
+ self.type = datatype
+ self.value = value
+ self._inits = [self.__typecast]
+ if self.value is not None:
+ self.__typecast ()
# ---------------------------------------------------------------------------
# Evaluate a constant
@@ -428,6 +432,34 @@
return u'%%(%s)s' % pKey
+ # ---------------------------------------------------------------------------
+ # Create a native python type for the constant value
+ # ---------------------------------------------------------------------------
+
+ def __typecast (self):
+
+ dtype = self.type.lower ()
+
+ if dtype == 'boolean':
+ self.value = self.value.upper () in ['TRUE', 'Y', '1']
+
+ elif dtype == 'number':
+ # NOTE: what about the decimal separator depending on the locale?
+ if "." in self.value:
+ self.value = float (self.value)
+ else:
+ self.value = int (self.value)
+
+ elif dtype == 'date':
+ self.value = GDateTime.parseISODate (self.value)
+
+ elif dtype == 'time':
+ self.value = GDateTime.parseISOTime (self.value)
+
+ elif dtype == 'datetime':
+ self.value = GDateTime.parseISO (self.value)
+
+
# -----------------------------------------------------------------------------
# Base class for parameter elements in a condition tree
# -----------------------------------------------------------------------------
@@ -1536,20 +1568,18 @@
isinstance (chkValue, datetime.date):
try:
- new = mx.DateTime.Parser.DateTimeFromString (oldValue)
+ new = GDateTime.parseISO (oldValue)
if isinstance (chkValue, datetime.time):
- micro = int (("%s" % (new.second - int (new.second))) [2:8])
- newValue = datetime.time (new.hour, new.minute, int (new.second),
- micro)
+ newValue = datetime.time (new.hour, new.minute, new.second,
+ new.microsecond)
elif isinstance (chkValue, datetime.date):
newValue = datetime.date (new.year, new.month, new.day)
elif isinstance (chkValue, datetime.datetime):
- micro = int (("%s" % (new.second - int (new.second))) [2:8])
newValue = datetime.datetime (new.year, new.month, new.day,
- new.hour, new.minute, int (new.second), micro)
+ new.hour, new.minute, new.second, new.microsecond)
else:
newValue = new
Modified: trunk/gnue-common/src/datasources/readgsd.py
===================================================================
--- trunk/gnue-common/src/datasources/readgsd.py 2005-07-27 08:52:03 UTC
(rev 7753)
+++ trunk/gnue-common/src/datasources/readgsd.py 2005-07-27 14:38:16 UTC
(rev 7754)
@@ -24,12 +24,12 @@
import os
import re
import sets
-import mx.DateTime.ISO
import datetime
from gnue.common.apps import errors, GClientApp
from gnue.common.datasources import GSchema, GDataSource, GConditions
from gnue.common.utils.FileUtils import openResource
+from gnue.common.utils import GDateTime
from gnue.common.apps.i18n import translate as _ # for epydoc
@@ -641,8 +641,7 @@
# Dates must conform with the ISO spec: YYYY-MM-DD
elif ftype == 'date':
try:
- val = mx.DateTime.ISO.ParseDate (contents.strip ())
- return datetime.date (val.year, val.month, val.day)
+ return GDateTime.parseISODate (contents.strip ())
except ValueError:
raise InvalidDateError, contents.strip ()
@@ -650,23 +649,14 @@
# Times must conform with the ISO spec: HH:[MM:[:SS[.ss]]]
elif ftype == 'time':
try:
- val = mx.DateTime.ISO.ParseTime (contents.strip ())
- # Adding the fractional part of seconds to the time instance, renders
- # all records as 'allways changed' since there's no driver handling
- # such times properly.
- micro = int ((val.second - int (val.second)) * 1000000)
- return datetime.time (val.hour, val.minute, int (val.second), micro)
+ return GDateTime.parseISOTime (contents.strip ())
except ValueError:
raise InvalidTimeError, contents.strip ()
elif ftype == 'datetime':
try:
- val = mx.DateTime.ISO.ParseDateTime (contents.strip ())
- # Fractional part of seconds is problematic
- micro = int ((val.second - int (val.second)) * 1000000)
- return datetime.datetime (val.year, val.month, val.day, val.hour,
- val.minute, int (val.second), micro)
+ return GDateTime.parseISO (contents.strip ())
except ValueError:
raise InvalidDateTimeError, contents.strip ()
Modified: trunk/gnue-common/src/rpc/drivers/xmlrpc/pw_xmlrpc/typeconv.py
===================================================================
--- trunk/gnue-common/src/rpc/drivers/xmlrpc/pw_xmlrpc/typeconv.py
2005-07-27 08:52:03 UTC (rev 7753)
+++ trunk/gnue-common/src/rpc/drivers/xmlrpc/pw_xmlrpc/typeconv.py
2005-07-27 14:38:16 UTC (rev 7754)
@@ -23,9 +23,12 @@
import time
import types
+import datetime
import mx.DateTime
import xmlrpclib
+from gnue.common.utils import GDateTime
+
# -----------------------------------------------------------------------------
# Convert native Python type to xmlrpc's type
# -----------------------------------------------------------------------------
@@ -63,6 +66,12 @@
elif isinstance (value, mx.DateTime.DateTimeDeltaType):
return xmlrpclib.DateTime ((1, 1, 1, value.hour, value.minute,
int (value.second), 1, 1, 1))
+
+ elif isinstance (value, datetime.datetime) or \
+ isinstance (value, datetime.date) or \
+ isinstance (value, datetime.time):
+ return xmlrpclib.DateTime (value.isoformat ())
+
# List
elif isinstance (value, types.ListType):
return [python_to_rpc (element, exception) for element in value]
@@ -123,10 +132,9 @@
elif isinstance (value, types.FloatType):
return value
- # Date/Time
+ # Date/Time/DateTime
elif isinstance (value, xmlrpclib.DateTime):
- # mx.DateTime.strptime doesn't work on windows
- return mx.DateTime.mktime (time.strptime (value.value, '%Y%m%dT%H:%M:%S'))
+ return GDateTime.parseISO (value.value)
# List
elif isinstance (value, types.ListType):
Modified: trunk/gnue-common/src/utils/GDateTime.py
===================================================================
--- trunk/gnue-common/src/utils/GDateTime.py 2005-07-27 08:52:03 UTC (rev
7753)
+++ trunk/gnue-common/src/utils/GDateTime.py 2005-07-27 14:38:16 UTC (rev
7754)
@@ -1,39 +1,353 @@
+# GNU Enterprise Common Library - Utilities - Datetime support
#
-# This file is part of GNU Enterprise.
+# Copyright 2001-2005 Free Software Foundation
#
-# 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
+# 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
+# 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
+# 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.
#
-# Copyright 2001-2005 Free Software Foundation
-#
-# FILE:
-# GDateTime.py
-#
-# DESCRIPTION:
-#
-# NOTES:
-#
+# $Id$
+import datetime
+import calendar
+import re
-def isLeapYear(year):
- return divmod(year,400)[1] == 0 or \
- (divmod(year,4)[1] == 0 and divmod(year,100)[1] != 0)
+from gnue.common.apps import errors
-class InvalidDate(StandardError):
+
+# =============================================================================
+# 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:
+ timepart = timepart.replace (':', '')
+
+ # 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 (StandardError):
+ pass
+
class GDateTime:
def __init__(self):
self.month = 0
@@ -71,3 +385,31 @@
raise InvalidDate, tmsg
+
+# =============================================================================
+# 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')
+
Property changes on: trunk/gnue-common/src/utils/GDateTime.py
___________________________________________________________________
Name: svn:keywords
+ Id
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [gnue] r7754 - in trunk/gnue-common/src: datasources rpc/drivers/xmlrpc/pw_xmlrpc utils,
johannes <=