summaryrefslogtreecommitdiffstats
path: root/Modules/_datetimemodule.c
diff options
context:
space:
mode:
Diffstat (limited to 'Modules/_datetimemodule.c')
-rw-r--r--Modules/_datetimemodule.c255
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, &timespec))
+ 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, &timestamp, &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", &timestamp))
+ if (PyArg_ParseTuple(args, "O:utcfromtimestamp", &timestamp))
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, &timespec))
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);