embedded/mx/DateTime/DateTime.py
changeset 0 b97547f5f1fa
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/embedded/mx/DateTime/DateTime.py	Wed Nov 05 15:52:50 2008 +0100
@@ -0,0 +1,1054 @@
+""" Python part of the low-level DateTime[Delta] type implementation.
+
+    Copyright (c) 1998-2001, Marc-Andre Lemburg; mailto:mal@lemburg.com
+    Copyright (c) 2000-2007, eGenix.com Software GmbH; mailto:info@egenix.com
+    See the documentation for further information on copyrights,
+    or contact the author. All Rights Reserved.
+"""
+# Import the python implementation module
+from mxDateTime_python import *
+from mxDateTime_python import __version__
+
+# Singletons
+oneSecond = DateTimeDelta(0,0,0,1)
+oneMinute = DateTimeDelta(0,0,1)
+oneHour = DateTimeDelta(0,1)
+oneDay = DateTimeDelta(1)
+oneWeek = DateTimeDelta(7)
+Epoch = DateTimeFromAbsDateTime(1,0)
+
+# Shortcuts for pickle; for backward compatibility only (they are now
+# defined in __init__.py to further reduce the pickles length)
+def _DT(absdate,abstime):
+    return DateTimeFromAbsDateTime(absdate,abstime)
+def _DTD(seconds):
+    return DateTimeDeltaFromSeconds(seconds)
+
+# Module init
+class modinit:
+
+    global _time,_string,_math,_types
+    import time,string,math,types
+    _time = time
+    _string = string
+    _math = math
+    _types = types
+
+del modinit
+
+### Helpers
+
+def _isstring(arg,
+
+              isinstance=isinstance, types=_types):
+    
+    if isinstance(arg, types.StringType):
+        return 1
+    try:
+        if isinstance(arg, types.UnicodeType):
+            return 1
+    except AttributeError:
+        pass
+    return 0
+
+### Compatibility APIs
+
+# Aliases and functions to make 'from mx.DateTime import *' work much
+# like 'from time import *'
+
+def localtime(ticks=None,
+              # Locals:
+              time=_time.time,float=float,localtime=_time.localtime,
+              round=round,int=int,DateTime=DateTime,floor=_math.floor):
+
+    """localtime(ticks=None)
+
+       Construct a DateTime instance using local time from ticks.  If
+       ticks are not given, it defaults to the current time.  The
+       result is similar to time.localtime(). Fractions of a second
+       are rounded to the nearest micro-second.
+
+    """
+    if ticks is None:
+        ticks = time()
+    else:
+        ticks = float(ticks)
+    ticks = round(ticks, 6)
+    fticks = floor(ticks)
+    Y,M,D,h,m,s = localtime(fticks)[:6]
+    s = s + (ticks - fticks)
+    return DateTime(Y,M,D,h,m,s)
+
+def gmtime(ticks=None,
+           # Locals:
+           time=_time.time,float=float,gmtime=_time.gmtime,
+           round=round,int=int,DateTime=DateTime,floor=_math.floor):
+
+    """gmtime(ticks=None)
+
+       Construct a DateTime instance using UTC time from ticks.  If
+       ticks are not given, it defaults to the current time.  The
+       result is similar to time.gmtime(). Fractions of a second are
+       rounded to the nearest micro-second.
+
+    """
+    if ticks is None:
+        ticks = time()
+    else:
+        ticks = float(ticks)
+    ticks = round(ticks, 6)
+    fticks = floor(ticks)
+    Y,M,D,h,m,s = gmtime(ticks)[:6]
+    s = s + (ticks - fticks)
+    return DateTime(Y,M,D,h,m,s)
+
+def mktime((year,month,day,hour,minute,second,dow,doy,dst),
+           # Locals:
+           DateTime=DateTime):
+
+    """mktime((year,month,day,hour,minute,second,dow,doy,dst))
+
+       Same as the DateTime() constructor accept that the interface
+       used is compatible to the similar time.mktime() API.
+
+       Note that the tuple elements dow, doy and dst are not used in
+       any way.
+      
+    """
+    return DateTime(year,month,day,hour,minute,second)
+
+def ctime(datetime):
+
+    """ctime(datetime)
+
+       Returns a string representation of the given DateTime instance
+       using the current locale's default settings.
+
+    """
+    return datetime.strftime('%c')
+
+def today(hour=0,minute=0,second=0.0,
+          # Locals:
+          localtime=_time.localtime,time=_time.time,DateTime=DateTime):
+
+    """today(hour=0,minute=0,second=0.0)
+
+       Returns a DateTime instance for today (in local time) at the
+       given time (defaults to midnight).
+
+    """
+    Y,M,D = localtime(time())[:3]
+    return DateTime(Y,M,D,hour,minute,second)
+
+def TimeDelta(hours=0.0,minutes=0.0,seconds=0.0,
+              # Locals:
+              DateTimeDelta=DateTimeDelta):
+
+    """TimeDelta(hours=0.0,minutes=0.0,seconds=0.0)
+
+       Returns a DateTimeDelta-object reflecting the given time
+       delta. Seconds can be given as float to indicate fractions.
+
+    """
+    return DateTimeDelta(0,hours,minutes,seconds)
+
+def gm2local(datetime):
+
+    """ gm2local(datetime)
+
+        Convert a DateTime instance holding UTC time to a DateTime
+        instance using local time.
+
+    """
+    return localtime(datetime.gmticks())
+
+def local2gm(datetime):
+
+    """ local2gm(datetime)
+
+        Convert a DateTime instance holding local time to a DateTime
+        instance using UTC time.
+
+    """
+    return gmtime(datetime.ticks())
+
+# Alias
+gmt = utc
+
+# Default value for DateTimeFromTJD's tjd_myriad parameter
+current_myriad = localtime().tjd_myriad
+
+def DateTimeFromTJD(tjd,tjd_myriad=current_myriad):
+
+    """ DateTimeFromTJD(tjd[,myriad])
+
+        Return a DateTime instance for the given Truncated Julian Day.
+        myriad defaults to the TJD myriad current at package import
+        time.
+
+        Note that this version of Truncated Julian Day number does
+        real truncation of important information. It's use is
+        discouraged and unsupported.
+
+    """
+    return DateTimeFromAbsDays(tjd + tjd_myriad * 10000.0 - 1721425.0)
+
+def DateTimeFromJDN(jdn):
+
+    """ DateTimeFromJDN(jdn)
+
+        Return a DateTime instance for the given Julian Day Number.
+
+        References:
+        -----------
+        Gregorian 2000-01-01 12:00:00 corresponds to JDN 2451545.0.
+        Gregorian 1858-11-17 00:00:00.00 corresponds to JDN 2400000.5; MJD 0.0.
+        Julian -4712-01-01 12:00:00.00 corresponds to JDN 0.0.
+        Gregorian -4713-11-24 12:00:00.00 corresponds to JDN 0.0.
+
+    """
+    return DateTimeFromAbsDays(jdn - 1721425.5)
+
+def DateTimeFromMJD(mjd):
+
+    """ DateTimeFromMJD(mjd)
+
+        Return a DateTime instance for the given Modified Julian Day
+        (MJD). The MJD is calculated the same way as the JDN except
+        that 1858-11-17 00:00:00.00 is taken as origin of the scale.
+
+    """
+    return DateTimeFromAbsDays(mjd + 678575.0)
+
+def DateTimeFrom(*args, **kws):
+
+    """ DateTimeFrom(*args, **kws)
+
+        Generic DateTime instance constructor. Can handle parsing
+        strings, numbers and keywords.
+
+        XXX Add support for Unicode.
+
+    """
+    if len(args) == 1:
+        # Single argument
+        arg = args[0]
+        argtype = type(arg)
+        if _isstring(arg):
+            import Parser
+            return apply(Parser.DateTimeFromString, args, kws)
+        elif argtype is DateTimeType:
+            return arg
+        elif argtype is DateTimeDeltaType:
+            raise TypeError,'cannot convert DateTimeDelta to DateTime'
+        else:
+            try:
+                value = float(arg)
+            except (TypeError, ValueError):
+                value = int(arg)
+            assert not kws
+            return DateTimeFromTicks(value)
+
+    elif len(args) > 1:
+        # More than one argument
+        if len(args) == 2 and _isstring(args[0]) and _isstring(args[1]):
+            # interpret as date and time string
+            import Parser
+            return apply(Parser.DateTimeFromString,
+                         (args[0] + ' ' + args[1],),
+                         kws)
+
+        # Assume the arguments are the same as for DateTime()
+        return apply(DateTime, args, kws)
+
+    elif len(kws) > 0:
+        # Keyword arguments; add defaults... today at 0:00:00
+        hour = kws.get('hour',0)
+        minute = kws.get('minute',0)
+        second = kws.get('second',0)
+        today = now()
+        day = kws.get('day',today.day)
+        month = kws.get('month',today.month)
+        year = kws.get('year',today.year)
+        return DateTime(year,month,day,hour,minute,second)
+
+    else:
+        raise TypeError,'cannot convert arguments to DateTime'
+
+def DateTimeDeltaFrom(*args, **kws):
+
+    """ DateTimeDeltaFrom(*args, **kws)
+
+        Generic DateTimeDelta instance constructor. Can handle parsing
+        strings, numbers and keywords.
+
+        XXX Add support for Unicode.
+
+    """
+    if len(args) == 1:
+        # Single argument
+        arg = args[0]
+        if _isstring(arg):
+            import Parser
+            return apply(Parser.DateTimeDeltaFromString, args, kws)
+        elif type(arg) is DateTimeDeltaType:
+            return arg
+        elif type(arg) is DateTimeType:
+            raise TypeError,'cannot convert DateTime to DateTimeDelta'
+        else:
+            try:
+                value = float(arg)
+            except TypeError:
+                value = int(arg)
+            assert not kws
+            return DateTimeDeltaFromSeconds(value)
+
+    elif len(args) > 1:
+        # Assume the arguments are the same as for DateTimeDelta()
+        return apply(DateTimeDelta, args, kws)
+
+    elif len(kws) > 0:
+        # Keyword arguments; default: 00:00:00:00.00
+        hours = kws.get('hours',0)
+        minutes = kws.get('minutes',0)
+        seconds = kws.get('seconds',0.0)
+        days = kws.get('days',0)
+        return DateTimeDelta(days,hours,minutes,seconds)
+
+    else:
+        raise TypeError,'cannot convert arguments to DateTimeDelta'
+
+def TimeDeltaFrom(*args, **kws):
+
+    """ TimeDeltaFrom(*args, **kws)
+
+        Generic TimeDelta instance constructor. Can handle parsing
+        strings, numbers and keywords.
+
+        XXX Add support for Unicode.
+
+    """
+    if len(args) > 1:
+        # Assume the arguments are the same as for TimeDelta(): without
+        # days part !
+        return apply(DateTimeDelta, (0,)+args, kws)
+    else:
+        # Otherwise treat the arguments just like for DateTimeDelta
+        # instances.
+        return apply(DateTimeDeltaFrom, args, kws)
+
+def DateFromTicks(ticks,
+                  # Locals:
+                  DateTime=DateTime,localtime=_time.localtime):
+
+    """ DateFromTicks(ticks)
+
+        Constructs a DateTime instance pointing to the local time date
+        at 00:00:00.00 (midnight) indicated by the given ticks value.
+        The time part is ignored.
+
+    """
+    return apply(DateTime, localtime(ticks)[:3])
+
+def TimestampFromTicks(ticks,
+                       # Locals:
+                       DateTime=DateTime,localtime=_time.localtime):
+
+    """ TimestampFromTicks(ticks)
+
+        Constructs a DateTime instance pointing to the local date and
+        time indicated by the given ticks value.
+
+    """
+    return apply(DateTime, localtime(ticks)[:6])
+
+def TimeFromTicks(ticks,
+                  # Locals:
+                  DateTimeDelta=DateTimeDelta,localtime=_time.localtime):
+
+    """ TimeFromTicks(ticks)
+
+        Constructs a DateTimeDelta instance pointing to the local time
+        indicated by the given ticks value. The date part is ignored.
+
+    """
+    return apply(DateTimeDelta, (0,) + localtime(ticks)[3:6])
+
+# Aliases
+utctime = gmtime
+utc2local = gm2local
+local2utc = local2gm
+DateTimeFromTicks = localtime
+Date = DateTime
+Time = TimeDelta
+Timestamp = DateTime
+DateFrom = DateTimeFrom # XXX should only parse the date part !
+TimeFrom = TimeDeltaFrom
+TimestampFrom = DateTimeFrom
+GregorianDateTime = DateTime
+GregorianDate = Date
+JulianDate = JulianDateTime
+
+
+### For backward compatibility (these are depreciated):
+
+def gmticks(datetime):
+
+    """gmticks(datetime)
+
+       [DEPRECIATED: use the .gmticks() method]
+    
+       Returns a ticks value based on the values stored in
+       datetime under the assumption that they are given in UTC,
+       rather than local time.
+
+    """
+    return datetime.gmticks()
+
+# Alias
+utcticks = gmticks
+
+def tz_offset(datetime,
+              # Locals:
+              oneSecond=oneSecond):
+
+    """tz_offset(datetime)
+
+       [DEPRECIATED: use the .gmtoffset() method]
+    
+       Returns a DateTimeDelta instance representing the UTC
+       offset for datetime assuming that the stored values refer
+       to local time. If you subtract this value from datetime,
+       you'll get UTC time.
+
+    """
+    return datetime.gmtoffset()
+
+### Constants (only English; see Locale.py for other languages)
+
+# Weekdays
+Monday =        0
+Tuesday =       1
+Wednesday =     2
+Thursday =      3
+Friday =        4
+Saturday =      5
+Sunday =        6
+# as mapping
+Weekday = {'Saturday': 5, 6: 'Sunday', 'Sunday': 6, 'Thursday': 3,
+           'Wednesday': 2, 'Friday': 4, 'Tuesday': 1, 'Monday': 0,
+           5: 'Saturday', 4: 'Friday', 3: 'Thursday', 2: 'Wednesday',
+           1: 'Tuesday', 0: 'Monday'}
+
+# Months
+January =       1
+February =      2
+March =         3
+April =         4
+May =           5
+June =          6
+July =          7
+August =        8 
+September =     9
+October =       10
+November =      11
+December =      12
+# as mapping
+Month = {2: 'February', 3: 'March', None: 0, 'July': 7, 11: 'November',
+    'December': 12, 'June': 6, 'January': 1, 'September': 9, 'August':
+    8, 'March': 3, 'November': 11, 'April': 4, 12: 'December', 'May':
+    5, 10: 'October', 9: 'September', 8: 'August', 7: 'July', 6:
+    'June', 5: 'May', 4: 'April', 'October': 10, 'February': 2, 1:
+    'January', 0: None}
+
+# Limits (see also the range checks in mxDateTime.c)
+MaxDateTime = DateTime(5867440,12,31) 
+MinDateTime = DateTime(-5851455,1,1)
+MaxDateTimeDelta = DateTimeDeltaFromSeconds(2147483647 * 86400.0)
+MinDateTimeDelta = -MaxDateTimeDelta
+
+###
+
+class RelativeDateTime:
+
+    """RelativeDateTime(years=0,months=0,days=0,
+                  hours=0,minutes=0,seconds=0,
+                  year=0,month=0,day=0,
+                  hour=None,minute=None,second=None,
+                  weekday=None,weeks=None)
+
+       Returns a RelativeDateTime instance for the specified relative
+       time. The constructor handles keywords, so you'll only have to
+       give those parameters which should be changed when you add the
+       relative to an absolute DateTime instance.
+
+       Adding RelativeDateTime instances is supported with the
+       following rules: deltas will be added together, right side
+       absolute values override left side ones.
+
+       Adding RelativeDateTime instances to DateTime instances will
+       return DateTime instances with the appropriate calculations
+       applied, e.g. to get a DateTime instance for the first of next
+       month, you'd call now() + RelativeDateTime(months=+1,day=1).
+
+    """
+    years = 0
+    months = 0
+    days = 0
+    year = None
+    month = 0
+    day = 0
+    hours = 0
+    minutes = 0
+    seconds = 0
+    hour = None
+    minute = None
+    second = None
+    weekday = None
+
+    # cached hash value
+    _hash = None
+
+    # For Zope security:
+    __roles__ = None
+    __allow_access_to_unprotected_subobjects__ = 1
+
+    def __init__(self,
+                 years=0,months=0,days=0,
+                 hours=0,minutes=0,seconds=0,
+                 year=None,month=None,day=None,
+                 hour=None,minute=None,second=None,
+                 weekday=None,weeks=0):
+        
+        self.years = years
+        self.months = months
+        self.days = days + weeks*7
+        self.year = year
+        self.month = month
+        self.day = day
+        self.hours = hours
+        self.minutes = minutes
+        self.seconds = seconds
+        self.hour = hour
+        self.minute = minute
+        self.second = second
+        if weekday is not None:
+            #  Make sure we've got a 2-tuple
+            assert len(weekday) == 2
+            self.weekday = weekday
+
+    def __add__(self,other,
+                # Locals:
+                isinstance=isinstance):
+
+        if isinstance(other,RelativeDateTime):
+            # RelativeDateTime (self) + RelativeDateTime (other)
+
+            r = RelativeDateTime()
+            # date deltas
+            r.years = self.years + other.years
+            r.months = self.months + other.months
+            r.days = self.days + other.days
+            # absolute entries of other override those in self, if given
+            r.year = other.year or self.year
+            r.month = other.month or self.month
+            r.day = other.day or self.day
+            r.weekday = other.weekday or self.weekday
+            # time deltas
+            r.hours = self.hours + other.hours
+            r.minutes = self.minutes + other.minutes
+            r.seconds = self.seconds + other.seconds
+            # absolute entries of other override those in self, if given
+            r.hour = other.hour or self.hour
+            r.minute = other.minute or self.minute
+            r.second = other.second or self.second
+            return r
+
+        else:
+            raise TypeError,"can't add the two types"
+
+    def __radd__(self,other,
+                 # Locals:
+                 isinstance=isinstance,DateTimeType=DateTimeType,
+                 DateTime=DateTime,DateTimeDelta=DateTimeDelta):
+
+        if isinstance(other,DateTimeType):
+            # DateTime (other) + RelativeDateTime (self)
+
+            # date
+            if self.year is None:
+                year = other.year + self.years
+            else:
+                year = self.year + self.years
+            if self.month is None:
+                month = other.month + self.months
+            else:
+                month = self.month + self.months
+            if self.day is None:
+                day = other.day
+            else:
+                day = self.day
+            if day < 0:
+                # fix negative day values
+                month = month + 1
+                day = day + 1
+            day = day + self.days
+            # time
+            if self.hour is None:
+                hour = other.hour + self.hours
+            else:
+                hour = self.hour + self.hours
+            if self.minute is None:
+                minute = other.minute + self.minutes
+            else:
+                minute = self.minute + self.minutes
+            if self.second is None:
+                second = other.second + self.seconds
+            else:
+                second = self.second + self.seconds
+
+            # Refit into proper ranges:
+            if month < 1 or month > 12:
+                month = month - 1
+                yeardelta, monthdelta = divmod(month, 12)
+                year = year + yeardelta
+                month = monthdelta + 1
+
+            # Make sure we have integers
+            year = int(year)
+            month = int(month)
+            day = int(day)
+
+            if self.weekday is None:
+                return DateTime(year, month, 1) + \
+                       DateTimeDelta(day-1,hour,minute,second)
+            
+            # Adjust to the correct weekday
+            day_of_week,index = self.weekday
+            d = DateTime(year, month, 1) + \
+                DateTimeDelta(day-1,hour,minute,second)
+            if index == 0:
+                # 0 index: next weekday if no match
+                return d + (day_of_week - d.day_of_week)
+            elif index > 0:
+                # positive index (1 == first weekday of month)
+                first = d - (d.day - 1)
+                diff = day_of_week - first.day_of_week
+                if diff >= 0:
+                    return first + (diff + (index-1) * 7)
+                else:
+                    return first + (diff + index * 7)
+            else:
+                # negative index (-1 == last weekday of month)
+                last = d + (d.days_in_month - d.day)
+                diff = day_of_week - last.day_of_week
+                if diff <= 0:
+                    return last + (diff + (index+1) * 7)
+                else:
+                    return last + (diff + index * 7)
+            
+        else:
+            raise TypeError,"can't add the two types"
+
+    def __sub__(self,other):
+
+        if isinstance(other,RelativeDateTime):
+            # RelativeDateTime (self) - RelativeDateTime (other)
+
+            r = RelativeDateTime()
+            # date deltas
+            r.years = self.years - other.years
+            r.months = self.months - other.months
+            r.days = self.days - other.days
+            # absolute entries of other override those in self, if given
+            r.year = other.year or self.year
+            r.month = other.month or self.month
+            r.day = other.day or self.day
+            r.weekday = other.weekday or self.weekday
+            # time deltas
+            r.hours = self.hours - other.hours
+            r.minutes = self.minutes - other.minutes
+            r.seconds = self.seconds - other.seconds
+            # absolute entries of other override those in self, if given
+            r.hour = other.hour or self.hour
+            r.minute = other.minute or self.minute
+            r.second = other.second or self.second
+
+            return r
+
+        else:
+            raise TypeError,"can't subtract the two types"
+
+    def __rsub__(self,other,
+                 # Locals:
+                 isinstance=isinstance,DateTimeType=DateTimeType):
+
+        if isinstance(other,DateTimeType):
+            # DateTime (other) - RelativeDateTime (self)
+            return other + self.__neg__()
+
+        else:
+            raise TypeError,"can't subtract the two types"
+
+    def __neg__(self):
+
+        # - RelativeDateTime(self)
+
+        r = RelativeDateTime()
+        # negate date deltas
+        r.years = - self.years
+        r.months = - self.months
+        r.days = - self.days
+        # absolute entries don't change
+        r.year = self.year
+        r.month = self.month
+        r.day = self.day
+        r.weekday = self.weekday
+        # negate time deltas
+        r.hours = - self.hours
+        r.minutes = - self.minutes
+        r.seconds = - self.seconds
+        # absolute entries don't change
+        r.hour = self.hour
+        r.minute = self.minute
+        r.second = self.second
+
+        return r
+
+    def __nonzero__(self):
+
+        # RelativeDateTime instances are considered false in case
+        # they do not define any alterations
+        if (self.year is None and
+            self.years == 0 and
+            self.month is None and
+            self.months == 0 and
+            self.day is None and
+            self.weekday is None and
+            self.days == 0 and
+            self.hour is None and
+            self.hours == 0 and
+            self.minute is None and
+            self.minutes == 0 and
+            self.second is None and
+            self.seconds == 0):
+            return 0
+        else:
+            return 1
+
+    def __mul__(self,other):
+
+        # RelativeDateTime (self) * Number (other)
+        factor = float(other)
+
+        r = RelativeDateTime()
+        # date deltas
+        r.years = factor * self.years
+        r.months = factor * self.months
+        r.days = factor * self.days
+        # time deltas
+        r.hours = factor * self.hours
+        r.minutes = factor * self.minutes
+        r.seconds = factor * self.seconds
+        return r
+
+    __rmul__ = __mul__
+
+    def __div__(self,other):
+
+        # RelativeDateTime (self) / Number (other)
+        return self.__mul__(1/float(other))
+
+    def __eq__(self, other):
+
+        if isinstance(self, RelativeDateTime) and \
+           isinstance(other, RelativeDateTime):
+            # RelativeDateTime (self) == RelativeDateTime (other)
+            if (self.years == other.years and
+                self.months == other.months and
+                self.days == other.days and
+                self.year == other.year and
+                self.day == other.day and
+                self.hours == other.hours and
+                self.minutes == other.minutes and
+                self.seconds == other.seconds and
+                self.hour == other.hour and
+                self.minute == other.minute and
+                self.second == other.second and
+                self.weekday == other.weekday):
+                return 1
+            else:
+                return 0
+        else:
+            raise TypeError,"can't compare the two types"
+
+    def __hash__(self):
+
+        if self._hash is not None:
+            return self._hash
+        x = 1234
+        for value in (self.years, self.months, self.days,
+                      self.year, self.day,
+                      self.hours, self.minutes, self.seconds,
+                      self.hour, self.minute, self.second,
+                      self.weekday):
+            if value is None:
+                x = 135051820 ^ x
+            else:
+                x = hash(value) ^ x
+        self._hash = x
+        return x
+
+    def __str__(self,
+
+                join=_string.join):
+
+        l = []
+        append = l.append
+
+        # Format date part
+        if self.year is not None:
+            append('%04i-' % self.year)
+        elif self.years:
+            append('(%0+5i)-' % self.years)
+        else:
+            append('YYYY-')
+        if self.month is not None:
+            append('%02i-' % self.month)
+        elif self.months:
+            append('(%0+3i)-' % self.months)
+        else:
+            append('MM-')
+        if self.day is not None:
+            append('%02i' % self.day)
+        elif self.days:
+            append('(%0+3i)' % self.days)
+        else:
+            append('DD')
+        if self.weekday:
+            append(' %s:%i' % (Weekday[self.weekday[0]][:3],self.weekday[1]))
+        append(' ')
+        
+        # Normalize relative time values to avoid fractions
+        hours = self.hours
+        minutes = self.minutes
+        seconds = self.seconds
+        hours_fraction = hours - int(hours)
+        minutes = minutes + hours_fraction * 60.0
+        minutes_fraction = minutes - int(minutes)
+        seconds = seconds + minutes_fraction * 6.0
+        seconds_fraction = seconds - int(seconds)
+
+        if 0:
+            # Normalize to standard time ranges
+            if seconds > 60.0:
+                extra_minutes, seconds = divmod(seconds, 60.0)
+                minutes = minutes + extra_minutes
+            elif seconds < -60.0:
+                extra_minutes, seconds = divmod(seconds, -60.0)
+                minutes = minutes - extra_minutes
+            if minutes >= 60.0:
+                extra_hours, minutes = divmod(minutes, 60.0)
+                hours = hours + extra_hours
+            elif minutes <= -60.0:
+                extra_hours, minutes = divmod(minutes, -60.0)
+                hours = hours - extra_hours
+
+        # Format time part
+        if self.hour is not None:
+            append('%02i:' % self.hour)
+        elif hours:
+            append('(%0+3i):' % hours)
+        else:
+            append('HH:')
+        if self.minute is not None:
+            append('%02i:' % self.minute)
+        elif minutes:
+            append('(%0+3i):' % minutes)
+        else:
+            append('MM:')
+        if self.second is not None:
+            append('%02i' % self.second)
+        elif seconds:
+            append('(%0+3i)' % seconds)
+        else:
+            append('SS')
+            
+        return join(l,'')
+
+    def __repr__(self):
+
+        return "<%s instance for '%s' at 0x%x>" % ( 
+            self.__class__.__name__, 
+            self.__str__(), 
+            id(self))
+
+# Alias
+RelativeDate = RelativeDateTime
+
+def RelativeDateTimeFrom(*args, **kws):
+
+    """ RelativeDateTimeFrom(*args, **kws)
+
+        Generic RelativeDateTime instance constructor. Can handle
+        parsing strings and keywords.
+
+    """
+    if len(args) == 1:
+        # Single argument
+        arg = args[0]
+        if _isstring(arg):
+            import Parser
+            return apply(Parser.RelativeDateTimeFromString, args, kws)
+        elif isinstance(arg, RelativeDateTime):
+            return arg
+        else:
+            raise TypeError,\
+                  'cannot convert argument to RelativeDateTime'
+
+    else:
+        return apply(RelativeDateTime,args,kws)
+
+def RelativeDateTimeDiff(date1,date2,
+
+                         floor=_math.floor,int=int,divmod=divmod,
+                         RelativeDateTime=RelativeDateTime):
+
+    """ RelativeDateTimeDiff(date1,date2)
+
+        Returns a RelativeDateTime instance representing the difference
+        between date1 and date2 in relative terms.
+
+        The following should hold: 
+        
+        date2 + RelativeDateDiff(date1,date2) == date1 
+
+        for all dates date1 and date2.
+
+        Note that due to the algorithm used by this function, not the
+        whole range of DateTime instances is supported; there could
+        also be a loss of precision.
+
+        XXX There are still some problems left (thanks to Carel
+        Fellinger for pointing these out):
+
+        29 1 1901 ->  1 3 1901 = 1 month
+        29 1 1901 ->  1 3 1900 = -10 month and -28 days, but
+        29 1 1901 -> 28 2 1900 = -11 month and -1 day
+
+        and even worse:
+
+        >>> print RelativeDateDiff(Date(1900,3,1),Date(1901,2,1))
+        YYYY-(-11)-DD HH:MM:SS
+
+        with:
+
+        >>> print Date(1901,1,29) + RelativeDateTime(months=-11)
+        1900-03-01 00:00:00.00
+        >>> print Date(1901,2,1) + RelativeDateTime(months=-11)
+        1900-03-01 00:00:00.00
+
+    """
+    diff = date1 - date2
+    if diff.days == 0:
+        return RelativeDateTime()
+    date1months = date1.year * 12 + (date1.month - 1)
+    date2months = date2.year * 12 + (date2.month - 1)
+    #print 'months',date1months,date2months
+
+    # Calculate the months difference
+    diffmonths = date1months - date2months
+    #print 'diffmonths',diffmonths
+    if diff.days > 0:
+        years,months = divmod(diffmonths,12)
+    else:
+        years,months = divmod(diffmonths,-12)
+        years = -years
+    date3 = date2 + RelativeDateTime(years=years,months=months)
+    diff3 = date1 - date3
+    days = date1.absdays - date3.absdays
+    #print 'date3',date3,'diff3',diff3,'days',days
+
+    # Correction to ensure that all relative parts have the same sign
+    while days * diff.days < 0:
+        if diff.days > 0:
+            diffmonths = diffmonths - 1
+            years,months = divmod(diffmonths,12)
+        else:
+            diffmonths = diffmonths + 1
+            years,months = divmod(diffmonths,-12)
+            years = -years
+        #print 'diffmonths',diffmonths
+        date3 = date2 + RelativeDateTime(years=years,months=months)
+        diff3 = date1 - date3
+        days = date1.absdays - date3.absdays
+        #print 'date3',date3,'diff3',diff3,'days',days
+
+    # Drop the fraction part of days
+    if days > 0:
+        days = int(floor(days))
+    else:
+        days = int(-floor(-days))
+
+    return RelativeDateTime(years=years,
+                            months=months,
+                            days=days,
+                            hours=diff3.hour,
+                            minutes=diff3.minute,
+                            seconds=diff3.second)
+
+# Aliases
+RelativeDateDiff = RelativeDateTimeDiff
+Age = RelativeDateTimeDiff
+
+###
+
+_current_year = now().year
+_current_century, _current_year_in_century = divmod(_current_year, 100)
+_current_century = _current_century * 100
+
+def add_century(year,
+
+                current_year=_current_year,
+                current_century=_current_century):
+    
+    """ Sliding window approach to the Y2K problem: adds a suitable
+        century to the given year and returns it as integer.
+
+        The window used depends on the current year (at import time).
+        If adding the current century to the given year gives a year
+        within the range current_year-70...current_year+30 [both
+        inclusive], then the current century is added. Otherwise the
+        century (current + 1 or - 1) producing the least difference is
+        chosen.
+
+    """
+    if year > 99:
+        # Take it as-is
+        return year
+    year = year + current_century
+    diff = year - current_year
+    if diff >= -70 and diff <= 30:
+        return year
+    elif diff < -70:
+        return year + 100
+    else:
+        return year - 100
+
+# Reference formulas for JDN taken from the Calendar FAQ:
+
+def gregorian_jdn(year,month,day):
+
+    # XXX These require proper integer division.
+    a = (14-month)/12
+    y = year+4800-a
+    m = month + 12*a - 3
+    return day + (306*m+5)/10 + y*365 + y/4 - y/100 + y/400 - 32045
+
+def julian_jdn(year,month,day):
+
+    # XXX These require proper integer division.
+    a = (14-month)/12
+    y = year+4800-a
+    m = month + 12*a - 3
+    return day + (306*m+5)/10 + y*365 + y/4 - 32083