diff options
author | Alexander Belopolsky <abalkin@users.noreply.github.com> | 2017-07-31 14:26:50 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-07-31 14:26:50 (GMT) |
commit | 018d353c1c8c87767d2335cd884017c2ce12e045 (patch) | |
tree | 36a485b724114e393901f3fa16d741cc63bd7cb2 /Modules/_datetimemodule.c | |
parent | c6ea8974e2d939223bfd6d64ee13ec89c090d2e0 (diff) | |
download | cpython-018d353c1c8c87767d2335cd884017c2ce12e045.zip cpython-018d353c1c8c87767d2335cd884017c2ce12e045.tar.gz cpython-018d353c1c8c87767d2335cd884017c2ce12e045.tar.bz2 |
Closes issue bpo-5288: Allow tzinfo objects with sub-minute offsets. (#2896)
* Closes issue bpo-5288: Allow tzinfo objects with sub-minute offsets.
* bpo-5288: Implemented %z formatting of sub-minute offsets.
* bpo-5288: Removed mentions of the whole minute limitation on TZ offsets.
* bpo-5288: Removed one more mention of the whole minute limitation.
Thanks @csabella!
* Fix a formatting error in the docs
* Addressed review comments.
Thanks, @haypo.
Diffstat (limited to 'Modules/_datetimemodule.c')
-rw-r--r-- | Modules/_datetimemodule.c | 57 |
1 files changed, 30 insertions, 27 deletions
diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 28805d1..1b68ff3 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -859,12 +859,6 @@ new_timezone(PyObject *offset, PyObject *name) Py_INCREF(PyDateTime_TimeZone_UTC); return PyDateTime_TimeZone_UTC; } - 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," - " not %R.", offset); - 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" @@ -935,12 +929,6 @@ call_tzinfo_method(PyObject *tzinfo, const char *name, PyObject *tzinfoarg) if (offset == Py_None || offset == NULL) return offset; if (PyDelta_Check(offset)) { - if (GET_TD_MICROSECONDS(offset) != 0) { - Py_DECREF(offset); - PyErr_Format(PyExc_ValueError, "offset must be a timedelta" - " representing a whole number of seconds"); - return NULL; - } if ((GET_TD_DAYS(offset) == -1 && GET_TD_SECONDS(offset) == 0) || GET_TD_DAYS(offset) < -1 || GET_TD_DAYS(offset) >= 1) { Py_DECREF(offset); @@ -966,9 +954,9 @@ call_tzinfo_method(PyObject *tzinfo, const char *name, PyObject *tzinfoarg) * result. tzinfo must be an instance of the tzinfo class. If utcoffset() * returns None, call_utcoffset returns 0 and sets *none to 1. If uctoffset() * doesn't return None or timedelta, TypeError is raised and this returns -1. - * If utcoffset() returns an invalid timedelta (out of range, or not a whole - * # of minutes), ValueError is raised and this returns -1. Else *none is - * set to 0 and the offset is returned (as int # of minutes east of UTC). + * If utcoffset() returns an out of range timedelta, + * ValueError is raised and this returns -1. Else *none is + * set to 0 and the offset is returned (as timedelta, positive east of UTC). */ static PyObject * call_utcoffset(PyObject *tzinfo, PyObject *tzinfoarg) @@ -979,10 +967,10 @@ call_utcoffset(PyObject *tzinfo, PyObject *tzinfoarg) /* Call tzinfo.dst(tzinfoarg), and extract an integer from the * result. tzinfo must be an instance of the tzinfo class. If dst() * returns None, call_dst returns 0 and sets *none to 1. If dst() - & doesn't return None or timedelta, TypeError is raised and this + * doesn't return None or timedelta, TypeError is raised and this * returns -1. If dst() returns an invalid timedelta for a UTC offset, * ValueError is raised and this returns -1. Else *none is set to 0 and - * the offset is returned (as an int # of minutes east of UTC). + * the offset is returned (as timedelta, positive east of UTC). */ static PyObject * call_dst(PyObject *tzinfo, PyObject *tzinfoarg) @@ -1100,13 +1088,13 @@ format_ctime(PyDateTime_Date *date, int hours, int minutes, int seconds) static PyObject *delta_negative(PyDateTime_Delta *self); -/* Add an hours & minutes UTC offset string to buf. buf has no more than +/* Add formatted UTC offset string to buf. buf has no more than * buflen bytes remaining. The UTC offset is gotten by calling * tzinfo.uctoffset(tzinfoarg). If that returns None, \0 is stored into * *buf, and that's all. Else the returned value is checked for sanity (an * integer in range), and if that's OK it's converted to an hours & minutes * string of the form - * sign HH sep MM + * sign HH sep MM [sep SS [. UUUUUU]] * Returns 0 if everything is OK. If the return value from utcoffset() is * bogus, an appropriate exception is set and -1 is returned. */ @@ -1115,7 +1103,7 @@ format_utcoffset(char *buf, size_t buflen, const char *sep, PyObject *tzinfo, PyObject *tzinfoarg) { PyObject *offset; - int hours, minutes, seconds; + int hours, minutes, seconds, microseconds; char sign; assert(buflen >= 1); @@ -1139,15 +1127,22 @@ format_utcoffset(char *buf, size_t buflen, const char *sep, sign = '+'; } /* Offset is not negative here. */ + microseconds = GET_TD_MICROSECONDS(offset); seconds = GET_TD_SECONDS(offset); Py_DECREF(offset); minutes = divmod(seconds, 60, &seconds); hours = divmod(minutes, 60, &minutes); - if (seconds == 0) - PyOS_snprintf(buf, buflen, "%c%02d%s%02d", sign, hours, sep, minutes); - else + if (microseconds) { + PyOS_snprintf(buf, buflen, "%c%02d%s%02d%s%02d.%06d", sign, + hours, sep, minutes, sep, seconds, microseconds); + return 0; + } + if (seconds) { PyOS_snprintf(buf, buflen, "%c%02d%s%02d%s%02d", sign, hours, sep, minutes, sep, seconds); + return 0; + } + PyOS_snprintf(buf, buflen, "%c%02d%s%02d", sign, hours, sep, minutes); return 0; } @@ -3241,7 +3236,7 @@ static PyMethodDef tzinfo_methods[] = { "values indicating West of UTC")}, {"dst", (PyCFunction)tzinfo_dst, METH_O, - PyDoc_STR("datetime -> DST offset in minutes east of UTC.")}, + PyDoc_STR("datetime -> DST offset as timedelta positive east of UTC.")}, {"fromutc", (PyCFunction)tzinfo_fromutc, METH_O, PyDoc_STR("datetime in UTC -> datetime in local time.")}, @@ -3375,7 +3370,7 @@ timezone_repr(PyDateTime_TimeZone *self) static PyObject * timezone_str(PyDateTime_TimeZone *self) { - int hours, minutes, seconds; + int hours, minutes, seconds, microseconds; PyObject *offset; char sign; @@ -3401,12 +3396,20 @@ timezone_str(PyDateTime_TimeZone *self) Py_INCREF(offset); } /* Offset is not negative here. */ + microseconds = GET_TD_MICROSECONDS(offset); seconds = GET_TD_SECONDS(offset); Py_DECREF(offset); minutes = divmod(seconds, 60, &seconds); hours = divmod(minutes, 60, &minutes); - /* XXX ignore sub-minute data, currently not allowed. */ - assert(seconds == 0); + if (microseconds != 0) { + return PyUnicode_FromFormat("UTC%c%02d:%02d:%02d.%06d", + sign, hours, minutes, + seconds, microseconds); + } + if (seconds != 0) { + return PyUnicode_FromFormat("UTC%c%02d:%02d:%02d", + sign, hours, minutes, seconds); + } return PyUnicode_FromFormat("UTC%c%02d:%02d", sign, hours, minutes); } |