Issue #5094: The ``datetime`` module now has a simple concrete class
implementing ``datetime.tzinfo`` interface.
This commit is contained in:
parent
510b6227a7
commit
4e749a1113
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue