Issue #5094: The ``datetime`` module now has a simple concrete class

implementing ``datetime.tzinfo`` interface.
This commit is contained in:
Alexander Belopolsky 2010-06-14 14:15:50 +00:00
parent 510b6227a7
commit 4e749a1113
5 changed files with 483 additions and 26 deletions

View File

@ -28,11 +28,14 @@ For applications requiring more, :class:`datetime` and :class:`time` objects
have an optional time zone information member, :attr:`tzinfo`, that can contain have an optional time zone information member, :attr:`tzinfo`, that can contain
an instance of a subclass of the abstract :class:`tzinfo` class. These an instance of a subclass of the abstract :class:`tzinfo` class. These
:class:`tzinfo` objects capture information about the offset from UTC time, the :class:`tzinfo` objects capture information about the offset from UTC time, the
time zone name, and whether Daylight Saving Time is in effect. Note that no time zone name, and whether Daylight Saving Time is in effect. Note that only
concrete :class:`tzinfo` classes are supplied by the :mod:`datetime` module. one concrete :class:`tzinfo` class, the :class:`timezone` class, is supplied by the
Supporting timezones at whatever level of detail is required is up to the :mod:`datetime` module. The :class:`timezone` class can reprsent simple
application. The rules for time adjustment across the world are more political timezones with fixed offset from UTC such as UTC itself or North American EST and
than rational, and there is no standard suitable for every application. EDT timezones. Supporting timezones at whatever level of detail is
required is up to the application. The rules for time adjustment across the
world are more political than rational, change frequently, and there is no
standard suitable for every application aside from UTC.
The :mod:`datetime` module exports the following constants: The :mod:`datetime` module exports the following constants:
@ -99,6 +102,14 @@ Available Types
time adjustment (for example, to account for time zone and/or daylight saving time adjustment (for example, to account for time zone and/or daylight saving
time). time).
.. class:: timezone
A class that implements the :class:`tzinfo` abstract base class as a
fixed offset from the UTC.
.. versionadded:: 3.2
Objects of these types are immutable. Objects of these types are immutable.
Objects of the :class:`date` type are always naive. Objects of the :class:`date` type are always naive.
@ -116,6 +127,7 @@ Subclass relationships::
object object
timedelta timedelta
tzinfo tzinfo
timezone
time time
date date
datetime datetime
@ -660,8 +672,8 @@ Other constructors, all class methods:
Return the current UTC date and time, with :attr:`tzinfo` ``None``. This is like Return the current UTC date and time, with :attr:`tzinfo` ``None``. This is like
:meth:`now`, but returns the current UTC date and time, as a naive :meth:`now`, but returns the current UTC date and time, as a naive
:class:`datetime` object. See also :meth:`now`. :class:`datetime` object. An aware current UTC datetime can be obtained by
calling ``datetime.now(timezone.utc)``. See also :meth:`now`.
.. classmethod:: datetime.fromtimestamp(timestamp, tz=None) .. classmethod:: datetime.fromtimestamp(timestamp, tz=None)
@ -1318,8 +1330,10 @@ Example:
:class:`tzinfo` is an abstract base class, meaning that this class should not be :class:`tzinfo` is an abstract base class, meaning that this class should not be
instantiated directly. You need to derive a concrete subclass, and (at least) instantiated directly. You need to derive a concrete subclass, and (at least)
supply implementations of the standard :class:`tzinfo` methods needed by the supply implementations of the standard :class:`tzinfo` methods needed by the
:class:`datetime` methods you use. The :mod:`datetime` module does not supply :class:`datetime` methods you use. The :mod:`datetime` module supplies
any concrete subclasses of :class:`tzinfo`. a simple concrete subclass of :class:`tzinfo` :class:`timezone` which can reprsent
timezones with fixed offset from UTC such as UTC itself or North American EST and
EDT.
An instance of (a concrete subclass of) :class:`tzinfo` can be passed to the An instance of (a concrete subclass of) :class:`tzinfo` can be passed to the
constructors for :class:`datetime` and :class:`time` objects. The latter objects constructors for :class:`datetime` and :class:`time` objects. The latter objects
@ -1520,9 +1534,65 @@ arranged, as in the example, by expressing DST switch times in the time zone's
standard local time. standard local time.
Applications that can't bear such ambiguities should avoid using hybrid Applications that can't bear such ambiguities should avoid using hybrid
:class:`tzinfo` subclasses; there are no ambiguities when using UTC, or any :class:`tzinfo` subclasses; there are no ambiguities when using :class:`timezone`,
other fixed-offset :class:`tzinfo` subclass (such as a class representing only or any other fixed-offset :class:`tzinfo` subclass (such as a class representing
EST (fixed offset -5 hours), or only EDT (fixed offset -4 hours)). only EST (fixed offset -5 hours), or only EDT (fixed offset -4 hours)).
.. _datetime-timezone:
:class:`timezone` Objects
--------------------------
A :class:`timezone` object represents a timezone that is defined by a
fixed offset from UTC. Note that objects of this class cannot be used
to represent timezone information in the locations where different
offsets are used in different days of the year or where historical
changes have been made to civil time.
.. class:: timezone(offset[, name])
The ``offset`` argument must be specified as a :class:`timedelta`
object representing the difference between the local time and UTC. It must
be within the range [``-timedelta(hours=23, minutes=59),
``timedelta(hours=23, minutes=59)``] and represent whole number of minutes,
otherwise :exc:`ValueError` is raised.
The ``name`` argument is optional. If specified it must be a string that
used as the value returned by the ``tzname(dt)`` method. Otherwise,
``tzname(dt)`` returns a string 'UTCsHH:MM', where s is the sign of
``offset``, HH and MM are two digits of ``offset.hours`` and
``offset.minutes`` respectively.
.. method:: timezone.utcoffset(self, dt)
Returns the fixed value specified when the :class:`timezone` instance is
constructed. The ``dt`` argument is ignored. The return value is a
:class:`timedelta` instance equal to the difference between the
local time and UTC.
.. method:: timezone.tzname(self, dt)
Returns the fixed value specified when the :class:`timezone` instance is
constructed or a string 'UTCsHH:MM', where s is the sign of
``offset``, HH and MM are two digits of ``offset.hours`` and
``offset.minutes`` respectively. The ``dt`` argument is ignored.
.. method:: timezone.dst(self, dt)
Always returns ``None``.
.. method:: timezone.fromutc(self, dt)
Returns ``dt + offset``. The ``dt`` argument must be aware with ``tzinfo``
set to ``self``.
Class attributes:
.. attribute:: timezone.utc
The UTC timezone, ``timezone(0, 'UTC')``.
.. _strftime-strptime-behavior: .. _strftime-strptime-behavior:

