Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Cache ``time.time``, ``time.struct_time``, and ``time.strftime`` imports,
speeding up :meth:`~datetime.date.timetuple` and
:meth:`~datetime.date.strftime` by up to 2x. Patch by Maurycy
Pawłowski-Wieroński.
72 changes: 56 additions & 16 deletions Modules/_datetimemodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ typedef struct {

/* The interned Unix epoch datetime instance */
PyObject *epoch;

PyObject *time_time;
PyObject *time_struct_time;
PyObject *time_strftime;
} datetime_state;

/* The module has a fixed number of static objects, due to being exposed
Expand Down Expand Up @@ -1879,10 +1883,19 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple,
assert(object && format && timetuple);
assert(PyUnicode_Check(format));

PyObject *strftime = PyImport_ImportModuleAttrString("time", "strftime");
if (strftime == NULL) {
PyObject *current_mod = NULL;
datetime_state *st = GET_CURRENT_STATE(current_mod);
if (st == NULL) {
return NULL;
}
if (st->time_strftime == NULL) {
st->time_strftime = PyImport_ImportModuleAttrString("time", "strftime");
if (st->time_strftime == NULL) {
RELEASE_CURRENT_STATE(st, current_mod);
return NULL;
}
}
PyObject *strftime = st->time_strftime;

/* Scan the input format, looking for %z/%Z/%f escapes, building
* a new format. Since computing the replacements for those codes
Expand Down Expand Up @@ -2042,7 +2055,7 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple,
Py_XDECREF(zreplacement);
Py_XDECREF(colonzreplacement);
Py_XDECREF(Zreplacement);
Py_XDECREF(strftime);
RELEASE_CURRENT_STATE(st, current_mod);
return result;

Error:
Expand All @@ -2059,13 +2072,20 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple,
static PyObject *
time_time(void)
Comment thread
StanFromIreland marked this conversation as resolved.
Outdated
{
PyObject *result = NULL;
PyObject *time = PyImport_ImportModuleAttrString("time", "time");

if (time != NULL) {
result = PyObject_CallNoArgs(time);
Py_DECREF(time);
PyObject *current_mod = NULL;
datetime_state *st = GET_CURRENT_STATE(current_mod);
if (st == NULL) {
return NULL;
}
if (st->time_time == NULL) {
st->time_time = PyImport_ImportModuleAttrString("time", "time");
Comment thread
maurycy marked this conversation as resolved.
Outdated
if (st->time_time == NULL) {
RELEASE_CURRENT_STATE(st, current_mod);
return NULL;
}
}
PyObject *result = PyObject_CallNoArgs(st->time_time);
RELEASE_CURRENT_STATE(st, current_mod);
return result;
}

Expand All @@ -2075,21 +2095,27 @@ time_time(void)
static PyObject *
build_struct_time(int y, int m, int d, int hh, int mm, int ss, int dstflag)
{
PyObject *struct_time;
PyObject *result;

struct_time = PyImport_ImportModuleAttrString("time", "struct_time");
if (struct_time == NULL) {
PyObject *current_mod = NULL;
datetime_state *st = GET_CURRENT_STATE(current_mod);
if (st == NULL) {
return NULL;
}
if (st->time_struct_time == NULL) {
st->time_struct_time = PyImport_ImportModuleAttrString("time", "struct_time");
if (st->time_struct_time == NULL) {
RELEASE_CURRENT_STATE(st, current_mod);
return NULL;
}
}

result = PyObject_CallFunction(struct_time, "((iiiiiiiii))",
PyObject *result = PyObject_CallFunction(st->time_struct_time,
"((iiiiiiiii))",
y, m, d,
hh, mm, ss,
weekday(y, m, d),
days_before_month(y, m) + d,
dstflag);
Py_DECREF(struct_time);
RELEASE_CURRENT_STATE(st, current_mod);
return result;
}

Expand Down Expand Up @@ -7414,6 +7440,9 @@ init_state(datetime_state *st, PyObject *module, PyObject *old_module)
.us_per_week = Py_NewRef(st_old->us_per_week),
.seconds_per_day = Py_NewRef(st_old->seconds_per_day),
.epoch = Py_NewRef(st_old->epoch),
.time_time = Py_XNewRef(st_old->time_time),
.time_struct_time = Py_XNewRef(st_old->time_struct_time),
Comment thread
StanFromIreland marked this conversation as resolved.
Outdated
.time_strftime = Py_XNewRef(st_old->time_strftime),
Comment thread
maurycy marked this conversation as resolved.
Outdated
};
return 0;
}
Expand Down Expand Up @@ -7458,6 +7487,10 @@ init_state(datetime_state *st, PyObject *module, PyObject *old_module)
return -1;
}

st->time_time = NULL;
st->time_struct_time = NULL;
st->time_strftime = NULL;

return 0;
}

Expand All @@ -7467,6 +7500,10 @@ traverse_state(datetime_state *st, visitproc visit, void *arg)
/* heap types */
Py_VISIT(st->isocalendar_date_type);

Py_VISIT(st->time_time);
Py_VISIT(st->time_struct_time);
Py_VISIT(st->time_strftime);

return 0;
}

Expand All @@ -7482,6 +7519,9 @@ clear_state(datetime_state *st)
Py_CLEAR(st->us_per_week);
Py_CLEAR(st->seconds_per_day);
Py_CLEAR(st->epoch);
Py_CLEAR(st->time_time);
Py_CLEAR(st->time_struct_time);
Py_CLEAR(st->time_strftime);
return 0;
}

Expand Down
Loading