diff options
Diffstat (limited to 'Modules/_datetimemodule.c')
-rw-r--r-- | Modules/_datetimemodule.c | 255 |
1 files changed, 131 insertions, 124 deletions
diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 0e50023..261c4bc 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -184,12 +184,12 @@ divide_nearest(PyObject *m, PyObject *n) * and the number of days before that month in the same year. These * are correct for non-leap years only. */ -static int _days_in_month[] = { +static const int _days_in_month[] = { 0, /* unused; this vector uses 1-based indexing */ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; -static int _days_before_month[] = { +static const int _days_before_month[] = { 0, /* unused; this vector uses 1-based indexing */ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; @@ -873,7 +873,7 @@ get_tzinfo_member(PyObject *self) * this returns NULL. Else result is returned. */ static PyObject * -call_tzinfo_method(PyObject *tzinfo, char *name, PyObject *tzinfoarg) +call_tzinfo_method(PyObject *tzinfo, const char *name, PyObject *tzinfoarg) { PyObject *offset; @@ -1009,10 +1009,10 @@ append_keyword_tzinfo(PyObject *repr, PyObject *tzinfo) static PyObject * format_ctime(PyDateTime_Date *date, int hours, int minutes, int seconds) { - static const char *DayNames[] = { + static const char * const DayNames[] = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" }; - static const char *MonthNames[] = { + static const char * const MonthNames[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; @@ -1057,10 +1057,8 @@ format_utcoffset(char *buf, size_t buflen, const char *sep, } /* Offset is normalized, so it is negative if days < 0 */ if (GET_TD_DAYS(offset) < 0) { - PyObject *temp = offset; sign = '-'; - offset = delta_negative((PyDateTime_Delta *)offset); - Py_DECREF(temp); + Py_SETREF(offset, delta_negative((PyDateTime_Delta *)offset)); if (offset == NULL) return -1; } @@ -2307,7 +2305,7 @@ static PyMethodDef delta_methods[] = { {NULL, NULL}, }; -static char delta_doc[] = +static const char delta_doc[] = PyDoc_STR("Difference between two datetime values."); static PyNumberMethods delta_as_number = { @@ -2886,7 +2884,7 @@ static PyMethodDef date_methods[] = { {NULL, NULL} }; -static char date_doc[] = +static const char date_doc[] = PyDoc_STR("date(year, month, day) --> date object"); static PyNumberMethods date_as_number = { @@ -3047,10 +3045,8 @@ tzinfo_fromutc(PyDateTime_TZInfo *self, PyObject *dt) if (dst == Py_None) goto Inconsistent; if (delta_bool((PyDateTime_Delta *)dst) != 0) { - PyObject *temp = result; - result = add_datetime_timedelta((PyDateTime_DateTime *)result, - (PyDateTime_Delta *)dst, 1); - Py_DECREF(temp); + Py_SETREF(result, add_datetime_timedelta((PyDateTime_DateTime *)result, + (PyDateTime_Delta *)dst, 1)); if (result == NULL) goto Fail; } @@ -3155,7 +3151,7 @@ static PyMethodDef tzinfo_methods[] = { {NULL, NULL} }; -static char tzinfo_doc[] = +static const char tzinfo_doc[] = PyDoc_STR("Abstract base class for time zone info objects."); static PyTypeObject PyDateTime_TZInfoType = { @@ -3287,6 +3283,11 @@ timezone_str(PyDateTime_TimeZone *self) Py_INCREF(self->name); return self->name; } + if ((PyObject *)self == PyDateTime_TimeZone_UTC || + (GET_TD_DAYS(self->offset) == 0 && + GET_TD_SECONDS(self->offset) == 0 && + GET_TD_MICROSECONDS(self->offset) == 0)) + return PyUnicode_FromString("UTC"); /* Offset is normalized, so it is negative if days < 0 */ if (GET_TD_DAYS(self->offset) < 0) { sign = '-'; @@ -3382,7 +3383,7 @@ static PyMethodDef timezone_methods[] = { {NULL, NULL} }; -static char timezone_doc[] = +static const char timezone_doc[] = PyDoc_STR("Fixed offset from UTC implementation of tzinfo."); static PyTypeObject PyDateTime_TimeZoneType = { @@ -3607,23 +3608,56 @@ time_str(PyDateTime_Time *self) } static PyObject * -time_isoformat(PyDateTime_Time *self, PyObject *unused) +time_isoformat(PyDateTime_Time *self, PyObject *args, PyObject *kw) { char buf[100]; + char *timespec = NULL; + static char *keywords[] = {"timespec", NULL}; PyObject *result; int us = TIME_GET_MICROSECOND(self); + static char *specs[][2] = { + {"hours", "%02d"}, + {"minutes", "%02d:%02d"}, + {"seconds", "%02d:%02d:%02d"}, + {"milliseconds", "%02d:%02d:%02d.%03d"}, + {"microseconds", "%02d:%02d:%02d.%06d"}, + }; + size_t given_spec; - if (us) - result = PyUnicode_FromFormat("%02d:%02d:%02d.%06d", - TIME_GET_HOUR(self), - TIME_GET_MINUTE(self), - TIME_GET_SECOND(self), - us); - else - result = PyUnicode_FromFormat("%02d:%02d:%02d", - TIME_GET_HOUR(self), - TIME_GET_MINUTE(self), - TIME_GET_SECOND(self)); + if (!PyArg_ParseTupleAndKeywords(args, kw, "|s:isoformat", keywords, ×pec)) + return NULL; + + if (timespec == NULL || strcmp(timespec, "auto") == 0) { + if (us == 0) { + /* seconds */ + given_spec = 2; + } + else { + /* microseconds */ + given_spec = 4; + } + } + else { + for (given_spec = 0; given_spec < Py_ARRAY_LENGTH(specs); given_spec++) { + if (strcmp(timespec, specs[given_spec][0]) == 0) { + if (given_spec == 3) { + /* milliseconds */ + us = us / 1000; + } + break; + } + } + } + + if (given_spec == Py_ARRAY_LENGTH(specs)) { + PyErr_Format(PyExc_ValueError, "Unknown timespec value"); + return NULL; + } + else { + result = PyUnicode_FromFormat(specs[given_spec][1], + TIME_GET_HOUR(self), TIME_GET_MINUTE(self), + TIME_GET_SECOND(self), us); + } if (result == NULL || !HASTZINFO(self) || self->tzinfo == Py_None) return result; @@ -3844,9 +3878,10 @@ time_reduce(PyDateTime_Time *self, PyObject *arg) static PyMethodDef time_methods[] = { - {"isoformat", (PyCFunction)time_isoformat, METH_NOARGS, - PyDoc_STR("Return string in ISO 8601 format, HH:MM:SS[.mmmmmm]" - "[+HH:MM].")}, + {"isoformat", (PyCFunction)time_isoformat, METH_VARARGS | METH_KEYWORDS, + PyDoc_STR("Return string in ISO 8601 format, [HH[:MM[:SS[.mmm[uuu]]]]]" + "[+HH:MM].\n\n" + "timespec specifies what components of the time to include.\n")}, {"strftime", (PyCFunction)time_strftime, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("format -> strftime() style string.")}, @@ -3872,7 +3907,7 @@ static PyMethodDef time_methods[] = { {NULL, NULL} }; -static char time_doc[] = +static const char time_doc[] = PyDoc_STR("time([hour[, minute[, second[, microsecond[, tzinfo]]]]]) --> a time object\n\ \n\ All arguments are optional. tzinfo may be None, or an instance of\n\ @@ -4083,44 +4118,6 @@ datetime_from_timet_and_us(PyObject *cls, TM_FUNC f, time_t timet, int us, tzinfo); } -static time_t -_PyTime_DoubleToTimet(double x) -{ - time_t result; - double diff; - - result = (time_t)x; - /* How much info did we lose? time_t may be an integral or - * floating type, and we don't know which. If it's integral, - * we don't know whether C truncates, rounds, returns the floor, - * etc. If we lost a second or more, the C rounding is - * unreasonable, or the input just doesn't fit in a time_t; - * call it an error regardless. Note that the original cast to - * time_t can cause a C error too, but nothing we can do to - * worm around that. - */ - diff = x - (double)result; - if (diff <= -1.0 || diff >= 1.0) { - PyErr_SetString(PyExc_OverflowError, - "timestamp out of range for platform time_t"); - result = (time_t)-1; - } - return result; -} - -/* Round a double to the nearest long. |x| must be small enough to fit - * in a C long; this is not checked. - */ -static double -_PyTime_RoundHalfEven(double x) -{ - double rounded = round(x); - if (fabs(x-rounded) == 0.5) - /* halfway case: round to even */ - rounded = 2.0*round(x/2.0); - return rounded; -} - /* Internal helper. * Build datetime from a Python timestamp. Pass localtime or gmtime for f, * to control the interpretation of the timestamp. Since a double doesn't @@ -4129,32 +4126,17 @@ _PyTime_RoundHalfEven(double x) * to get that much precision (e.g., C time() isn't good enough). */ static PyObject * -datetime_from_timestamp(PyObject *cls, TM_FUNC f, double timestamp, +datetime_from_timestamp(PyObject *cls, TM_FUNC f, PyObject *timestamp, PyObject *tzinfo) { time_t timet; - double fraction; - int us; + long us; - timet = _PyTime_DoubleToTimet(timestamp); - if (timet == (time_t)-1 && PyErr_Occurred()) + if (_PyTime_ObjectToTimeval(timestamp, + &timet, &us, _PyTime_ROUND_HALF_EVEN) == -1) return NULL; - fraction = timestamp - (double)timet; - us = (int)_PyTime_RoundHalfEven(fraction * 1e6); - if (us < 0) { - /* Truncation towards zero is not what we wanted - for negative numbers (Python's mod semantics) */ - timet -= 1; - us += 1000000; - } - /* If timestamp is less than one microsecond smaller than a - * full second, round up. Otherwise, ValueErrors are raised - * for some floats. */ - if (us == 1000000) { - timet += 1; - us = 0; - } - return datetime_from_timet_and_us(cls, f, timet, us, tzinfo); + + return datetime_from_timet_and_us(cls, f, timet, (int)us, tzinfo); } /* Internal helper. @@ -4205,10 +4187,7 @@ datetime_datetime_now_impl(PyTypeObject *type, PyObject *tz) tz); if (self != NULL && tz != Py_None) { /* Convert UTC to tzinfo's zone. */ - PyObject *temp = self; - - self = _PyObject_CallMethodId(tz, &PyId_fromutc, "O", self); - Py_DECREF(temp); + self = _PyObject_CallMethodId(tz, &PyId_fromutc, "N", self); } return self; } @@ -4227,11 +4206,11 @@ static PyObject * datetime_fromtimestamp(PyObject *cls, PyObject *args, PyObject *kw) { PyObject *self; - double timestamp; + PyObject *timestamp; PyObject *tzinfo = Py_None; static char *keywords[] = {"timestamp", "tz", NULL}; - if (! PyArg_ParseTupleAndKeywords(args, kw, "d|O:fromtimestamp", + if (! PyArg_ParseTupleAndKeywords(args, kw, "O|O:fromtimestamp", keywords, ×tamp, &tzinfo)) return NULL; if (check_tzinfo_subclass(tzinfo) < 0) @@ -4243,10 +4222,7 @@ datetime_fromtimestamp(PyObject *cls, PyObject *args, PyObject *kw) tzinfo); if (self != NULL && tzinfo != Py_None) { /* Convert UTC to tzinfo's zone. */ - PyObject *temp = self; - - self = _PyObject_CallMethodId(tzinfo, &PyId_fromutc, "O", self); - Py_DECREF(temp); + self = _PyObject_CallMethodId(tzinfo, &PyId_fromutc, "N", self); } return self; } @@ -4255,10 +4231,10 @@ datetime_fromtimestamp(PyObject *cls, PyObject *args, PyObject *kw) static PyObject * datetime_utcfromtimestamp(PyObject *cls, PyObject *args) { - double timestamp; + PyObject *timestamp; PyObject *result = NULL; - if (PyArg_ParseTuple(args, "d:utcfromtimestamp", ×tamp)) + if (PyArg_ParseTuple(args, "O:utcfromtimestamp", ×tamp)) result = datetime_from_timestamp(cls, gmtime, timestamp, Py_None); return result; @@ -4469,9 +4445,7 @@ datetime_subtract(PyObject *left, PyObject *right) return NULL; if (offdiff != NULL) { - PyObject *temp = result; - result = delta_subtract(result, offdiff); - Py_DECREF(temp); + Py_SETREF(result, delta_subtract(result, offdiff)); Py_DECREF(offdiff); } } @@ -4536,25 +4510,55 @@ static PyObject * datetime_isoformat(PyDateTime_DateTime *self, PyObject *args, PyObject *kw) { int sep = 'T'; - static char *keywords[] = {"sep", NULL}; + char *timespec = NULL; + static char *keywords[] = {"sep", "timespec", NULL}; char buffer[100]; - PyObject *result; + PyObject *result = NULL; int us = DATE_GET_MICROSECOND(self); + static char *specs[][2] = { + {"hours", "%04d-%02d-%02d%c%02d"}, + {"minutes", "%04d-%02d-%02d%c%02d:%02d"}, + {"seconds", "%04d-%02d-%02d%c%02d:%02d:%02d"}, + {"milliseconds", "%04d-%02d-%02d%c%02d:%02d:%02d.%03d"}, + {"microseconds", "%04d-%02d-%02d%c%02d:%02d:%02d.%06d"}, + }; + size_t given_spec; - if (!PyArg_ParseTupleAndKeywords(args, kw, "|C:isoformat", keywords, &sep)) + if (!PyArg_ParseTupleAndKeywords(args, kw, "|Cs:isoformat", keywords, &sep, ×pec)) return NULL; - if (us) - result = PyUnicode_FromFormat("%04d-%02d-%02d%c%02d:%02d:%02d.%06d", + + if (timespec == NULL || strcmp(timespec, "auto") == 0) { + if (us == 0) { + /* seconds */ + given_spec = 2; + } + else { + /* microseconds */ + given_spec = 4; + } + } + else { + for (given_spec = 0; given_spec < Py_ARRAY_LENGTH(specs); given_spec++) { + if (strcmp(timespec, specs[given_spec][0]) == 0) { + if (given_spec == 3) { + us = us / 1000; + } + break; + } + } + } + + if (given_spec == Py_ARRAY_LENGTH(specs)) { + PyErr_Format(PyExc_ValueError, "Unknown timespec value"); + return NULL; + } + else { + result = PyUnicode_FromFormat(specs[given_spec][1], GET_YEAR(self), GET_MONTH(self), GET_DAY(self), (int)sep, DATE_GET_HOUR(self), DATE_GET_MINUTE(self), DATE_GET_SECOND(self), us); - else - result = PyUnicode_FromFormat("%04d-%02d-%02d%c%02d:%02d:%02d", - GET_YEAR(self), GET_MONTH(self), - GET_DAY(self), (int)sep, - DATE_GET_HOUR(self), DATE_GET_MINUTE(self), - DATE_GET_SECOND(self)); + } if (!result || !HASTZINFO(self)) return result; @@ -4766,7 +4770,7 @@ local_timezone(PyDateTime_DateTime *utc_time) if (seconds == NULL) goto error; Py_DECREF(delta); - timestamp = PyLong_AsLong(seconds); + timestamp = _PyLong_AsTime_t(seconds); Py_DECREF(seconds); if (timestamp == -1 && PyErr_Occurred()) return NULL; @@ -5093,9 +5097,12 @@ static PyMethodDef datetime_methods[] = { {"isoformat", (PyCFunction)datetime_isoformat, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("[sep] -> string in ISO 8601 format, " - "YYYY-MM-DDTHH:MM:SS[.mmmmmm][+HH:MM].\n\n" + "YYYY-MM-DDT[HH[:MM[:SS[.mmm[uuu]]]]][+HH:MM].\n" "sep is used to separate the year from the time, and " - "defaults to 'T'.")}, + "defaults to 'T'.\n" + "timespec specifies what components of the time to include" + " (allowed values are 'auto', 'hours', 'minutes', 'seconds'," + " 'milliseconds', and 'microseconds').\n")}, {"utcoffset", (PyCFunction)datetime_utcoffset, METH_NOARGS, PyDoc_STR("Return self.tzinfo.utcoffset(self).")}, @@ -5118,7 +5125,7 @@ static PyMethodDef datetime_methods[] = { {NULL, NULL} }; -static char datetime_doc[] = +static const char datetime_doc[] = PyDoc_STR("datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])\n\ \n\ The year, month and day arguments are required. tzinfo may be None, or an\n\ @@ -5382,19 +5389,19 @@ PyInit__datetime(void) /* A 4-year cycle has an extra leap day over what we'd get from * pasting together 4 single years. */ - assert(DI4Y == 4 * 365 + 1); + Py_BUILD_ASSERT(DI4Y == 4 * 365 + 1); assert(DI4Y == days_before_year(4+1)); /* Similarly, a 400-year cycle has an extra leap day over what we'd * get from pasting together 4 100-year cycles. */ - assert(DI400Y == 4 * DI100Y + 1); + Py_BUILD_ASSERT(DI400Y == 4 * DI100Y + 1); assert(DI400Y == days_before_year(400+1)); /* OTOH, a 100-year cycle has one fewer leap day than we'd get from * pasting together 25 4-year cycles. */ - assert(DI100Y == 25 * DI4Y - 1); + Py_BUILD_ASSERT(DI100Y == 25 * DI4Y - 1); assert(DI100Y == days_before_year(100+1)); one = PyLong_FromLong(1); |