mirror of
https://github.com/python/cpython.git
synced 2025-08-03 00:23:06 +00:00
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.
This commit is contained in:
parent
c6ea8974e2
commit
018d353c1c
5 changed files with 127 additions and 79 deletions
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue