Repository navigation
changed localtime to gmtime to fix daylight savings issues - #3805
JamesUnicomb wants to merge 2 commits into
Conversation
| inline std::tm *localtime_thread_safe(const std::time_t *time, std::tm *buf) { | ||
| inline std::tm *gmtime_thread_safe(const std::time_t *time, std::tm *buf) { | ||
| #if (defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || defined(_MSC_VER) | ||
| if (localtime_s(buf, time)) |
There was a problem hiding this comment.
All the Windows tests are failing. Do you already have a plan for that? Could we simply remove the #if and use the same code, with mutex, everywhere?
| localtime.tm_hour, | ||
| localtime.tm_min, | ||
| localtime.tm_sec, | ||
| return PyDateTime_FromDateAndTime(gmtime.tm_year + 1900, |
There was a problem hiding this comment.
Doesn't this change the behavior?
Before: local timezone
After: actually UTC but (incorrectly?) appearing as local timezone?
Same question from a different angle: as-is this PR fixes the roundtrip issue, i.e. fixes an internal consistency issue, but is it still externally inconsistent?
Do we have to tell PyDateTime_FromDateAndTime() somehow that the passed time is UTC?
I just looked a bit, seeing this warning 3 times under https://docs.python.org/3/library/datetime.html:
Warning Because naive datetime objects are treated by many datetime methods as local times, it is preferred to use aware datetimes to represent times in UTC. As such, the recommended way to create an object representing a specific timestamp in UTC is by calling datetime.fromtimestamp(timestamp, tz=timezone.utc).
How can handle this correctly from the C API?
| inline std::tm *localtime_thread_safe(const std::time_t *time, std::tm *buf) { | ||
| inline std::tm *gmtime_thread_safe(const std::time_t *time, std::tm *buf) { | ||
| #if (defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || defined(_MSC_VER) | ||
| if (localtime_s(buf, time)) |
There was a problem hiding this comment.
This function needs to be swapped out too to stop the tests from failing
| localtime.tm_hour, | ||
| localtime.tm_min, | ||
| localtime.tm_sec, | ||
| return PyDateTime_FromDateAndTime(gmtime.tm_year + 1900, |
There was a problem hiding this comment.
@JamesUnicomb I am not sure about the UTC issue, but the datetime roundtrip issue can be easily resolved. We just need to use PyDateTime_FromDateAndTimeAndFold instead of PyDateTime_FromDateAndTime and make sure to properly set the is_fold argument.
| } | ||
|
|
||
| value = time_point_cast<Duration>(system_clock::from_time_t(std::mktime(&cal)) + msecs); | ||
| value = time_point_cast<Duration>(system_clock::from_time_t(timegm(&cal)) + msecs); |
There was a problem hiding this comment.
timegm isn't available on windows, so this line needs to change to something like:
#if defined(_MSC_VER)
time_t tt = _mkgmtime(&cal);
#else
time_t tt = timegm(&cal);
#endif
value = time_point_cast<Duration>(system_clock::from_time_t(tt) + msecs);There was a problem hiding this comment.
As @Skylion007 pointed out before, we don't have to go to UTC to solve the roundtrip problem: #3805 (comment)
I think it's better to keep using the local time zone, but make proper use of the fold feature.
There was a problem hiding this comment.
with that solution would std::system_clock time points go into python as time aware (UTC) datetime objects, and python datetime objects go into C++ as UTC?
C++ system_clock is Unix Time (so UTC modulo leap seconds and such), so the time zone conversions are a little surprising.
There was a problem hiding this comment.
I believe python datetime objects should be in local time in both? I am not sure we are actually attaching the time zone to the Python objects (we should but it doesn't seem straight forward using the raw C - API).
If it's too tricky, we might want to call the Python datetime function directly if we need to.
|
I'm a noob here, so please, disregard readily and with prejudice if what I write does not jive with the spirit of the project. It feels like localtime_thread_safe is a critical part of the contract for this cast, and that theres probably upstream customer assumptions around it, esp if its not a 1:1 conversion (as you said, daylight savings time can cause off-by-some-amount-of-time discrepancies). It feels, then, like the right thing to do here would be make this a perfectly backward compatible change, via pre-processor macro, something like: PYBIND11_USE_LEGACY_LOCAL_TIME (where the default is now the gmtime) (my preference) or PYBIND11_USE_GMTIME (where the default is the old behavior) |
|
I remembered this old issue and used codex to dig it up and analyze: codex GPT-6.1-Sol ultra The likely match is PR #3805, “changed localtime to gmtime to fix daylight savings issues”, opened in March 2022. It is still open and unmerged. The related issue #1638 is closed, although later comments report continuing problems. A real fall-back bug remains today. I checked current upstream master (
For Los Angeles’s upcoming November 1 transition, I reproduced:
The original report needs a distinction: its Sydney example was The old PR proposed switching all conversions to UTC. Review identified Windows failures and a change to the documented local-time behavior. Your review favored retaining local time and correctly supporting The fix should:
Both regular and free-threaded CPython 3.14.4 passed all 43 existing chrono tests, while the new standalone probe found 16 incorrect conversions in each environment. This was focused chrono validation; Catch2 and the full suite were not run. The reproducer, regular results, and free-threaded results are available locally. The source fix is still outstanding. I’d target a release before October 25, 2026, when EU clocks go back; US/Pacific follows on November 1. Downstream extensions will need rebuilding with the fixed headers. EU schedule, NIST US schedule. |
|
Closing after the alternative PR #6202 was merged. |
I was having issues with pybind using UTC times properly. As well, daylight savings datetimes were failing "roundtrip" tests.
Linked to this discussion:
#3451 (reply in thread)