diff CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/lib/python3.8/site-packages/DateTime/DateTime.py @ 68:5028fdace37b

planemo upload commit 2e9511a184a1ca667c7be0c6321a36dc4e3d116d
author jpayne
date Tue, 18 Mar 2025 16:23:26 -0400
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/lib/python3.8/site-packages/DateTime/DateTime.py	Tue Mar 18 16:23:26 2025 -0400
@@ -0,0 +1,1948 @@
+##############################################################################
+#
+# Copyright (c) 2002 Zope Foundation and Contributors.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE
+#
+##############################################################################
+
+import copyreg as copy_reg
+import math
+import re
+from datetime import datetime
+from time import altzone
+from time import daylight
+from time import gmtime
+from time import localtime
+from time import time
+from time import timezone
+from time import tzname
+
+from zope.interface import implementer
+
+from .interfaces import DateError
+from .interfaces import DateTimeError
+from .interfaces import IDateTime
+from .interfaces import SyntaxError
+from .interfaces import TimeError
+from .pytz_support import PytzCache
+
+
+basestring = str
+long = int
+explicit_unicode_type = type(None)
+
+default_datefmt = None
+
+
+def getDefaultDateFormat():
+    global default_datefmt
+    if default_datefmt is None:
+        try:
+            from App.config import getConfiguration
+            default_datefmt = getConfiguration().datetime_format
+            return default_datefmt
+        except Exception:
+            return 'us'
+    else:
+        return default_datefmt
+
+
+# To control rounding errors, we round system time to the nearest
+# microsecond.  Then delicate calculations can rely on the fact that the
+# maximum precision that needs to be preserved is known.
+_system_time = time
+
+
+def time():
+    return round(_system_time(), 6)
+
+
+# Determine machine epoch
+tm = ((0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334),
+      (0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335))
+yr, mo, dy, hr, mn, sc = gmtime(0)[:6]
+i = int(yr - 1)
+to_year = int(i * 365 + i // 4 - i // 100 + i // 400 - 693960.0)
+to_month = tm[yr % 4 == 0 and (yr % 100 != 0 or yr % 400 == 0)][mo]
+EPOCH = ((to_year + to_month + dy +
+          (hr / 24.0 + mn / 1440.0 + sc / 86400.0)) * 86400)
+jd1901 = 2415385
+
+_TZINFO = PytzCache()
+
+INT_PATTERN = re.compile(r'([0-9]+)')
+FLT_PATTERN = re.compile(r':([0-9]+\.[0-9]+)')
+NAME_PATTERN = re.compile(r'([a-zA-Z]+)', re.I)
+SPACE_CHARS = ' \t\n'
+DELIMITERS = '-/.:,+'
+
+_MONTH_LEN = ((0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31),
+              (0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31))
+_MONTHS = ('', 'January', 'February', 'March', 'April', 'May', 'June',
+           'July', 'August', 'September', 'October', 'November', 'December')
+_MONTHS_A = ('', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
+             'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec')
+_MONTHS_P = ('', 'Jan.', 'Feb.', 'Mar.', 'Apr.', 'May', 'June',
+             'July', 'Aug.', 'Sep.', 'Oct.', 'Nov.', 'Dec.')
+_MONTHMAP = {'january': 1, 'jan': 1,
+             'february': 2, 'feb': 2,
+             'march': 3, 'mar': 3,
+             'april': 4, 'apr': 4,
+             'may': 5,
+             'june': 6, 'jun': 6,
+             'july': 7, 'jul': 7,
+             'august': 8, 'aug': 8,
+             'september': 9, 'sep': 9, 'sept': 9,
+             'october': 10, 'oct': 10,
+             'november': 11, 'nov': 11,
+             'december': 12, 'dec': 12}
+_DAYS = ('Sunday', 'Monday', 'Tuesday', 'Wednesday',
+         'Thursday', 'Friday', 'Saturday')
+_DAYS_A = ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat')
+_DAYS_P = ('Sun.', 'Mon.', 'Tue.', 'Wed.', 'Thu.', 'Fri.', 'Sat.')
+_DAYMAP = {'sunday': 1, 'sun': 1,
+           'monday': 2, 'mon': 2,
+           'tuesday': 3, 'tues': 3, 'tue': 3,
+           'wednesday': 4, 'wed': 4,
+           'thursday': 5, 'thurs': 5, 'thur': 5, 'thu': 5,
+           'friday': 6, 'fri': 6,
+           'saturday': 7, 'sat': 7}
+
+numericTimeZoneMatch = re.compile(r'[+-][0-9][0-9][0-9][0-9]').match
+iso8601Match = re.compile(r'''
+  (?P<year>\d\d\d\d)                # four digits year
+  (?:-?                             # one optional dash
+   (?:                              # followed by:
+    (?P<year_day>\d\d\d             #  three digits year day
+     (?!\d))                        #  when there is no fourth digit
+   |                                # or:
+    W                               #  one W
+    (?P<week>\d\d)                  #  two digits week
+    (?:-?                           #  one optional dash
+     (?P<week_day>\d)               #  one digit week day
+    )?                              #  week day is optional
+   |                                # or:
+    (?P<month>\d\d)?                #  two digits month
+    (?:-?                           #  one optional dash
+     (?P<day>\d\d)?                 #  two digits day
+    )?                              #  after day is optional
+   )                                #
+  )?                                # after year is optional
+  (?:[T ]                           # one T or one whitespace
+   (?P<hour>\d\d)                   # two digits hour
+   (?::?                            # one optional colon
+    (?P<minute>\d\d)?               # two digits minute
+    (?::?                           # one optional colon
+     (?P<second>\d\d)?              # two digits second
+     (?:[.,]                        # one dot or one comma
+      (?P<fraction>\d+)             # n digits fraction
+     )?                             # after second is optional
+    )?                              # after minute is optional
+   )?                               # after hour is optional
+   (?:                              # timezone:
+    (?P<Z>Z)                        #  one Z
+   |                                # or:
+    (?P<signal>[-+])                #  one plus or one minus as signal
+    (?P<hour_off>\d                 #  one digit for hour offset...
+     (?:\d(?!\d$)                   #  ...or two, if not the last two digits
+    )?)                             #  second hour offset digit is optional
+    (?::?                           #  one optional colon
+     (?P<min_off>\d\d)              #  two digits minute offset
+    )?                              #  after hour offset is optional
+   )?                               # timezone is optional
+  )?                                # time is optional
+  (?P<garbage>.*)                   # store the extra garbage
+''', re.VERBOSE).match
+
+
+def _findLocalTimeZoneName(isDST):
+    if not daylight:
+        # Daylight savings does not occur in this time zone.
+        isDST = 0
+    try:
+        # Get the name of the current time zone depending
+        # on DST.
+        _localzone = PytzCache._zmap[tzname[isDST].lower()]
+    except BaseException:
+        try:
+            # Generate a GMT-offset zone name.
+            if isDST:
+                localzone = altzone
+            else:
+                localzone = timezone
+            offset = (-localzone / 3600.0)
+            majorOffset = int(offset)
+            if majorOffset != 0:
+                minorOffset = abs(int((offset % majorOffset) * 60.0))
+            else:
+                minorOffset = 0
+            m = majorOffset >= 0 and '+' or ''
+            lz = '%s%0.02d%0.02d' % (m, majorOffset, minorOffset)
+            _localzone = PytzCache._zmap[('GMT%s' % lz).lower()]
+        except BaseException:
+            _localzone = ''
+    return _localzone
+
+
+_localzone0 = _findLocalTimeZoneName(0)
+_localzone1 = _findLocalTimeZoneName(1)
+_multipleZones = (_localzone0 != _localzone1)
+
+# Some utility functions for calculating dates:
+
+
+def _calcSD(t):
+    # Returns timezone-independent days since epoch and the fractional
+    # part of the days.
+    dd = t + EPOCH - 86400.0
+    d = dd / 86400.0
+    s = d - math.floor(d)
+    return s, d
+
+
+def _calcDependentSecond(tz, t):
+    # Calculates the timezone-dependent second (integer part only)
+    # from the timezone-independent second.
+    fset = _tzoffset(tz, t)
+    return fset + long(math.floor(t)) + long(EPOCH) - 86400
+
+
+def _calcDependentSecond2(yr, mo, dy, hr, mn, sc):
+    # Calculates the timezone-dependent second (integer part only)
+    # from the date given.
+    ss = int(hr) * 3600 + int(mn) * 60 + int(sc)
+    x = long(_julianday(yr, mo, dy) - jd1901) * 86400 + ss
+    return x
+
+
+def _calcIndependentSecondEtc(tz, x, ms):
+    # Derive the timezone-independent second from the timezone
+    # dependent second.
+    fsetAtEpoch = _tzoffset(tz, 0.0)
+    nearTime = x - fsetAtEpoch - long(EPOCH) + 86400 + ms
+    # nearTime is now within an hour of being correct.
+    # Recalculate t according to DST.
+    fset = long(_tzoffset(tz, nearTime))
+    d = (x - fset) / 86400.0 + (ms / 86400.0)
+    t = x - fset - long(EPOCH) + 86400 + ms
+    micros = (x + 86400 - fset) * 1000000 + \
+        long(round(ms * 1000000.0)) - long(EPOCH * 1000000.0)
+    s = d - math.floor(d)
+    return (s, d, t, micros)
+
+
+def _calcHMS(x, ms):
+    # hours, minutes, seconds from integer and float.
+    hr = x // 3600
+    x = x - hr * 3600
+    mn = x // 60
+    sc = x - mn * 60 + ms
+    return (hr, mn, sc)
+
+
+def _calcYMDHMS(x, ms):
+    # x is a timezone-dependent integer of seconds.
+    # Produces yr,mo,dy,hr,mn,sc.
+    yr, mo, dy = _calendarday(x // 86400 + jd1901)
+    x = int(x - (x // 86400) * 86400)
+    hr = x // 3600
+    x = x - hr * 3600
+    mn = x // 60
+    sc = x - mn * 60 + ms
+    return (yr, mo, dy, hr, mn, sc)
+
+
+def _julianday(yr, mo, dy):
+    y, m, d = long(yr), long(mo), long(dy)
+    if m > 12:
+        y = y + m // 12
+        m = m % 12
+    elif m < 1:
+        m = -m
+        y = y - m // 12 - 1
+        m = 12 - m % 12
+    if y > 0:
+        yr_correct = 0
+    else:
+        yr_correct = 3
+    if m < 3:
+        y, m = y - 1, m + 12
+    if y * 10000 + m * 100 + d > 15821014:
+        b = 2 - y // 100 + y // 400
+    else:
+        b = 0
+    return ((1461 * y - yr_correct) // 4 +
+            306001 * (m + 1) // 10000 + d + 1720994 + b)
+
+
+def _calendarday(j):
+    j = long(j)
+    if (j < 2299160):
+        b = j + 1525
+    else:
+        a = (4 * j - 7468861) // 146097
+        b = j + 1526 + a - a // 4
+    c = (20 * b - 2442) // 7305
+    d = 1461 * c // 4
+    e = 10000 * (b - d) // 306001
+    dy = int(b - d - 306001 * e // 10000)
+    mo = (e < 14) and int(e - 1) or int(e - 13)
+    yr = (mo > 2) and (c - 4716) or (c - 4715)
+    return (int(yr), int(mo), int(dy))
+
+
+def _tzoffset(tz, t):
+    """Returns the offset in seconds to GMT from a specific timezone (tz) at
+    a specific time (t).  NB! The _tzoffset result is the same same sign as
+    the time zone, i.e. GMT+2 has a 7200 second offset. This is the opposite
+    sign of time.timezone which (confusingly) is -7200 for GMT+2."""
+    try:
+        return _TZINFO[tz].info(t)[0]
+    except Exception:
+        if numericTimeZoneMatch(tz) is not None:
+            return int(tz[0:3]) * 3600 + int(tz[0] + tz[3:5]) * 60
+        else:
+            return 0  # ??
+
+
+def _correctYear(year):
+    # Y2K patch.
+    if year >= 0 and year < 100:
+        # 00-69 means 2000-2069, 70-99 means 1970-1999.
+        if year < 70:
+            year = 2000 + year
+        else:
+            year = 1900 + year
+    return year
+
+
+def safegmtime(t):
+    '''gmtime with a safety zone.'''
+    try:
+        return gmtime(t)
+    except (ValueError, OverflowError):
+        raise TimeError('The time %f is beyond the range of this Python '
+                        'implementation.' % float(t))
+
+
+def safelocaltime(t):
+    '''localtime with a safety zone.'''
+    try:
+        return localtime(t)
+    except (ValueError, OverflowError):
+        raise TimeError('The time %f is beyond the range of this Python '
+                        'implementation.' % float(t))
+
+
+def _tzoffset2rfc822zone(seconds):
+    """Takes an offset, such as from _tzoffset(), and returns an rfc822
+       compliant zone specification. Please note that the result of
+       _tzoffset() is the negative of what time.localzone and time.altzone is.
+    """
+    return "%+03d%02d" % divmod((seconds // 60), 60)
+
+
+def _tzoffset2iso8601zone(seconds):
+    """Takes an offset, such as from _tzoffset(), and returns an ISO 8601
+       compliant zone specification. Please note that the result of
+       _tzoffset() is the negative of what time.localzone and time.altzone is.
+    """
+    return "%+03d:%02d" % divmod((seconds // 60), 60)
+
+
+def Timezones():
+    """Return the list of recognized timezone names"""
+    return sorted(list(PytzCache._zmap.values()))
+
+
+class strftimeFormatter:
+
+    def __init__(self, dt, format):
+        self.dt = dt
+        self.format = format
+
+    def __call__(self):
+        return self.dt.strftime(self.format)
+
+
+@implementer(IDateTime)
+class DateTime:
+    """DateTime objects represent instants in time and provide
+       interfaces for controlling its representation without
+       affecting the absolute value of the object.
+
+       DateTime objects may be created from a wide variety of string
+       or numeric data, or may be computed from other DateTime objects.
+       DateTimes support the ability to convert their representations
+       to many major timezones, as well as the ability to create a
+       DateTime object in the context of a given timezone.
+
+       DateTime objects provide partial numerical behavior:
+
+          - Two date-time objects can be subtracted to obtain a time,
+            in days between the two.
+
+          - A date-time object and a positive or negative number may
+            be added to obtain a new date-time object that is the given
+            number of days later than the input date-time object.
+
+          - A positive or negative number and a date-time object may
+            be added to obtain a new date-time object that is the given
+            number of days later than the input date-time object.
+
+          - A positive or negative number may be subtracted from a
+            date-time object to obtain a new date-time object that is
+            the given number of days earlier than the input date-time
+            object.
+
+        DateTime objects may be converted to integer, long, or float
+        numbers of days since January 1, 1901, using the standard int,
+        long, and float functions (Compatibility Note: int, long and
+        float return the number of days since 1901 in GMT rather than
+        local machine timezone). DateTime objects also provide access
+        to their value in a float format usable with the Python time
+        module, provided that the value of the object falls in the
+        range of the epoch-based time module, and as a datetime.datetime
+        object.
+
+        A DateTime object should be considered immutable; all conversion
+        and numeric operations return a new DateTime object rather than
+        modify the current object."""
+
+    # For security machinery:
+    __roles__ = None
+    __allow_access_to_unprotected_subobjects__ = 1
+
+    # Limit the amount of instance attributes
+    __slots__ = (
+        '_timezone_naive',
+        '_tz',
+        '_dayoffset',
+        '_year',
+        '_month',
+        '_day',
+        '_hour',
+        '_minute',
+        '_second',
+        '_nearsec',
+        '_d',
+        '_micros',
+        'time',
+    )
+
+    def __init__(self, *args, **kw):
+        """Return a new date-time object"""
+        try:
+            return self._parse_args(*args, **kw)
+        except (DateError, TimeError, DateTimeError):
+            raise
+        except Exception:
+            raise SyntaxError('Unable to parse {}, {}'.format(args, kw))
+
+    def __getstate__(self):
+        return (self._micros,
+                getattr(self, '_timezone_naive', False),
+                self._tz)
+
+    def __setstate__(self, value):
+        if isinstance(value, tuple):
+            micros, tz_naive, tz = value
+            if isinstance(micros, float):
+                # BBB: support for pickle where micros was a float
+                micros = int(micros * 1000000)
+            self._parse_args(micros / 1000000., tz)
+            self._micros = micros
+            self._timezone_naive = tz_naive
+        else:
+            for k, v in value.items():
+                if k in self.__slots__:
+                    setattr(self, k, v)
+            # BBB: support for very old DateTime pickles
+            if '_micros' not in value:
+                self._micros = long(value['_t'] * 1000000)
+            if '_timezone_naive' not in value:
+                self._timezone_naive = False
+
+    def _parse_args(self, *args, **kw):
+        """Return a new date-time object.
+
+        A DateTime object always maintains its value as an absolute
+        UTC time, and is represented in the context of some timezone
+        based on the arguments used to create the object. A DateTime
+        object's methods return values based on the timezone context.
+
+        Note that in all cases the local machine timezone is used for
+        representation if no timezone is specified.
+
+        DateTimes may be created with zero to seven arguments.
+
+          - If the function is called with no arguments or with None,
+            then the current date/time is returned, represented in the
+            timezone of the local machine.
+
+          - If the function is invoked with a single string argument
+            which is a recognized timezone name, an object representing
+            the current time is returned, represented in the specified
+            timezone.
+
+          - If the function is invoked with a single string argument
+            representing a valid date/time, an object representing
+            that date/time will be returned.
+
+            As a general rule, any date-time representation that is
+            recognized and unambiguous to a resident of North America
+            is acceptable. The reason for this qualification is that
+            in North America, a date like: 2/1/1994 is interpreted
+            as February 1, 1994, while in some parts of the world,
+            it is interpreted as January 2, 1994.
+
+            A date/time string consists of two components, a date
+            component and an optional time component, separated by one
+            or more spaces. If the time component is omitted, 12:00am is
+            assumed. Any recognized timezone name specified as the final
+            element of the date/time string will be used for computing
+            the date/time value. If you create a DateTime with the
+            string 'Mar 9, 1997 1:45pm US/Pacific', the value will
+            essentially be the same as if you had captured time.time()
+            at the specified date and time on a machine in that timezone:
+
+            <pre>
+            e = DateTime('US/Eastern')
+            # returns current date/time, represented in US/Eastern.
+
+            x = DateTime('1997/3/9 1:45pm')
+            # returns specified time, represented in local machine zone.
+
+            y = DateTime('Mar 9, 1997 13:45:00')
+            # y is equal to x
+            </pre>
+
+            The date component consists of year, month, and day
+            values. The year value must be a one-, two-, or
+            four-digit integer. If a one- or two-digit year is
+            used, the year is assumed to be in the twentieth
+            century. The month may be an integer, from 1 to 12, a
+            month name, or a month abbreviation, where a period may
+            optionally follow the abbreviation. The day must be an
+            integer from 1 to the number of days in the month. The
+            year, month, and day values may be separated by
+            periods, hyphens, forward slashes, or spaces. Extra
+            spaces are permitted around the delimiters. Year,
+            month, and day values may be given in any order as long
+            as it is possible to distinguish the components. If all
+            three components are numbers that are less than 13,
+            then a month-day-year ordering is assumed.
+
+            The time component consists of hour, minute, and second
+            values separated by colons.  The hour value must be an
+            integer between 0 and 23 inclusively. The minute value
+            must be an integer between 0 and 59 inclusively. The
+            second value may be an integer value between 0 and
+            59.999 inclusively. The second value or both the minute
+            and second values may be omitted. The time may be
+            followed by am or pm in upper or lower case, in which
+            case a 12-hour clock is assumed.
+
+            New in Zope 2.4:
+            The DateTime constructor automatically detects and handles
+            ISO8601 compliant dates (YYYY-MM-DDThh:ss:mmTZD).
+
+            New in Zope 2.9.6:
+            The existing ISO8601 parser was extended to support almost
+            the whole ISO8601 specification. New formats includes:
+
+            <pre>
+            y = DateTime('1993-045')
+            # returns the 45th day from 1993, which is 14th February
+
+            w = DateTime('1993-W06-7')
+            # returns the 7th day from the 6th week from 1993, which
+            # is also 14th February
+            </pre>
+
+            See http://en.wikipedia.org/wiki/ISO_8601 for full specs.
+
+            Note that the Zope DateTime parser assumes timezone naive ISO
+            strings to be in UTC rather than local time as specified.
+
+          - If the DateTime function is invoked with a single numeric
+            argument, the number is assumed to be a floating point value
+            such as that returned by time.time().
+
+            A DateTime object is returned that represents the GMT value
+            of the time.time() float represented in the local machine's
+            timezone.
+
+          - If the DateTime function is invoked with a single argument
+            that is a DateTime instance, a copy of the passed object will
+            be created.
+
+          - New in 2.11:
+            The DateTime function may now be invoked with a single argument
+            that is a datetime.datetime instance. DateTimes may be converted
+            back to datetime.datetime objects with asdatetime().
+            DateTime instances may be converted to a timezone naive
+            datetime.datetime in UTC with utcdatetime().
+
+          - If the function is invoked with two numeric arguments, then
+            the first is taken to be an integer year and the second
+            argument is taken to be an offset in days from the beginning
+            of the year, in the context of the local machine timezone.
+
+            The date-time value returned is the given offset number of
+            days from the beginning of the given year, represented in
+            the timezone of the local machine. The offset may be positive
+            or negative.
+
+            Two-digit years are assumed to be in the twentieth
+            century.
+
+          - If the function is invoked with two arguments, the first
+            a float representing a number of seconds past the epoch
+            in gmt (such as those returned by time.time()) and the
+            second a string naming a recognized timezone, a DateTime
+            with a value of that gmt time will be returned, represented
+            in the given timezone.
+
+            <pre>
+            import time
+            t = time.time()
+
+            now_east = DateTime(t,'US/Eastern')
+            # Time t represented as US/Eastern
+
+            now_west = DateTime(t,'US/Pacific')
+            # Time t represented as US/Pacific
+
+            # now_east == now_west
+            # only their representations are different
+            </pre>
+
+          - If the function is invoked with three or more numeric
+            arguments, then the first is taken to be an integer
+            year, the second is taken to be an integer month, and
+            the third is taken to be an integer day. If the
+            combination of values is not valid, then a
+            DateError is raised. Two-digit years are assumed
+            to be in the twentieth century. The fourth, fifth, and
+            sixth arguments specify a time in hours, minutes, and
+            seconds; hours and minutes should be positive integers
+            and seconds is a positive floating point value, all of
+            these default to zero if not given. An optional string may
+            be given as the final argument to indicate timezone (the
+            effect of this is as if you had taken the value of time.time()
+            at that time on a machine in the specified timezone).
+
+            New in Zope 2.7:
+            A new keyword parameter "datefmt" can be passed to the
+            constructor. If set to "international", the constructor
+            is forced to treat ambiguous dates as "days before month
+            before year". This useful if you need to parse non-US
+            dates in a reliable way
+
+        In any case that a floating point number of seconds is given
+        or derived, it's rounded to the nearest millisecond.
+
+        If a string argument passed to the DateTime constructor cannot be
+        parsed, it will raise DateTime.SyntaxError. Invalid date components
+        will raise a DateError, while invalid time or timezone components
+        will raise a DateTimeError.
+
+        The module function Timezones() will return a list of the (common)
+        timezones recognized by the DateTime module. Recognition of
+        timezone names is case-insensitive.
+        """
+
+        datefmt = kw.get('datefmt', getDefaultDateFormat())
+        d = t = s = None
+        ac = len(args)
+        microsecs = None
+
+        if ac == 10:
+            # Internal format called only by DateTime
+            yr, mo, dy, hr, mn, sc, tz, t, d, s = args
+        elif ac == 11:
+            # Internal format that includes milliseconds (from the epoch)
+            yr, mo, dy, hr, mn, sc, tz, t, d, s, millisecs = args
+            microsecs = millisecs * 1000
+
+        elif ac == 12:
+            # Internal format that includes microseconds (from the epoch) and a
+            # flag indicating whether this was constructed in a timezone naive
+            # manner
+            yr, mo, dy, hr, mn, sc, tz, t, d, s, microsecs, tznaive = args
+            if tznaive is not None:  # preserve this information
+                self._timezone_naive = tznaive
+
+        elif not args or (ac and args[0] is None):
+            # Current time, to be displayed in local timezone
+            t = time()
+            lt = safelocaltime(t)
+            tz = self.localZone(lt)
+            ms = (t - math.floor(t))
+            s, d = _calcSD(t)
+            yr, mo, dy, hr, mn, sc = lt[:6]
+            sc = sc + ms
+            self._timezone_naive = False
+
+        elif ac == 1:
+            arg = args[0]
+
+            if arg == '':
+                raise SyntaxError(arg)
+
+            if isinstance(arg, DateTime):
+                """Construct a new DateTime instance from a given
+                DateTime instance.
+                """
+                t = arg.timeTime()
+                s, d = _calcSD(t)
+                yr, mo, dy, hr, mn, sc, tz = arg.parts()
+
+            elif isinstance(arg, datetime):
+                yr, mo, dy, hr, mn, sc, numerictz, tznaive = \
+                    self._parse_iso8601_preserving_tznaive(arg.isoformat())
+                if arg.tzinfo is None:
+                    self._timezone_naive = True
+                    tz = None
+                else:
+                    self._timezone_naive = False
+                    # if we have a pytz tzinfo, use the `zone` attribute
+                    # as a key
+                    tz = getattr(arg.tzinfo, 'zone', numerictz)
+                ms = sc - math.floor(sc)
+                x = _calcDependentSecond2(yr, mo, dy, hr, mn, sc)
+
+                if tz:
+                    try:
+                        zone = _TZINFO[tz]
+                    except DateTimeError:
+                        try:
+                            zone = _TZINFO[numerictz]
+                        except DateTimeError:
+                            raise DateTimeError(
+                                'Unknown time zone in date: %s' % arg)
+                    tz = zone.tzinfo.zone
+                else:
+                    tz = self._calcTimezoneName(x, ms)
+                s, d, t, microsecs = _calcIndependentSecondEtc(tz, x, ms)
+
+            elif (isinstance(arg, basestring) and
+                  arg.lower() in _TZINFO._zidx):
+                # Current time, to be displayed in specified timezone
+                t, tz = time(), _TZINFO._zmap[arg.lower()]
+                ms = (t - math.floor(t))
+                # Use integer arithmetic as much as possible.
+                s, d = _calcSD(t)
+                x = _calcDependentSecond(tz, t)
+                yr, mo, dy, hr, mn, sc = _calcYMDHMS(x, ms)
+
+            elif isinstance(arg, basestring):
+                # Date/time string
+                iso8601 = iso8601Match(arg.strip())
+                fields_iso8601 = iso8601 and iso8601.groupdict() or {}
+                if fields_iso8601 and not fields_iso8601.get('garbage'):
+                    yr, mo, dy, hr, mn, sc, tz, tznaive = \
+                        self._parse_iso8601_preserving_tznaive(arg)
+                    self._timezone_naive = tznaive
+                else:
+                    yr, mo, dy, hr, mn, sc, tz = self._parse(arg, datefmt)
+
+                if not self._validDate(yr, mo, dy):
+                    raise DateError('Invalid date: %s' % arg)
+                if not self._validTime(hr, mn, int(sc)):
+                    raise TimeError('Invalid time: %s' % arg)
+                ms = sc - math.floor(sc)
+                x = _calcDependentSecond2(yr, mo, dy, hr, mn, sc)
+
+                if tz:
+                    try:
+                        tz = _TZINFO._zmap[tz.lower()]
+                    except KeyError:
+                        if numericTimeZoneMatch(tz) is None:
+                            raise DateTimeError(
+                                'Unknown time zone in date: %s' % arg)
+                else:
+                    tz = self._calcTimezoneName(x, ms)
+                s, d, t, microsecs = _calcIndependentSecondEtc(tz, x, ms)
+
+            else:
+                # Seconds from epoch, gmt
+                t = arg
+                lt = safelocaltime(t)
+                tz = self.localZone(lt)
+                ms = (t - math.floor(t))
+                s, d = _calcSD(t)
+                yr, mo, dy, hr, mn, sc = lt[:6]
+                sc = sc + ms
+
+        elif ac == 2:
+            if isinstance(args[1], basestring):
+                # Seconds from epoch (gmt) and timezone
+                t, tz = args
+                ms = (t - math.floor(t))
+                try:
+                    tz = _TZINFO._zmap[tz.lower()]
+                except KeyError:
+                    if numericTimeZoneMatch(tz) is None:
+                        raise DateTimeError('Unknown time zone: %s' % tz)
+                # Use integer arithmetic as much as possible.
+                s, d = _calcSD(t)
+                x = _calcDependentSecond(tz, t)
+                yr, mo, dy, hr, mn, sc = _calcYMDHMS(x, ms)
+            else:
+                # Year, julian expressed in local zone
+                t = time()
+                lt = safelocaltime(t)
+                tz = self.localZone(lt)
+                yr, jul = args
+                yr = _correctYear(yr)
+                d = (_julianday(yr, 1, 0) - jd1901) + jul
+                x_float = d * 86400.0
+                x_floor = math.floor(x_float)
+                ms = x_float - x_floor
+                x = long(x_floor)
+                yr, mo, dy, hr, mn, sc = _calcYMDHMS(x, ms)
+                s, d, t, microsecs = _calcIndependentSecondEtc(tz, x, ms)
+        else:
+            # Explicit format
+            yr, mo, dy = args[:3]
+            hr, mn, sc, tz = 0, 0, 0, 0
+            yr = _correctYear(yr)
+            if not self._validDate(yr, mo, dy):
+                raise DateError('Invalid date: {}'.format(args))
+            args = args[3:]
+            if args:
+                hr, args = args[0], args[1:]
+                if args:
+                    mn, args = args[0], args[1:]
+                    if args:
+                        sc, args = args[0], args[1:]
+                        if args:
+                            tz, args = args[0], args[1:]
+                            if args:
+                                raise DateTimeError('Too many arguments')
+            if not self._validTime(hr, mn, sc):
+                raise TimeError('Invalid time: %s' % repr(args))
+
+            x = _calcDependentSecond2(yr, mo, dy, hr, mn, sc)
+            ms = sc - math.floor(sc)
+            if tz:
+                try:
+                    tz = _TZINFO._zmap[tz.lower()]
+                except KeyError:
+                    if numericTimeZoneMatch(tz) is None:
+                        raise DateTimeError('Unknown time zone: %s' % tz)
+            else:
+                # Get local time zone name
+                tz = self._calcTimezoneName(x, ms)
+            s, d, t, microsecs = _calcIndependentSecondEtc(tz, x, ms)
+
+        self._dayoffset = int((_julianday(yr, mo, dy) + 2) % 7)
+        # Round to nearest microsecond in platform-independent way. You
+        # cannot rely on C sprintf (Python '%') formatting to round
+        # consistently; doing it ourselves ensures that all but truly
+        # horrid C sprintf implementations will yield the same result
+        # cross-platform, provided the format asks for exactly 6 digits after
+        # the decimal point.
+        sc = round(sc, 6)
+        if sc >= 60.0:  # can happen if, e.g., orig sc was 59.9999999
+            sc = 59.999999
+        self._nearsec = math.floor(sc)
+        self._year, self._month, self._day = yr, mo, dy
+        self._hour, self._minute, self._second = hr, mn, sc
+        self.time, self._d, self._tz = s, d, tz
+        # self._micros is the time since the epoch
+        # in long integer microseconds.
+        if microsecs is None:
+            microsecs = long(round(t * 1000000.0))
+        self._micros = microsecs
+
+    def localZone(self, ltm=None):
+        '''Returns the time zone on the given date.  The time zone
+        can change according to daylight savings.'''
+        if not _multipleZones:
+            return _localzone0
+        if ltm is None:
+            ltm = localtime(time())
+        isDST = ltm[8]
+        lz = isDST and _localzone1 or _localzone0
+        return lz
+
+    def _calcTimezoneName(self, x, ms):
+        # Derive the name of the local time zone at the given
+        # timezone-dependent second.
+        if not _multipleZones:
+            return _localzone0
+        fsetAtEpoch = _tzoffset(_localzone0, 0.0)
+        nearTime = x - fsetAtEpoch - long(EPOCH) + 86400 + ms
+        # nearTime is within an hour of being correct.
+        try:
+            ltm = safelocaltime(nearTime)
+        except BaseException:
+            # We are beyond the range of Python's date support.
+            # Hopefully we can assume that daylight savings schedules
+            # repeat every 28 years.  Calculate the name of the
+            # time zone using a supported range of years.
+            yr, mo, dy, hr, mn, sc = _calcYMDHMS(x, 0)
+            yr = ((yr - 1970) % 28) + 1970
+            x = _calcDependentSecond2(yr, mo, dy, hr, mn, sc)
+            nearTime = x - fsetAtEpoch - long(EPOCH) + 86400 + ms
+
+            # nearTime might still be negative if we are east of Greenwich.
+            # But we can assume on 1969/12/31 were no timezone changes.
+            nearTime = max(0, nearTime)
+
+            ltm = safelocaltime(nearTime)
+        tz = self.localZone(ltm)
+        return tz
+
+    def _parse(self, st, datefmt=getDefaultDateFormat()):
+        # Parse date-time components from a string
+        month = year = tz = tm = None
+        ValidZones = _TZINFO._zidx
+        TimeModifiers = ['am', 'pm']
+
+        # Find timezone first, since it should always be the last
+        # element, and may contain a slash, confusing the parser.
+        st = st.strip()
+        sp = st.split()
+        tz = sp[-1]
+        if tz and (tz.lower() in ValidZones):
+            self._timezone_naive = False
+            st = ' '.join(sp[:-1])
+        else:
+            self._timezone_naive = True
+            tz = None  # Decide later, since the default time zone
+        # could depend on the date.
+
+        ints = []
+        i = 0
+        len_st = len(st)
+        while i < len_st:
+            while i < len_st and st[i] in SPACE_CHARS:
+                i += 1
+            if i < len_st and st[i] in DELIMITERS:
+                d = st[i]
+                i += 1
+            else:
+                d = ''
+            while i < len_st and st[i] in SPACE_CHARS:
+                i += 1
+
+            # The float pattern needs to look back 1 character, because it
+            # actually looks for a preceding colon like ':33.33'. This is
+            # needed to avoid accidentally matching the date part of a
+            # dot-separated date string such as '1999.12.31'.
+            if i > 0:
+                b = i - 1
+            else:
+                b = i
+
+            ts_results = FLT_PATTERN.match(st, b)
+            if ts_results:
+                s = ts_results.group(1)
+                i = i + len(s)
+                ints.append(float(s))
+                continue
+
+            ts_results = INT_PATTERN.match(st, i)
+            if ts_results:
+                s = ts_results.group(0)
+
+                ls = len(s)
+                i = i + ls
+                if (ls == 4 and d and d in '+-' and
+                        (len(ints) + (not not month) >= 3)):
+                    tz = '{}{}'.format(d, s)
+                else:
+                    v = int(s)
+                    ints.append(v)
+                continue
+
+            ts_results = NAME_PATTERN.match(st, i)
+            if ts_results:
+                s = ts_results.group(0).lower()
+                i = i + len(s)
+                if i < len_st and st[i] == '.':
+                    i += 1
+                # Check for month name:
+                _v = _MONTHMAP.get(s)
+                if _v is not None:
+                    if month is None:
+                        month = _v
+                    else:
+                        raise SyntaxError(st)
+                    continue
+                # Check for time modifier:
+                if s in TimeModifiers:
+                    if tm is None:
+                        tm = s
+                    else:
+                        raise SyntaxError(st)
+                    continue
+                # Check for and skip day of week:
+                if s in _DAYMAP:
+                    continue
+
+            raise SyntaxError(st)
+
+        day = None
+        if ints[-1] > 60 and d not in ('.', ':', '/') and len(ints) > 2:
+            year = ints[-1]
+            del ints[-1]
+            if month:
+                day = ints[0]
+                del ints[:1]
+            else:
+                if datefmt == "us":
+                    month = ints[0]
+                    day = ints[1]
+                else:
+                    month = ints[1]
+                    day = ints[0]
+                del ints[:2]
+        elif month:
+            if len(ints) > 1:
+                if ints[0] > 31:
+                    year = ints[0]
+                    day = ints[1]
+                else:
+                    year = ints[1]
+                    day = ints[0]
+                del ints[:2]
+        elif len(ints) > 2:
+            if ints[0] > 31:
+                year = ints[0]
+                if ints[1] > 12:
+                    day = ints[1]
+                    month = ints[2]
+                else:
+                    day = ints[2]
+                    month = ints[1]
+            if ints[1] > 31:
+                year = ints[1]
+                if ints[0] > 12 and ints[2] <= 12:
+                    day = ints[0]
+                    month = ints[2]
+                elif ints[2] > 12 and ints[0] <= 12:
+                    day = ints[2]
+                    month = ints[0]
+            elif ints[2] > 31:
+                year = ints[2]
+                if ints[0] > 12:
+                    day = ints[0]
+                    month = ints[1]
+                else:
+                    if datefmt == "us":
+                        day = ints[1]
+                        month = ints[0]
+                    else:
+                        day = ints[0]
+                        month = ints[1]
+
+            elif ints[0] <= 12:
+                month = ints[0]
+                day = ints[1]
+                year = ints[2]
+            del ints[:3]
+
+        if day is None:
+            # Use today's date.
+            year, month, day = localtime(time())[:3]
+
+        year = _correctYear(year)
+        if year < 1000:
+            raise SyntaxError(st)
+
+        leap = year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
+        try:
+            if not day or day > _MONTH_LEN[leap][month]:
+                raise DateError(st)
+        except IndexError:
+            raise DateError(st)
+
+        tod = 0
+        if ints:
+            i = ints[0]
+            # Modify hour to reflect am/pm
+            if tm and (tm == 'pm') and i < 12:
+                i += 12
+            if tm and (tm == 'am') and i == 12:
+                i = 0
+            if i > 24:
+                raise TimeError(st)
+            tod = tod + int(i) * 3600
+            del ints[0]
+            if ints:
+                i = ints[0]
+                if i > 60:
+                    raise TimeError(st)
+                tod = tod + int(i) * 60
+                del ints[0]
+                if ints:
+                    i = ints[0]
+                    if i > 60:
+                        raise TimeError(st)
+                    tod = tod + i
+                    del ints[0]
+                    if ints:
+                        raise SyntaxError(st)
+
+        tod_int = int(math.floor(tod))
+        ms = tod - tod_int
+        hr, mn, sc = _calcHMS(tod_int, ms)
+        if not tz:
+            # Figure out what time zone it is in the local area
+            # on the given date.
+            x = _calcDependentSecond2(year, month, day, hr, mn, sc)
+            tz = self._calcTimezoneName(x, ms)
+
+        return year, month, day, hr, mn, sc, tz
+
+    # Internal methods
+    def _validDate(self, y, m, d):
+        if m < 1 or m > 12 or y < 0 or d < 1 or d > 31:
+            return 0
+        return d <= _MONTH_LEN[
+            (y % 4 == 0 and (y % 100 != 0 or y % 400 == 0))][m]
+
+    def _validTime(self, h, m, s):
+        return h >= 0 and h <= 23 and m >= 0 and m <= 59 and s >= 0 and s < 60
+
+    def __getattr__(self, name):
+        if '%' in name:
+            return strftimeFormatter(self, name)
+        raise AttributeError(name)
+
+    # Conversion and comparison methods
+
+    def timeTime(self):
+        """Return the date/time as a floating-point number in UTC,
+        in the format used by the Python time module.
+
+        Note that it is possible to create date/time values with
+        DateTime that have no meaningful value to the time module.
+        """
+        return self._micros / 1000000.0
+
+    def toZone(self, z):
+        """Return a DateTime with the value as the current
+        object, represented in the indicated timezone.
+        """
+        t, tz = self._t, _TZINFO._zmap[z.lower()]
+        micros = self.micros()
+        tznaive = False  # you're performing a timzone change, can't be naive
+
+        try:
+            # Try to use time module for speed.
+            yr, mo, dy, hr, mn, sc = safegmtime(t + _tzoffset(tz, t))[:6]
+            sc = self._second
+            return self.__class__(yr, mo, dy, hr, mn, sc, tz, t,
+                                  self._d, self.time, micros, tznaive)
+        except Exception:
+            # gmtime can't perform the calculation in the given range.
+            # Calculate the difference between the two time zones.
+            tzdiff = _tzoffset(tz, t) - _tzoffset(self._tz, t)
+            if tzdiff == 0:
+                return self
+            sc = self._second
+            ms = sc - math.floor(sc)
+            x = _calcDependentSecond2(self._year, self._month, self._day,
+                                      self._hour, self._minute, sc)
+            x_new = x + tzdiff
+            yr, mo, dy, hr, mn, sc = _calcYMDHMS(x_new, ms)
+            return self.__class__(yr, mo, dy, hr, mn, sc, tz, t,
+                                  self._d, self.time, micros, tznaive)
+
+    def isFuture(self):
+        """Return true if this object represents a date/time
+        later than the time of the call.
+        """
+        return (self._t > time())
+
+    def isPast(self):
+        """Return true if this object represents a date/time
+        earlier than the time of the call.
+        """
+        return (self._t < time())
+
+    def isCurrentYear(self):
+        """Return true if this object represents a date/time
+        that falls within the current year, in the context
+        of this object's timezone representation.
+        """
+        t = time()
+        return safegmtime(t + _tzoffset(self._tz, t))[0] == self._year
+
+    def isCurrentMonth(self):
+        """Return true if this object represents a date/time
+        that falls within the current month, in the context
+        of this object's timezone representation.
+        """
+        t = time()
+        gmt = safegmtime(t + _tzoffset(self._tz, t))
+        return gmt[0] == self._year and gmt[1] == self._month
+
+    def isCurrentDay(self):
+        """Return true if this object represents a date/time
+        that falls within the current day, in the context
+        of this object's timezone representation.
+        """
+        t = time()
+        gmt = safegmtime(t + _tzoffset(self._tz, t))
+        return (gmt[0] == self._year and gmt[1] == self._month and
+                gmt[2] == self._day)
+
+    def isCurrentHour(self):
+        """Return true if this object represents a date/time
+        that falls within the current hour, in the context
+        of this object's timezone representation.
+        """
+        t = time()
+        gmt = safegmtime(t + _tzoffset(self._tz, t))
+        return (gmt[0] == self._year and gmt[1] == self._month and
+                gmt[2] == self._day and gmt[3] == self._hour)
+
+    def isCurrentMinute(self):
+        """Return true if this object represents a date/time
+        that falls within the current minute, in the context
+        of this object's timezone representation.
+        """
+        t = time()
+        gmt = safegmtime(t + _tzoffset(self._tz, t))
+        return (gmt[0] == self._year and gmt[1] == self._month and
+                gmt[2] == self._day and gmt[3] == self._hour and
+                gmt[4] == self._minute)
+
+    def earliestTime(self):
+        """Return a new DateTime object that represents the earliest
+        possible time (in whole seconds) that still falls within
+        the current object's day, in the object's timezone context.
+        """
+        return self.__class__(
+            self._year, self._month, self._day, 0, 0, 0, self._tz)
+
+    def latestTime(self):
+        """Return a new DateTime object that represents the latest
+        possible time (in whole seconds) that still falls within
+        the current object's day, in the object's timezone context.
+        """
+        return self.__class__(
+            self._year, self._month, self._day, 23, 59, 59, self._tz)
+
+    def greaterThan(self, t):
+        """Compare this DateTime object to another DateTime object
+        OR a floating point number such as that which is returned
+        by the Python time module.
+
+        Returns true if the object represents a date/time greater
+        than the specified DateTime or time module style time.
+
+        Revised to give more correct results through comparison of
+        long integer microseconds.
+        """
+        if t is None:
+            return True
+        if isinstance(t, (float, int)):
+            return self._micros > long(t * 1000000)
+        try:
+            return self._micros > t._micros
+        except AttributeError:
+            return self._micros > t
+
+    __gt__ = greaterThan
+
+    def greaterThanEqualTo(self, t):
+        """Compare this DateTime object to another DateTime object
+        OR a floating point number such as that which is returned
+        by the Python time module.
+
+        Returns true if the object represents a date/time greater
+        than or equal to the specified DateTime or time module style
+        time.
+
+        Revised to give more correct results through comparison of
+        long integer microseconds.
+        """
+        if t is None:
+            return True
+        if isinstance(t, (float, int)):
+            return self._micros >= long(t * 1000000)
+        try:
+            return self._micros >= t._micros
+        except AttributeError:
+            return self._micros >= t
+
+    __ge__ = greaterThanEqualTo
+
+    def equalTo(self, t):
+        """Compare this DateTime object to another DateTime object
+        OR a floating point number such as that which is returned
+        by the Python time module.
+
+        Returns true if the object represents a date/time equal to
+        the specified DateTime or time module style time.
+
+        Revised to give more correct results through comparison of
+        long integer microseconds.
+        """
+        if t is None:
+            return False
+        if isinstance(t, (float, int)):
+            return self._micros == long(t * 1000000)
+        try:
+            return self._micros == t._micros
+        except AttributeError:
+            return self._micros == t
+
+    def notEqualTo(self, t):
+        """Compare this DateTime object to another DateTime object
+        OR a floating point number such as that which is returned
+        by the Python time module.
+
+        Returns true if the object represents a date/time not equal
+        to the specified DateTime or time module style time.
+
+        Revised to give more correct results through comparison of
+        long integer microseconds.
+        """
+        return not self.equalTo(t)
+
+    def __eq__(self, t):
+        """Compare this DateTime object to another DateTime object.
+        Return True if their internal state is the same. Two objects
+        representing the same time in different timezones are regared as
+        unequal. Use the equalTo method if you are only interested in them
+        referring to the same moment in time.
+        """
+        if not isinstance(t, DateTime):
+            return False
+        return (self._micros, self._tz) == (t._micros, t._tz)
+
+    def __ne__(self, t):
+        return not self.__eq__(t)
+
+    def lessThan(self, t):
+        """Compare this DateTime object to another DateTime object
+        OR a floating point number such as that which is returned
+        by the Python time module.
+
+        Returns true if the object represents a date/time less than
+        the specified DateTime or time module style time.
+
+        Revised to give more correct results through comparison of
+        long integer microseconds.
+        """
+        if t is None:
+            return False
+        if isinstance(t, (float, int)):
+            return self._micros < long(t * 1000000)
+        try:
+            return self._micros < t._micros
+        except AttributeError:
+            return self._micros < t
+
+    __lt__ = lessThan
+
+    def lessThanEqualTo(self, t):
+        """Compare this DateTime object to another DateTime object
+        OR a floating point number such as that which is returned
+        by the Python time module.
+
+        Returns true if the object represents a date/time less than
+        or equal to the specified DateTime or time module style time.
+
+        Revised to give more correct results through comparison of
+        long integer microseconds.
+        """
+        if t is None:
+            return False
+        if isinstance(t, (float, int)):
+            return self._micros <= long(t * 1000000)
+        try:
+            return self._micros <= t._micros
+        except AttributeError:
+            return self._micros <= t
+
+    __le__ = lessThanEqualTo
+
+    def isLeapYear(self):
+        """Return true if the current year (in the context of the
+        object's timezone) is a leap year.
+        """
+        return (self._year % 4 == 0 and
+                (self._year % 100 != 0 or self._year % 400 == 0))
+
+    def dayOfYear(self):
+        """Return the day of the year, in context of the timezone
+        representation of the object.
+        """
+        d = int(self._d + (_tzoffset(self._tz, self._t) / 86400.0))
+        return int((d + jd1901) - _julianday(self._year, 1, 0))
+
+    # Component access
+    def parts(self):
+        """Return a tuple containing the calendar year, month,
+        day, hour, minute second and timezone of the object.
+        """
+        return (self._year, self._month, self._day, self._hour,
+                self._minute, self._second, self._tz)
+
+    def timezone(self):
+        """Return the timezone in which the object is represented."""
+        return self._tz
+
+    def tzoffset(self):
+        """Return the timezone offset for the objects timezone."""
+        return _tzoffset(self._tz, self._t)
+
+    def year(self):
+        """Return the calendar year of the object."""
+        return self._year
+
+    def month(self):
+        """Return the month of the object as an integer."""
+        return self._month
+
+    @property
+    def _fmon(self):
+        return _MONTHS[self._month]
+
+    def Month(self):
+        """Return the full month name."""
+        return self._fmon
+
+    @property
+    def _amon(self):
+        return _MONTHS_A[self._month]
+
+    def aMonth(self):
+        """Return the abbreviated month name."""
+        return self._amon
+
+    def Mon(self):
+        """Compatibility: see aMonth."""
+        return self._amon
+
+    @property
+    def _pmon(self):
+        return _MONTHS_P[self._month]
+
+    def pMonth(self):
+        """Return the abbreviated (with period) month name."""
+        return self._pmon
+
+    def Mon_(self):
+        """Compatibility: see pMonth."""
+        return self._pmon
+
+    def day(self):
+        """Return the integer day."""
+        return self._day
+
+    @property
+    def _fday(self):
+        return _DAYS[self._dayoffset]
+
+    def Day(self):
+        """Return the full name of the day of the week."""
+        return self._fday
+
+    def DayOfWeek(self):
+        """Compatibility: see Day."""
+        return self._fday
+
+    @property
+    def _aday(self):
+        return _DAYS_A[self._dayoffset]
+
+    def aDay(self):
+        """Return the abbreviated name of the day of the week."""
+        return self._aday
+
+    @property
+    def _pday(self):
+        return _DAYS_P[self._dayoffset]
+
+    def pDay(self):
+        """Return the abbreviated (with period) name of the day of the week."""
+        return self._pday
+
+    def Day_(self):
+        """Compatibility: see pDay."""
+        return self._pday
+
+    def dow(self):
+        """Return the integer day of the week, where Sunday is 0."""
+        return self._dayoffset
+
+    def dow_1(self):
+        """Return the integer day of the week, where Sunday is 1."""
+        return self._dayoffset + 1
+
+    @property
+    def _pmhour(self):
+        hr = self._hour
+        if hr > 12:
+            return hr - 12
+        return hr or 12
+
+    def h_12(self):
+        """Return the 12-hour clock representation of the hour."""
+        return self._pmhour
+
+    def h_24(self):
+        """Return the 24-hour clock representation of the hour."""
+        return self._hour
+
+    @property
+    def _pm(self):
+        hr = self._hour
+        if hr >= 12:
+            return 'pm'
+        return 'am'
+
+    def ampm(self):
+        """Return the appropriate time modifier (am or pm)."""
+        return self._pm
+
+    def hour(self):
+        """Return the 24-hour clock representation of the hour."""
+        return self._hour
+
+    def minute(self):
+        """Return the minute."""
+        return self._minute
+
+    def second(self):
+        """Return the second."""
+        return self._second
+
+    def millis(self):
+        """Return the millisecond since the epoch in GMT."""
+        return self._micros // 1000
+
+    def micros(self):
+        """Return the microsecond since the epoch in GMT."""
+        return self._micros
+
+    def timezoneNaive(self):
+        """The Python datetime module introduces the idea of distinguishing
+        between timezone aware and timezone naive datetime values. For lossless
+        conversion to and from datetime.datetime we record this
+        information using True / False. DateTime makes no distinction, if we
+        don't have any information we return None here.
+        """
+        try:
+            return self._timezone_naive
+        except AttributeError:
+            return None
+
+    def strftime(self, format):
+        """Format the date/time using the *current timezone representation*."""
+        x = _calcDependentSecond2(self._year, self._month, self._day,
+                                  self._hour, self._minute, self._second)
+        ltz = self._calcTimezoneName(x, 0)
+        tzdiff = _tzoffset(ltz, self._t) - _tzoffset(self._tz, self._t)
+        zself = self + tzdiff / 86400.0
+        microseconds = int((zself._second - zself._nearsec) * 1000000)
+        unicode_format = False
+        if isinstance(format, explicit_unicode_type):
+            format = format.encode('utf-8')
+            unicode_format = True
+        ds = datetime(zself._year, zself._month, zself._day, zself._hour,
+                      zself._minute, int(zself._nearsec),
+                      microseconds).strftime(format)
+        if unicode_format:
+            return ds.decode('utf-8')
+        return ds
+
+    # General formats from previous DateTime
+    def Date(self):
+        """Return the date string for the object."""
+        return "%s/%2.2d/%2.2d" % (self._year, self._month, self._day)
+
+    def Time(self):
+        """Return the time string for an object to the nearest second."""
+        return '%2.2d:%2.2d:%2.2d' % (self._hour, self._minute, self._nearsec)
+
+    def TimeMinutes(self):
+        """Return the time string for an object not showing seconds."""
+        return '%2.2d:%2.2d' % (self._hour, self._minute)
+
+    def AMPM(self):
+        """Return the time string for an object to the nearest second."""
+        return '%2.2d:%2.2d:%2.2d %s' % (
+            self._pmhour, self._minute, self._nearsec, self._pm)
+
+    def AMPMMinutes(self):
+        """Return the time string for an object not showing seconds."""
+        return '%2.2d:%2.2d %s' % (self._pmhour, self._minute, self._pm)
+
+    def PreciseTime(self):
+        """Return the time string for the object."""
+        return '%2.2d:%2.2d:%06.3f' % (self._hour, self._minute, self._second)
+
+    def PreciseAMPM(self):
+        """Return the time string for the object."""
+        return '%2.2d:%2.2d:%06.3f %s' % (
+            self._pmhour, self._minute, self._second, self._pm)
+
+    def yy(self):
+        """Return calendar year as a 2 digit string."""
+        return str(self._year)[-2:]
+
+    def mm(self):
+        """Return month as a 2 digit string."""
+        return '%02d' % self._month
+
+    def dd(self):
+        """Return day as a 2 digit string."""
+        return '%02d' % self._day
+
+    def rfc822(self):
+        """Return the date in RFC 822 format."""
+        tzoffset = _tzoffset2rfc822zone(_tzoffset(self._tz, self._t))
+        return '%s, %2.2d %s %d %2.2d:%2.2d:%2.2d %s' % (
+            self._aday, self._day, self._amon, self._year,
+            self._hour, self._minute, self._nearsec, tzoffset)
+
+    # New formats
+    def fCommon(self):
+        """Return a string representing the object's value
+        in the format: March 1, 1997 1:45 pm.
+        """
+        return '%s %s, %4.4d %s:%2.2d %s' % (
+               self._fmon, self._day, self._year, self._pmhour,
+               self._minute, self._pm)
+
+    def fCommonZ(self):
+        """Return a string representing the object's value
+        in the format: March 1, 1997 1:45 pm US/Eastern.
+        """
+        return '%s %s, %4.4d %d:%2.2d %s %s' % (
+               self._fmon, self._day, self._year, self._pmhour,
+               self._minute, self._pm, self._tz)
+
+    def aCommon(self):
+        """Return a string representing the object's value
+        in the format: Mar 1, 1997 1:45 pm.
+        """
+        return '%s %s, %4.4d %s:%2.2d %s' % (
+               self._amon, self._day, self._year, self._pmhour,
+               self._minute, self._pm)
+
+    def aCommonZ(self):
+        """Return a string representing the object's value
+        in the format: Mar 1, 1997 1:45 pm US/Eastern.
+        """
+        return '%s %s, %4.4d %d:%2.2d %s %s' % (
+               self._amon, self._day, self._year, self._pmhour,
+               self._minute, self._pm, self._tz)
+
+    def pCommon(self):
+        """Return a string representing the object's value
+        in the format: Mar. 1, 1997 1:45 pm.
+        """
+        return '%s %s, %4.4d %s:%2.2d %s' % (
+               self._pmon, self._day, self._year, self._pmhour,
+               self._minute, self._pm)
+
+    def pCommonZ(self):
+        """Return a string representing the object's value
+        in the format: Mar. 1, 1997 1:45 pm US/Eastern.
+        """
+        return '%s %s, %4.4d %d:%2.2d %s %s' % (
+               self._pmon, self._day, self._year, self._pmhour,
+               self._minute, self._pm, self._tz)
+
+    def ISO(self):
+        """Return the object in ISO standard format.
+
+        Note: this is *not* ISO 8601-format! See the ISO8601 and
+        HTML4 methods below for ISO 8601-compliant output.
+
+        Dates are output as: YYYY-MM-DD HH:MM:SS
+        """
+        return "%.4d-%.2d-%.2d %.2d:%.2d:%.2d" % (
+            self._year, self._month, self._day,
+            self._hour, self._minute, self._second)
+
+    def ISO8601(self):
+        """Return the object in ISO 8601-compatible format containing the
+        date, time with seconds-precision and the time zone identifier.
+
+        See: http://www.w3.org/TR/NOTE-datetime
+
+        Dates are output as: YYYY-MM-DDTHH:MM:SSTZD
+            T is a literal character.
+            TZD is Time Zone Designator, format +HH:MM or -HH:MM
+
+        If the instance is timezone naive (it was not specified with a timezone
+        when it was constructed) then the timezone is omitted.
+
+        The HTML4 method below offers the same formatting, but converts
+        to UTC before returning the value and sets the TZD "Z".
+        """
+        if self.timezoneNaive():
+            return "%0.4d-%0.2d-%0.2dT%0.2d:%0.2d:%0.2d" % (
+                self._year, self._month, self._day,
+                self._hour, self._minute, self._second)
+        tzoffset = _tzoffset2iso8601zone(_tzoffset(self._tz, self._t))
+        return "%0.4d-%0.2d-%0.2dT%0.2d:%0.2d:%0.2d%s" % (
+            self._year, self._month, self._day,
+            self._hour, self._minute, self._second, tzoffset)
+
+    def HTML4(self):
+        """Return the object in the format used in the HTML4.0 specification,
+        one of the standard forms in ISO8601.
+
+        See: http://www.w3.org/TR/NOTE-datetime
+
+        Dates are output as: YYYY-MM-DDTHH:MM:SSZ
+           T, Z are literal characters.
+           The time is in UTC.
+        """
+        newdate = self.toZone('UTC')
+        return "%0.4d-%0.2d-%0.2dT%0.2d:%0.2d:%0.2dZ" % (
+            newdate._year, newdate._month, newdate._day,
+            newdate._hour, newdate._minute, newdate._second)
+
+    def asdatetime(self):
+        """Return a standard library datetime.datetime
+        """
+        tznaive = self.timezoneNaive()
+        if tznaive:
+            tzinfo = None
+        else:
+            tzinfo = _TZINFO[self._tz].tzinfo
+        second = int(self._second)
+        microsec = self.micros() % 1000000
+        dt = datetime(self._year, self._month, self._day, self._hour,
+                      self._minute, second, microsec, tzinfo)
+        return dt
+
+    def utcdatetime(self):
+        """Convert the time to UTC and return a timezone naive datetime object
+        """
+        utc = self.toZone('UTC')
+        second = int(utc._second)
+        microsec = utc.micros() % 1000000
+        dt = datetime(utc._year, utc._month, utc._day, utc._hour,
+                      utc._minute, second, microsec)
+        return dt
+
+    def __add__(self, other):
+        """A DateTime may be added to a number and a number may be
+        added to a DateTime; two DateTimes cannot be added.
+        """
+        if hasattr(other, '_t'):
+            raise DateTimeError('Cannot add two DateTimes')
+        o = float(other)
+        tz = self._tz
+        omicros = round(o * 86400000000)
+        tmicros = self.micros() + omicros
+        t = tmicros / 1000000.0
+        d = (tmicros + long(EPOCH * 1000000)) / 86400000000.0
+        s = d - math.floor(d)
+        ms = t - math.floor(t)
+        x = _calcDependentSecond(tz, t)
+        yr, mo, dy, hr, mn, sc = _calcYMDHMS(x, ms)
+        return self.__class__(yr, mo, dy, hr, mn, sc, self._tz,
+                              t, d, s, tmicros, self.timezoneNaive())
+
+    __radd__ = __add__
+
+    def __sub__(self, other):
+        """Either a DateTime or a number may be subtracted from a
+        DateTime, however, a DateTime may not be subtracted from
+        a number.
+        """
+        if hasattr(other, '_d'):
+            return (self.micros() - other.micros()) / 86400000000.0
+        else:
+            return self.__add__(-(other))
+
+    def __repr__(self):
+        """Convert a DateTime to a string that looks like a Python
+        expression.
+        """
+        return '{}(\'{}\')'.format(self.__class__.__name__, str(self))
+
+    def __str__(self):
+        """Convert a DateTime to a string."""
+        y, m, d = self._year, self._month, self._day
+        h, mn, s, t = self._hour, self._minute, self._second, self._tz
+        if s == int(s):
+            # A whole number of seconds -- suppress milliseconds.
+            return '%4.4d/%2.2d/%2.2d %2.2d:%2.2d:%2.2d %s' % (
+                y, m, d, h, mn, s, t)
+        else:
+            # s is already rounded to the nearest microsecond, and
+            # it's not a whole number of seconds.  Be sure to print
+            # 2 digits before the decimal point.
+            return '%4.4d/%2.2d/%2.2d %2.2d:%2.2d:%06.6f %s' % (
+                y, m, d, h, mn, s, t)
+
+    def __format__(self, fmt):
+        """Render a DateTime in an f-string."""
+        if not isinstance(fmt, str):
+            raise TypeError("must be str, not %s" % type(fmt).__name__)
+        if len(fmt) != 0:
+            return self.strftime(fmt)
+        return str(self)
+
+    def __hash__(self):
+        """Compute a hash value for a DateTime."""
+        return int(((self._year % 100 * 12 + self._month) * 31 +
+                    self._day + self.time) * 100)
+
+    def __int__(self):
+        """Convert to an integer number of seconds since the epoch (gmt)."""
+        return int(self.micros() // 1000000)
+
+    def __long__(self):
+        """Convert to a long-int number of seconds since the epoch (gmt)."""
+        return long(self.micros() // 1000000)  # pragma: PY2
+
+    def __float__(self):
+        """Convert to floating-point number of seconds since the epoch (gmt).
+        """
+        return self.micros() / 1000000.0
+
+    @property
+    def _t(self):
+        return self._micros / 1000000.0
+
+    def _parse_iso8601(self, s):
+        # preserve the previously implied contract
+        # who knows where this could be used...
+        return self._parse_iso8601_preserving_tznaive(s)[:7]
+
+    def _parse_iso8601_preserving_tznaive(self, s):
+        try:
+            return self.__parse_iso8601(s)
+        except IndexError:
+            raise SyntaxError(
+                'Not an ISO 8601 compliant date string: "%s"' % s)
+
+    def __parse_iso8601(self, s):
+        """Parse an ISO 8601 compliant date.
+
+        See: http://en.wikipedia.org/wiki/ISO_8601
+        """
+        month = day = week_day = 1
+        year = hour = minute = seconds = hour_off = min_off = 0
+        tznaive = True
+
+        iso8601 = iso8601Match(s.strip())
+        fields = iso8601 and iso8601.groupdict() or {}
+        if not iso8601 or fields.get('garbage'):
+            raise IndexError
+
+        if fields['year']:
+            year = int(fields['year'])
+        if fields['month']:
+            month = int(fields['month'])
+        if fields['day']:
+            day = int(fields['day'])
+
+        if fields['year_day']:
+            d = DateTime('%s-01-01' % year) + int(fields['year_day']) - 1
+            month = d.month()
+            day = d.day()
+
+        if fields['week']:
+            week = int(fields['week'])
+            if fields['week_day']:
+                week_day = int(fields['week_day'])
+            d = DateTime('%s-01-04' % year)
+            d = d - (d.dow() + 6) % 7 + week * 7 + week_day - 8
+            month = d.month()
+            day = d.day()
+
+        if fields['hour']:
+            hour = int(fields['hour'])
+
+        if fields['minute']:
+            minute = int(fields['minute'])
+        elif fields['fraction']:
+            minute = 60.0 * float('0.%s' % fields['fraction'])
+            seconds, minute = math.modf(minute)
+            minute = int(minute)
+            seconds = 60.0 * seconds
+            # Avoid reprocess when handling seconds, bellow
+            fields['fraction'] = None
+
+        if fields['second']:
+            seconds = int(fields['second'])
+            if fields['fraction']:
+                seconds = seconds + float('0.%s' % fields['fraction'])
+        elif fields['fraction']:
+            seconds = 60.0 * float('0.%s' % fields['fraction'])
+
+        if fields['hour_off']:
+            hour_off = int(fields['hour_off'])
+            if fields['signal'] == '-':
+                hour_off *= -1
+
+        if fields['min_off']:
+            min_off = int(fields['min_off'])
+
+        if fields['signal'] or fields['Z']:
+            tznaive = False
+        else:
+            tznaive = True
+
+        # Differ from the specification here. To preserve backwards
+        # compatibility assume a default timezone == UTC.
+        tz = 'GMT%+03d%02d' % (hour_off, min_off)
+
+        return year, month, day, hour, minute, seconds, tz, tznaive
+
+    def JulianDay(self):
+        """Return the Julian day.
+
+        See: https://www.tondering.dk/claus/cal/julperiod.php#formula
+        """
+        a = (14 - self._month) // 12
+        y = self._year + 4800 - a
+        m = self._month + (12 * a) - 3
+        return (self._day + (153 * m + 2) // 5 + 365 * y +
+                y // 4 - y // 100 + y // 400 - 32045)
+
+    def week(self):
+        """Return the week number according to ISO.
+
+        See: https://www.tondering.dk/claus/cal/week.php#weekno
+        """
+        J = self.JulianDay()
+        d4 = (J + 31741 - (J % 7)) % 146097 % 36524 % 1461
+        L = d4 // 1460
+        d1 = ((d4 - L) % 365) + L
+        return d1 // 7 + 1
+
+    def encode(self, out):
+        """Encode value for XML-RPC."""
+        out.write('<value><dateTime.iso8601>')
+        out.write(self.ISO8601())
+        out.write('</dateTime.iso8601></value>\n')
+
+
+# Provide the _dt_reconstructor function here, in case something
+# accidentally creates a reference to this function
+
+orig_reconstructor = copy_reg._reconstructor
+
+
+def _dt_reconstructor(cls, base, state):
+    if cls is DateTime:
+        return cls(state)
+    return orig_reconstructor(cls, base, state)