View File

@ -15,6 +15,7 @@ from datetime import MINYEAR, MAXYEAR
from datetime import timedelta from datetime import timedelta
from datetime import tzinfo from datetime import tzinfo
from datetime import time from datetime import time
from datetime import timezone
from datetime import date, datetime from datetime import date, datetime
pickle_choices = [(pickle, pickle, proto) for proto in range(3)] pickle_choices = [(pickle, pickle, proto) for proto in range(3)]
@ -49,6 +50,7 @@ class TestModule(unittest.TestCase):
# tzinfo tests # tzinfo tests
class FixedOffset(tzinfo): class FixedOffset(tzinfo):
def __init__(self, offset, name, dstoffset=42): def __init__(self, offset, name, dstoffset=42):
if isinstance(offset, int): if isinstance(offset, int):
offset = timedelta(minutes=offset) offset = timedelta(minutes=offset)
@ -67,6 +69,7 @@ class FixedOffset(tzinfo):
return self.__dstoffset return self.__dstoffset
class PicklableFixedOffset(FixedOffset): class PicklableFixedOffset(FixedOffset):
def __init__(self, offset=None, name=None, dstoffset=None): def __init__(self, offset=None, name=None, dstoffset=None):
FixedOffset.__init__(self, offset, name, dstoffset) FixedOffset.__init__(self, offset, name, dstoffset)
@ -131,6 +134,97 @@ class TestTZInfo(unittest.TestCase):
self.assertEqual(derived.utcoffset(None), offset) self.assertEqual(derived.utcoffset(None), offset)
self.assertEqual(derived.tzname(None), 'cookie') self.assertEqual(derived.tzname(None), 'cookie')
class TestTimeZone(unittest.TestCase):
def setUp(self):
self.ACDT = timezone(timedelta(hours=9.5), 'ACDT')
self.EST = timezone(-timedelta(hours=5), 'EST')
self.DT = datetime(2010, 1, 1)
def test_str(self):
for tz in [self.ACDT, self.EST, timezone.utc,
timezone.min, timezone.max]:
self.assertEqual(str(tz), tz.tzname(None))
def test_class_members(self):
limit = timedelta(hours=23, minutes=59)
self.assertEquals(timezone.utc.utcoffset(None), ZERO)
self.assertEquals(timezone.min.utcoffset(None), -limit)
self.assertEquals(timezone.max.utcoffset(None), limit)
def test_constructor(self):
self.assertEquals(timezone.utc, timezone(timedelta(0)))
# invalid offsets
for invalid in [timedelta(microseconds=1), timedelta(1, 1),
timedelta(seconds=1), timedelta(1), -timedelta(1)]:
self.assertRaises(ValueError, timezone, invalid)
self.assertRaises(ValueError, timezone, -invalid)
with self.assertRaises(TypeError): timezone(None)
with self.assertRaises(TypeError): timezone(42)
with self.assertRaises(TypeError): timezone(ZERO, None)
with self.assertRaises(TypeError): timezone(ZERO, 42)
def test_inheritance(self):
self.assertTrue(isinstance(timezone.utc, tzinfo))
self.assertTrue(isinstance(self.EST, tzinfo))
def test_utcoffset(self):
dummy = self.DT
for h in [0, 1.5, 12]:
offset = h * HOUR
self.assertEquals(offset, timezone(offset).utcoffset(dummy))
self.assertEquals(-offset, timezone(-offset).utcoffset(dummy))
with self.assertRaises(TypeError): self.EST.utcoffset('')
with self.assertRaises(TypeError): self.EST.utcoffset(5)
def test_dst(self):
self.assertEquals(None, timezone.utc.dst(self.DT))
with self.assertRaises(TypeError): self.EST.dst('')
with self.assertRaises(TypeError): self.EST.dst(5)
def test_tzname(self):
self.assertEquals('UTC+00:00', timezone(ZERO).tzname(None))
self.assertEquals('UTC-05:00', timezone(-5 * HOUR).tzname(None))
self.assertEquals('UTC+09:30', timezone(9.5 * HOUR).tzname(None))
self.assertEquals('UTC-00:01', timezone(timedelta(minutes=-1)).tzname(None))
self.assertEquals('XYZ', timezone(-5 * HOUR, 'XYZ').tzname(None))
with self.assertRaises(TypeError): self.EST.tzname('')
with self.assertRaises(TypeError): self.EST.tzname(5)
def test_fromutc(self):
with self.assertRaises(ValueError):
timezone.utc.fromutc(self.DT)
for tz in [self.EST, self.ACDT, Eastern]:
utctime = self.DT.replace(tzinfo=tz)
local = tz.fromutc(utctime)
self.assertEquals(local - utctime, tz.utcoffset(local))
self.assertEquals(local,
self.DT.replace(tzinfo=timezone.utc))
def test_comparison(self):
self.assertNotEqual(timezone(ZERO), timezone(HOUR))
self.assertEqual(timezone(HOUR), timezone(HOUR))
self.assertEqual(timezone(-5 * HOUR), timezone(-5 * HOUR, 'EST'))
with self.assertRaises(TypeError): timezone(ZERO) < timezone(ZERO)
self.assertIn(timezone(ZERO), {timezone(ZERO)})
def test_aware_datetime(self):
# test that timezone instances can be used by datetime
t = datetime(1, 1, 1)
for tz in [timezone.min, timezone.max, timezone.utc]:
self.assertEquals(tz.tzname(t),
t.replace(tzinfo=tz).tzname())
self.assertEquals(tz.utcoffset(t),
t.replace(tzinfo=tz).utcoffset())
self.assertEquals(tz.dst(t),
t.replace(tzinfo=tz).dst())
############################################################################# #############################################################################
# Base clase for testing a particular aspect of timedelta, time, date and # Base clase for testing a particular aspect of timedelta, time, date and
# datetime comparisons. # datetime comparisons.
@ -2729,20 +2823,21 @@ class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
# We don't know which time zone we're in, and don't have a tzinfo # We don't know which time zone we're in, and don't have a tzinfo
# class to represent it, so seeing whether a tz argument actually # class to represent it, so seeing whether a tz argument actually
# does a conversion is tricky. # does a conversion is tricky.
weirdtz = FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0)
utc = FixedOffset(0, "utc", 0) utc = FixedOffset(0, "utc", 0)
for dummy in range(3): for weirdtz in [FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0),
now = datetime.now(weirdtz) timezone(timedelta(hours=15, minutes=58), "weirdtz"),]:
self.assertTrue(now.tzinfo is weirdtz) for dummy in range(3):
utcnow = datetime.utcnow().replace(tzinfo=utc) now = datetime.now(weirdtz)
now2 = utcnow.astimezone(weirdtz) self.assertTrue(now.tzinfo is weirdtz)
if abs(now - now2) < timedelta(seconds=30): utcnow = datetime.utcnow().replace(tzinfo=utc)
break now2 = utcnow.astimezone(weirdtz)
# Else the code is broken, or more than 30 seconds passed between if abs(now - now2) < timedelta(seconds=30):
# calls; assuming the latter, just try again. break
else: # Else the code is broken, or more than 30 seconds passed between
# Three strikes and we're out. # calls; assuming the latter, just try again.
self.fail("utcnow(), now(tz), or astimezone() may be broken") else:
# Three strikes and we're out.
self.fail("utcnow(), now(tz), or astimezone() may be broken")
def test_tzinfo_fromtimestamp(self): def test_tzinfo_fromtimestamp(self):
import time import time

