diff --git a/include/pybind11/chrono.h b/include/pybind11/chrono.h index 167ea0e3d..b17f29d43 100644 --- a/include/pybind11/chrono.h +++ b/include/pybind11/chrono.h @@ -99,7 +99,7 @@ public: PYBIND11_TYPE_CASTER(type, const_name("datetime.timedelta")); }; -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)) return nullptr; @@ -107,7 +107,7 @@ inline std::tm *localtime_thread_safe(const std::time_t *time, std::tm *buf) { #else static std::mutex mtx; std::lock_guard lock(mtx); - std::tm *tm_ptr = std::localtime(time); + std::tm *tm_ptr = std::gmtime(time); if (tm_ptr != nullptr) { *buf = *tm_ptr; } @@ -166,7 +166,7 @@ public: return false; } - value = time_point_cast(system_clock::from_time_t(std::mktime(&cal)) + msecs); + value = time_point_cast(system_clock::from_time_t(timegm(&cal)) + msecs); return true; } @@ -194,17 +194,17 @@ public: std::time_t tt = system_clock::to_time_t(time_point_cast(src - us)); - std::tm localtime; - std::tm *localtime_ptr = localtime_thread_safe(&tt, &localtime); - if (!localtime_ptr) { - throw cast_error("Unable to represent system_clock in local time"); + std::tm gmtime; + std::tm *gmtime_ptr = gmtime_thread_safe(&tt, &gmtime); + if (!gmtime_ptr) { + throw cast_error("Unable to represent system_clock in GMT time"); } - return PyDateTime_FromDateAndTime(localtime.tm_year + 1900, - localtime.tm_mon + 1, - localtime.tm_mday, - localtime.tm_hour, - localtime.tm_min, - localtime.tm_sec, + return PyDateTime_FromDateAndTime(gmtime.tm_year + 1900, + gmtime.tm_mon + 1, + gmtime.tm_mday, + gmtime.tm_hour, + gmtime.tm_min, + gmtime.tm_sec, us.count()); } PYBIND11_TYPE_CASTER(type, const_name("datetime.datetime")); diff --git a/tests/test_chrono.py b/tests/test_chrono.py index 7f47b37a2..87366782f 100644 --- a/tests/test_chrono.py +++ b/tests/test_chrono.py @@ -71,6 +71,16 @@ def test_chrono_system_clock_roundtrip_date(): assert time2.microsecond == 0 +def test_chrono_system_clock_roundtrip_daylight_savings(): + # naive datetime - AEDST clock will change + datetime1 = datetime.datetime(2021, 10, 3, 2, 18, 46, 677734) + + # Roundtrip the time + datetime2 = m.test_chrono2(datetime1) + + assert datetime2.hour == datetime1.hour + + SKIP_TZ_ENV_ON_WIN = pytest.mark.skipif( "env.WIN", reason="TZ environment variable only supported on POSIX" )