From 36c494b0fac1ce021371acf80d8b4a632d00e16a Mon Sep 17 00:00:00 2001 From: maurycy <5383+maurycy@users.noreply.github.com> Date: Sat, 4 Apr 2026 14:24:12 +0200 Subject: [PATCH 1/6] simple caching --- Modules/_datetimemodule.c | 60 ++++++++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 16 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 163e499d957b2e..40b94b63ed606d 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -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 @@ -1879,10 +1883,12 @@ 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; } + 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 @@ -2042,7 +2048,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: @@ -2059,13 +2065,13 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple, static PyObject * time_time(void) { - 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; } + PyObject *result = PyObject_CallNoArgs(st->time_time); + RELEASE_CURRENT_STATE(st, current_mod); return result; } @@ -2075,21 +2081,20 @@ 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; } - 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; } @@ -7414,6 +7419,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_NewRef(st_old->time_time), + .time_struct_time = Py_NewRef(st_old->time_struct_time), + .time_strftime = Py_NewRef(st_old->time_strftime), }; return 0; } @@ -7458,6 +7466,19 @@ init_state(datetime_state *st, PyObject *module, PyObject *old_module) return -1; } + st->time_time = PyImport_ImportModuleAttrString("time", "time"); + if (st->time_time == NULL) { + return -1; + } + st->time_struct_time = PyImport_ImportModuleAttrString("time", "struct_time"); + if (st->time_struct_time == NULL) { + return -1; + } + st->time_strftime = PyImport_ImportModuleAttrString("time", "strftime"); + if (st->time_strftime == NULL) { + return -1; + } + return 0; } @@ -7467,6 +7488,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; } @@ -7482,6 +7507,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; } From 48e9ea134b63c492bf14d453c03178d3eee17a9b Mon Sep 17 00:00:00 2001 From: maurycy <5383+maurycy@users.noreply.github.com> Date: Sat, 4 Apr 2026 14:25:46 +0200 Subject: [PATCH 2/6] news --- .../2026-04-04-14-25-43.gh-issue-148085.o97yTo.rst | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-04-04-14-25-43.gh-issue-148085.o97yTo.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-04-04-14-25-43.gh-issue-148085.o97yTo.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-04-04-14-25-43.gh-issue-148085.o97yTo.rst new file mode 100644 index 00000000000000..c7a07644964c8d --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-04-04-14-25-43.gh-issue-148085.o97yTo.rst @@ -0,0 +1,3 @@ +Cache ``time.time``, ``time.struct_time``, and ``time.strftime`` imports, +speeding :meth:~datetime.timetuple and :meth:~datetime.strftime up to 2x. +Patch by Maurycy Pawłowski-Wieroński. From ae32bfda565f3c7180ce2d7058b9995c2b6bb5d3 Mon Sep 17 00:00:00 2001 From: maurycy <5383+maurycy@users.noreply.github.com> Date: Sat, 4 Apr 2026 14:28:50 +0200 Subject: [PATCH 3/6] oops, lint --- .../2026-04-04-14-25-43.gh-issue-148085.o97yTo.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-04-04-14-25-43.gh-issue-148085.o97yTo.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-04-04-14-25-43.gh-issue-148085.o97yTo.rst index c7a07644964c8d..c69085e72ecfaa 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2026-04-04-14-25-43.gh-issue-148085.o97yTo.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-04-04-14-25-43.gh-issue-148085.o97yTo.rst @@ -1,3 +1,3 @@ Cache ``time.time``, ``time.struct_time``, and ``time.strftime`` imports, -speeding :meth:~datetime.timetuple and :meth:~datetime.strftime up to 2x. +speeding :meth:`~datetime.timetuple` and :meth:`~datetime.strftime` up to 2x. Patch by Maurycy Pawłowski-Wieroński. From 7e7b7398821eb55dcb8f70d8b9eb54086ec4fed0 Mon Sep 17 00:00:00 2001 From: maurycy <5383+maurycy@users.noreply.github.com> Date: Sat, 4 Apr 2026 14:42:25 +0200 Subject: [PATCH 4/6] lazy --- Modules/_datetimemodule.c | 42 +++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 40b94b63ed606d..6347e456c7e30b 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -1888,6 +1888,13 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple, 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 @@ -2070,6 +2077,13 @@ time_time(void) if (st == NULL) { return NULL; } + if (st->time_time == NULL) { + st->time_time = PyImport_ImportModuleAttrString("time", "time"); + 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; @@ -2086,6 +2100,13 @@ build_struct_time(int y, int m, int d, int hh, int mm, int ss, int dstflag) 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; + } + } PyObject *result = PyObject_CallFunction(st->time_struct_time, "((iiiiiiiii))", @@ -7419,9 +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_NewRef(st_old->time_time), - .time_struct_time = Py_NewRef(st_old->time_struct_time), - .time_strftime = Py_NewRef(st_old->time_strftime), + .time_time = Py_XNewRef(st_old->time_time), + .time_struct_time = Py_XNewRef(st_old->time_struct_time), + .time_strftime = Py_XNewRef(st_old->time_strftime), }; return 0; } @@ -7466,18 +7487,9 @@ init_state(datetime_state *st, PyObject *module, PyObject *old_module) return -1; } - st->time_time = PyImport_ImportModuleAttrString("time", "time"); - if (st->time_time == NULL) { - return -1; - } - st->time_struct_time = PyImport_ImportModuleAttrString("time", "struct_time"); - if (st->time_struct_time == NULL) { - return -1; - } - st->time_strftime = PyImport_ImportModuleAttrString("time", "strftime"); - if (st->time_strftime == NULL) { - return -1; - } + st->time_time = NULL; + st->time_struct_time = NULL; + st->time_strftime = NULL; return 0; } From cd247ca877890f93c76871b53cfaae39e3b3bd13 Mon Sep 17 00:00:00 2001 From: maurycy <5383+maurycy@users.noreply.github.com> Date: Sat, 4 Apr 2026 15:10:25 +0200 Subject: [PATCH 5/6] i thought it existed... ok, now. no warnings? --- .../2026-04-04-14-25-43.gh-issue-148085.o97yTo.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-04-04-14-25-43.gh-issue-148085.o97yTo.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-04-04-14-25-43.gh-issue-148085.o97yTo.rst index c69085e72ecfaa..14dcfd479bc401 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2026-04-04-14-25-43.gh-issue-148085.o97yTo.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-04-04-14-25-43.gh-issue-148085.o97yTo.rst @@ -1,3 +1,4 @@ Cache ``time.time``, ``time.struct_time``, and ``time.strftime`` imports, -speeding :meth:`~datetime.timetuple` and :meth:`~datetime.strftime` up to 2x. -Patch by Maurycy Pawłowski-Wieroński. +speeding up :meth:`~datetime.date.timetuple` and +:meth:`~datetime.date.strftime` by up to 2x. Patch by Maurycy +Pawłowski-Wieroński. From 2f3dbe0e0134b4a2d550694af7190878db463f33 Mon Sep 17 00:00:00 2001 From: maurycy <5383+maurycy@users.noreply.github.com> Date: Sat, 4 Apr 2026 15:25:30 +0200 Subject: [PATCH 6/6] mv to library --- .../2026-04-04-14-25-43.gh-issue-148085.o97yTo.rst | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Misc/NEWS.d/next/{Core_and_Builtins => Library}/2026-04-04-14-25-43.gh-issue-148085.o97yTo.rst (100%) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-04-04-14-25-43.gh-issue-148085.o97yTo.rst b/Misc/NEWS.d/next/Library/2026-04-04-14-25-43.gh-issue-148085.o97yTo.rst similarity index 100% rename from Misc/NEWS.d/next/Core_and_Builtins/2026-04-04-14-25-43.gh-issue-148085.o97yTo.rst rename to Misc/NEWS.d/next/Library/2026-04-04-14-25-43.gh-issue-148085.o97yTo.rst