commit-gnue
[Top][All Lists]
Advanced

[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





reply via email to

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