diff --git a/Misc/NEWS.d/next/Build/2025-01-03-13-02-06.gh-issue-123681.gQ67nK.rst b/Misc/NEWS.d/next/Build/2025-01-03-13-02-06.gh-issue-123681.gQ67nK.rst new file mode 100644 index 00000000000000..8f4f1fb4fabbe5 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2025-01-03-13-02-06.gh-issue-123681.gQ67nK.rst @@ -0,0 +1,3 @@ +Check the ``strftime()`` behavior at runtime instead of at the compile time +to support cross-compiling. Remove macros Py_NORMALIZE_CENTURY and +Py_STRFTIME_C99_SUPPORT. diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 9bba0e3354b26b..c808d36b791d59 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -1757,6 +1757,42 @@ format_utcoffset(char *buf, size_t buflen, const char *sep, return 0; } +/* Check whether year with century should be normalized for strftime. */ +inline static int +normalize_century(void) +{ + static int cache = -1; + if (cache < 0) { + char year[5]; + struct tm date = { + .tm_year = -1801, + .tm_mon = 0, + .tm_mday = 1 + }; + cache = (strftime(year, sizeof(year), "%Y", &date) && + strcmp(year, "0099") != 0); + } + return cache; +} + +/* Check whether C99-specific strftime specifiers are supported. */ +inline static int +strftime_c99_support(void) +{ + static int cache = -1; + if (cache < 0) { + char full_date[11]; + struct tm date = { + .tm_year = 0, + .tm_mon = 0, + .tm_mday = 1 + }; + cache = (strftime(full_date, sizeof(full_date), "%F", &date) && + strcmp(full_date, "1900-01-01") == 0); + } + return cache; +} + static PyObject * make_somezreplacement(PyObject *object, char *sep, PyObject *tzinfoarg) { @@ -1928,10 +1964,9 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple, } replacement = freplacement; } -#ifdef Py_NORMALIZE_CENTURY - else if (ch == 'Y' || ch == 'G' - || ch == 'F' || ch == 'C' - ) { + else if (normalize_century() && (ch == 'Y' || ch == 'G' || + (strftime_c99_support() && (ch == 'F' || ch == 'C')))) + { /* 0-pad year with century as necessary */ PyObject *item = PySequence_GetItem(timetuple, 0); if (item == NULL) { @@ -1982,7 +2017,6 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple, } continue; } -#endif else { /* percent followed by something else */ continue; diff --git a/Tools/c-analyzer/cpython/ignored.tsv b/Tools/c-analyzer/cpython/ignored.tsv index 14dc5007b65861..28a83c8c41fe45 100644 --- a/Tools/c-analyzer/cpython/ignored.tsv +++ b/Tools/c-analyzer/cpython/ignored.tsv @@ -224,6 +224,8 @@ Modules/_datetimemodule.c datetime_isoformat specs - Modules/_datetimemodule.c parse_hh_mm_ss_ff correction - Modules/_datetimemodule.c time_isoformat specs - Modules/_datetimemodule.c - capi_types - +Modules/_datetimemodule.c normalize_century cache - +Modules/_datetimemodule.c strftime_c99_support cache - Modules/_decimal/_decimal.c - cond_map_template - Modules/_decimal/_decimal.c - dec_signal_string - Modules/_decimal/_decimal.c - dflt_ctx - diff --git a/configure b/configure index 1b75ddfa26dcdd..79608c563a93e3 100755 --- a/configure +++ b/configure @@ -27579,110 +27579,6 @@ printf "%s\n" "#define HAVE_STAT_TV_NSEC2 1" >>confdefs.h fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether year with century should be normalized for strftime" >&5 -printf %s "checking whether year with century should be normalized for strftime... " >&6; } -if test ${ac_cv_normalize_century+y} -then : - printf %s "(cached) " >&6 -else case e in #( - e) -if test "$cross_compiling" = yes -then : - ac_cv_normalize_century=yes -else case e in #( - e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -#include -#include - -int main(void) -{ - char year[5]; - struct tm date = { - .tm_year = -1801, - .tm_mon = 0, - .tm_mday = 1 - }; - if (strftime(year, sizeof(year), "%Y", &date) && !strcmp(year, "0099")) { - return 1; - } - return 0; -} - -_ACEOF -if ac_fn_c_try_run "$LINENO" -then : - ac_cv_normalize_century=yes -else case e in #( - e) ac_cv_normalize_century=no ;; -esac -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext ;; -esac -fi - ;; -esac -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_normalize_century" >&5 -printf "%s\n" "$ac_cv_normalize_century" >&6; } -if test "$ac_cv_normalize_century" = yes -then - -printf "%s\n" "#define Py_NORMALIZE_CENTURY 1" >>confdefs.h - -fi - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C99-compatible strftime specifiers are supported" >&5 -printf %s "checking whether C99-compatible strftime specifiers are supported... " >&6; } -if test ${ac_cv_strftime_c99_support+y} -then : - printf %s "(cached) " >&6 -else case e in #( - e) -if test "$cross_compiling" = yes -then : - ac_cv_strftime_c99_support= -else case e in #( - e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -#include -#include - -int main(void) -{ - char full_date[11]; - struct tm date = { - .tm_year = 0, - .tm_mon = 0, - .tm_mday = 1 - }; - if (strftime(full_date, sizeof(full_date), "%F", &date) && !strcmp(full_date, "1900-01-01")) { - return 0; - } - return 1; -} - -_ACEOF -if ac_fn_c_try_run "$LINENO" -then : - ac_cv_strftime_c99_support=yes -else case e in #( - e) as_fn_error $? "Python requires C99-compatible strftime specifiers" "$LINENO" 5 ;; -esac -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext ;; -esac -fi - ;; -esac -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_strftime_c99_support" >&5 -printf "%s\n" "$ac_cv_strftime_c99_support" >&6; } - have_curses=no have_panel=no diff --git a/configure.ac b/configure.ac index c449bb5ebb3cd4..50bcd7eedb6c3c 100644 --- a/configure.ac +++ b/configure.ac @@ -6670,57 +6670,6 @@ then [Define if you have struct stat.st_mtimensec]) fi -AC_CACHE_CHECK([whether year with century should be normalized for strftime], [ac_cv_normalize_century], [ -AC_RUN_IFELSE([AC_LANG_SOURCE([[ -#include -#include - -int main(void) -{ - char year[5]; - struct tm date = { - .tm_year = -1801, - .tm_mon = 0, - .tm_mday = 1 - }; - if (strftime(year, sizeof(year), "%Y", &date) && !strcmp(year, "0099")) { - return 1; - } - return 0; -} -]])], -[ac_cv_normalize_century=yes], -[ac_cv_normalize_century=no], -[ac_cv_normalize_century=yes])]) -if test "$ac_cv_normalize_century" = yes -then - AC_DEFINE([Py_NORMALIZE_CENTURY], [1], - [Define if year with century should be normalized for strftime.]) -fi - -AC_CACHE_CHECK([whether C99-compatible strftime specifiers are supported], [ac_cv_strftime_c99_support], [ -AC_RUN_IFELSE([AC_LANG_SOURCE([[ -#include -#include - -int main(void) -{ - char full_date[11]; - struct tm date = { - .tm_year = 0, - .tm_mon = 0, - .tm_mday = 1 - }; - if (strftime(full_date, sizeof(full_date), "%F", &date) && !strcmp(full_date, "1900-01-01")) { - return 0; - } - return 1; -} -]])], -[ac_cv_strftime_c99_support=yes], -[AC_MSG_ERROR([Python requires C99-compatible strftime specifiers])], -[ac_cv_strftime_c99_support=])]) - dnl check for ncursesw/ncurses and panelw/panel dnl NOTE: old curses is not detected. dnl have_curses=[no, yes] diff --git a/pyconfig.h.in b/pyconfig.h.in index aa086d49e90a5b..065673ac6ae81e 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -1715,9 +1715,6 @@ /* Defined if _Complex C type is available. */ #undef Py_HAVE_C_COMPLEX -/* Define if year with century should be normalized for strftime. */ -#undef Py_NORMALIZE_CENTURY - /* Define if you want to enable remote debugging support. */ #undef Py_REMOTE_DEBUG