summaryrefslogtreecommitdiffstats
path: root/Modules/datetimemodule.c
diff options
context:
space:
mode:
authorAlexander Belopolsky <alexander.belopolsky@gmail.com>2010-06-14 14:15:50 (GMT)
committerAlexander Belopolsky <alexander.belopolsky@gmail.com>2010-06-14 14:15:50 (GMT)
commit4e749a1113acb4affed27afda07e17d96945290b (patch)
tree77f5847390e81b47a0df49d4899d759892a437cb /Modules/datetimemodule.c
parent510b6227a764e6de23e48708cd3e1293ec2521be (diff)
downloadcpython-4e749a1113acb4affed27afda07e17d96945290b.zip
cpython-4e749a1113acb4affed27afda07e17d96945290b.tar.gz
cpython-4e749a1113acb4affed27afda07e17d96945290b.tar.bz2
Issue #5094: The ``datetime`` module now has a simple concrete class
implementing ``datetime.tzinfo`` interface.
Diffstat (limited to 'Modules/datetimemodule.c')
-rw-r--r--Modules/datetimemodule.c285
1 files changed, 284 insertions, 1 deletions
diff --git a/Modules/datetimemodule.c b/Modules/datetimemodule.c
index 30816ed..de9db6b 100644
--- a/Modules/datetimemodule.c
+++ b/Modules/datetimemodule.c
@@ -102,6 +102,7 @@ static PyTypeObject PyDateTime_DateTimeType;
static PyTypeObject PyDateTime_DeltaType;
static PyTypeObject PyDateTime_TimeType;
static PyTypeObject PyDateTime_TZInfoType;
+static PyTypeObject PyDateTime_TimeZoneType;
/* ---------------------------------------------------------------------------
* Math utilities.
@@ -771,6 +772,52 @@ new_delta_ex(int days, int seconds, int microseconds, int normalize,
#define new_delta(d, s, us, normalize) \
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.
*/
@@ -3261,7 +3308,7 @@ static PyTypeObject PyDateTime_TZInfoType = {
PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
tzinfo_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
@@ -3283,6 +3330,206 @@ static PyTypeObject PyDateTime_TZInfoType = {
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.
*/
@@ -4971,6 +5218,7 @@ PyInit_datetime(void)
PyObject *m; /* a module object */
PyObject *d; /* its dict */
PyObject *x;
+ PyObject *delta;
m = PyModule_Create(&datetimemodule);
if (m == NULL)
@@ -4986,6 +5234,8 @@ PyInit_datetime(void)
return NULL;
if (PyType_Ready(&PyDateTime_TZInfoType) < 0)
return NULL;
+ if (PyType_Ready(&PyDateTime_TimeZoneType) < 0)
+ return NULL;
/* timedelta values */
d = PyDateTime_DeltaType.tp_dict;
@@ -5059,6 +5309,36 @@ PyInit_datetime(void)
return NULL;
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 */
PyModule_AddIntConstant(m, "MINYEAR", MINYEAR);
PyModule_AddIntConstant(m, "MAXYEAR", MAXYEAR);
@@ -5079,6 +5359,9 @@ PyInit_datetime(void)
Py_INCREF(&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);
if (x == NULL)
return NULL;