View File

@ -406,6 +406,7 @@ Bob Kahn
Kurt B. Kaiser Kurt B. Kaiser
Tamito Kajiyama Tamito Kajiyama
Peter van Kampen Peter van Kampen
Rafe Kaplan
Jacob Kaplan-Moss Jacob Kaplan-Moss
Lou Kates Lou Kates
Hiroaki Kawai Hiroaki Kawai

View File

@ -1306,6 +1306,14 @@ Library
Extension Modules Extension Modules
----------------- -----------------
- Issue #5094: The ``datetime`` module now has a simple concrete class
implementing ``datetime.tzinfo`` interface. Instances of the new
class, ``datetime.timezone``, return fixed name and UTC offset from
their ``tzname(dt)`` and ``utcoffset(dt)`` methods. The ``dst(dt)``
method always returns ``None``. A class attribute, ``utc`` contains
an instance representing the UTC timezone. Original patch by Rafe
Kaplan.
- Issue #8973: Add __all__ to struct module; this ensures that - Issue #8973: Add __all__ to struct module; this ensures that
help(struct) includes documentation for the struct.Struct class. help(struct) includes documentation for the struct.Struct class.

View File

@ -102,6 +102,7 @@ static PyTypeObject PyDateTime_DateTimeType;
static PyTypeObject PyDateTime_DeltaType; static PyTypeObject PyDateTime_DeltaType;
static PyTypeObject PyDateTime_TimeType; static PyTypeObject PyDateTime_TimeType;
static PyTypeObject PyDateTime_TZInfoType; static PyTypeObject PyDateTime_TZInfoType;
static PyTypeObject PyDateTime_TimeZoneType;
/* --------------------------------------------------------------------------- /* ---------------------------------------------------------------------------
* Math utilities. * Math utilities.
@ -771,6 +772,52 @@ new_delta_ex(int days, int seconds, int microseconds, int normalize,
#define new_delta(d, s, us, normalize) \ #define new_delta(d, s, us, normalize) \
new_delta_ex(d, s, us, normalize, &PyDateTime_DeltaType) new_delta_ex(d, s, us, normalize, &PyDateTime_DeltaType)
typedef struct
{
PyObject_HEAD
PyObject *offset;
PyObject *name;
} PyDateTime_TimeZone;
/* Create new timezone instance checking offset range. This
function does not check the name argument. Caller must assure
that offset is a timedelta instance and name is either NULL
or a unicode object. */
static PyObject *
new_timezone(PyObject *offset, PyObject *name)
{
PyDateTime_TimeZone *self;
PyTypeObject *type = &PyDateTime_TimeZoneType;
assert(offset != NULL);
assert(PyDelta_Check(offset));
assert(name == NULL || PyUnicode_Check(name));
if (GET_TD_MICROSECONDS(offset) != 0 || GET_TD_SECONDS(offset) % 60 != 0) {
PyErr_Format(PyExc_ValueError, "offset must be a timedelta"
" representing a whole number of minutes");
return NULL;
}
if ((GET_TD_DAYS(offset) == -1 && GET_TD_SECONDS(offset) == 0) ||
GET_TD_DAYS(offset) < -1 || GET_TD_DAYS(offset) >= 1) {
PyErr_Format(PyExc_ValueError, "offset must be a timedelta"
" strictly between -timedelta(hours=24) and"
" timedelta(hours=24).");
return NULL;
}
self = (PyDateTime_TimeZone *)(type->tp_alloc(type, 0));
if (self == NULL) {
return NULL;
}
Py_INCREF(offset);
self->offset = offset;
Py_XINCREF(name);
self->name = name;
return (PyObject *)self;
}
/* --------------------------------------------------------------------------- /* ---------------------------------------------------------------------------
* tzinfo helpers. * tzinfo helpers.
*/ */
@ -3261,7 +3308,7 @@ static PyTypeObject PyDateTime_TZInfoType = {
PyObject_GenericGetAttr, /* tp_getattro */ PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */ 0, /* tp_setattro */
0, /* tp_as_buffer */ 0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
tzinfo_doc, /* tp_doc */ tzinfo_doc, /* tp_doc */
0, /* tp_traverse */ 0, /* tp_traverse */
0, /* tp_clear */ 0, /* tp_clear */
@ -3283,6 +3330,206 @@ static PyTypeObject PyDateTime_TZInfoType = {
0, /* tp_free */ 0, /* tp_free */
}; };
static char *timezone_kws[] = {"offset", "name", NULL};
static PyObject *
timezone_new(PyTypeObject *type, PyObject *args, PyObject *kw)
{
PyObject *offset;
PyObject *name = NULL;
if (PyArg_ParseTupleAndKeywords(args, kw, "O!|O!:timezone", timezone_kws,
&PyDateTime_DeltaType, &offset,
&PyUnicode_Type, &name))
return new_timezone(offset, name);
return NULL;
}
static void
timezone_dealloc(PyDateTime_TimeZone *self)
{
Py_CLEAR(self->offset);
Py_CLEAR(self->name);
Py_TYPE(self)->tp_free((PyObject *)self);
}
static PyObject *
timezone_richcompare(PyDateTime_TimeZone *self,
PyDateTime_TimeZone *other, int op)
{
if (op != Py_EQ && op != Py_NE) {
Py_INCREF(Py_NotImplemented);
return Py_NotImplemented;
}
return delta_richcompare(self->offset, other->offset, op);
}
static long
timezone_hash(PyDateTime_TimeZone *self)
{
return delta_hash((PyDateTime_Delta *)self->offset);
}
/* Check argument type passed to tzname, utcoffset, or dst methods.
Returns 0 for good argument. Returns -1 and sets exception info
otherwise.
*/
static int
_timezone_check_argument(PyObject *dt, const char *meth)
{
if (dt == Py_None || PyDateTime_Check(dt))
return 0;
PyErr_Format(PyExc_TypeError, "%s(dt) argument must be a datetime instance"
" or None, not %.200s", meth, Py_TYPE(dt)->tp_name);
return -1;
}
static PyObject *
timezone_str(PyDateTime_TimeZone *self)
{
char buf[10];
int hours, minutes, seconds;
PyObject *offset;
char sign;
if (self->name != NULL) {
Py_INCREF(self->name);
return self->name;
}
/* Offset is normalized, so it is negative if days < 0 */
if (GET_TD_DAYS(self->offset) < 0) {
sign = '-';
offset = delta_negative((PyDateTime_Delta *)self->offset);
if (offset == NULL)
return NULL;
}
else {
sign = '+';
offset = self->offset;
Py_INCREF(offset);
}
/* Offset is not negative here. */
seconds = GET_TD_SECONDS(offset);
Py_DECREF(offset);
minutes = divmod(seconds, 60, &seconds);
hours = divmod(minutes, 60, &minutes);
assert(seconds == 0);
/* XXX ignore sub-minute data, curently not allowed. */
PyOS_snprintf(buf, sizeof(buf), "UTC%c%02d:%02d", sign, hours, minutes);
return PyUnicode_FromString(buf);
}
static PyObject *
timezone_tzname(PyDateTime_TimeZone *self, PyObject *dt)
{
if (_timezone_check_argument(dt, "tzname") == -1)
return NULL;
return timezone_str(self);
}
static PyObject *
timezone_utcoffset(PyDateTime_TimeZone *self, PyObject *dt)
{
if (_timezone_check_argument(dt, "utcoffset") == -1)
return NULL;
Py_INCREF(self->offset);
return self->offset;
}
static PyObject *
timezone_dst(PyObject *self, PyObject *dt)
{
if (_timezone_check_argument(dt, "dst") == -1)
return NULL;
Py_RETURN_NONE;
}
static PyObject *
add_datetime_timedelta(PyDateTime_DateTime *date, PyDateTime_Delta *delta,
int factor);
static PyObject *
timezone_fromutc(PyDateTime_TimeZone *self, PyDateTime_DateTime *dt)
{
if (! PyDateTime_Check(dt)) {
PyErr_SetString(PyExc_TypeError,
"fromutc: argument must be a datetime");
return NULL;
}
if (! HASTZINFO(dt) || dt->tzinfo != (PyObject *)self) {
PyErr_SetString(PyExc_ValueError, "fromutc: dt.tzinfo "
"is not self");
return NULL;
}
return add_datetime_timedelta(dt, (PyDateTime_Delta *)self->offset, 1);
}
static PyMethodDef timezone_methods[] = {
{"tzname", (PyCFunction)timezone_tzname, METH_O,
PyDoc_STR("If name is specified when timezone is created, returns the name."
" Otherwise returns offset as 'UTC(+|-)HHMM'.")},
{"utcoffset", (PyCFunction)timezone_utcoffset, METH_O,
PyDoc_STR("Returns fixed offset. Ignores its argument.")},
{"dst", (PyCFunction)timezone_dst, METH_O,
PyDoc_STR("Returns None. Ignores its argument.")},
{"fromutc", (PyCFunction)timezone_fromutc, METH_O,
PyDoc_STR("datetime in UTC -> datetime in local time.")},
{NULL, NULL}
};
static char timezone_doc[] =
PyDoc_STR("Fixed offset from UTC implementation of tzinfo.");
static PyTypeObject PyDateTime_TimeZoneType = {
PyVarObject_HEAD_INIT(NULL, 0)
"datetime.timezone", /* tp_name */
sizeof(PyDateTime_TimeZone), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)timezone_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
(hashfunc)timezone_hash, /* tp_hash */
0, /* tp_call */
(reprfunc)timezone_str, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
timezone_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
(richcmpfunc)timezone_richcompare,/* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
timezone_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
&PyDateTime_TZInfoType, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
timezone_new, /* tp_new */
};
/* /*
* PyDateTime_Time implementation. * PyDateTime_Time implementation.
*/ */
@ -4971,6 +5218,7 @@ PyInit_datetime(void)
PyObject *m; /* a module object */ PyObject *m; /* a module object */
PyObject *d; /* its dict */ PyObject *d; /* its dict */
PyObject *x; PyObject *x;
PyObject *delta;
m = PyModule_Create(&datetimemodule); m = PyModule_Create(&datetimemodule);
if (m == NULL) if (m == NULL)
@ -4986,6 +5234,8 @@ PyInit_datetime(void)
return NULL; return NULL;
if (PyType_Ready(&PyDateTime_TZInfoType) < 0) if (PyType_Ready(&PyDateTime_TZInfoType) < 0)
return NULL; return NULL;
if (PyType_Ready(&PyDateTime_TimeZoneType) < 0)
return NULL;
/* timedelta values */ /* timedelta values */
d = PyDateTime_DeltaType.tp_dict; d = PyDateTime_DeltaType.tp_dict;
@ -5059,6 +5309,36 @@ PyInit_datetime(void)
return NULL; return NULL;
Py_DECREF(x); Py_DECREF(x);
/* timezone values */
d = PyDateTime_TimeZoneType.tp_dict;
delta = new_delta(0, 0, 0, 0);
if (delta == NULL)
return NULL;
x = new_timezone(delta, NULL);
Py_DECREF(delta);
if (x == NULL || PyDict_SetItemString(d, "utc", x) < 0)
return NULL;
Py_DECREF(x);
delta = new_delta(-1, 60, 0, 1); /* -23:59 */
if (delta == NULL)
return NULL;
x = new_timezone(delta, NULL);
Py_DECREF(delta);
if (x == NULL || PyDict_SetItemString(d, "min", x) < 0)
return NULL;
Py_DECREF(x);
delta = new_delta(0, (23 * 60 + 59) * 60, 0, 0); /* +23:59 */
if (delta == NULL)
return NULL;
x = new_timezone(delta, NULL);
Py_DECREF(delta);
if (x == NULL || PyDict_SetItemString(d, "max", x) < 0)
return NULL;
Py_DECREF(x);
/* module initialization */ /* module initialization */
PyModule_AddIntConstant(m, "MINYEAR", MINYEAR); PyModule_AddIntConstant(m, "MINYEAR", MINYEAR);
PyModule_AddIntConstant(m, "MAXYEAR", MAXYEAR); PyModule_AddIntConstant(m, "MAXYEAR", MAXYEAR);
@ -5079,6 +5359,9 @@ PyInit_datetime(void)
Py_INCREF(&PyDateTime_TZInfoType); Py_INCREF(&PyDateTime_TZInfoType);
PyModule_AddObject(m, "tzinfo", (PyObject *) &PyDateTime_TZInfoType); PyModule_AddObject(m, "tzinfo", (PyObject *) &PyDateTime_TZInfoType);
Py_INCREF(&PyDateTime_TimeZoneType);
PyModule_AddObject(m, "timezone", (PyObject *) &PyDateTime_TimeZoneType);
x = PyCapsule_New(&CAPI, PyDateTime_CAPSULE_NAME, NULL); x = PyCapsule_New(&CAPI, PyDateTime_CAPSULE_NAME, NULL);
if (x == NULL) if (x == NULL)
return NULL; return NULL;