From 93b27d32cda0f62b5e39709f2bbedb32b1cd49ec Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Mon, 26 May 2025 19:53:21 +0300 Subject: [PATCH] Fix DST time calculations on MS-Windows * src/w32.c (w32_fix_tzset): New function. * src/timefns.c (emacs_localtime_rz, tzlookup): * src/w32.c (sys_localtime): Call 'w32_fix_tzset'. (Bug#11281) (cherry picked from commit 3b5226af3f737c82f9bbedd8fbe067bab4b4b67a) --- src/timefns.c | 4 ++++ src/w32.c | 25 +++++++++++++++++++++++++ src/w32.h | 1 + 3 files changed, 30 insertions(+) diff --git a/src/timefns.c b/src/timefns.c index 4d296ff8dcd..8cf424bbe7e 100644 --- a/src/timefns.c +++ b/src/timefns.c @@ -189,6 +189,7 @@ emacs_localtime_rz (timezone_t tz, time_t const *t, struct tm *tm) display-time) are in real danger of missing timezone and DST changes. Calling tzset before each localtime call fixes that. */ tzset (); + w32_fix_tzset (); #endif tm = localtime_rz (tz, t, tm); if (!tm && errno == ENOMEM) @@ -306,6 +307,9 @@ tzlookup (Lisp_Object zone, bool settz) block_input (); emacs_setenv_TZ (zone_string); tzset (); +#ifdef WINDOWSNT + w32_fix_tzset (); +#endif timezone_t old_tz = local_tz; local_tz = new_tz; tzfree (old_tz); diff --git a/src/w32.c b/src/w32.c index 5de721ad71f..9e17c2e5fbb 100644 --- a/src/w32.c +++ b/src/w32.c @@ -10289,6 +10289,30 @@ w32_read_registry (HKEY rootkey, Lisp_Object lkey, Lisp_Object lname) } +/* mingw.org's MinGW doesn't declare _dstbias. MinGW64 defines it as a + macro. */ +#ifndef _dstbias +__MINGW_IMPORT int _dstbias; +#endif + +/* Fix a bug in MS implementation of 'tzset'. This function should be + called immediately after 'tzset'. */ +void +w32_fix_tzset (void) +{ + char *tz_env = getenv ("TZ"); + + /* When TZ is defined in the environment, '_tzset' updates _daylight, + but not _dstbias. Then if we are switching from a timezone without + DST to a timezone with DST, 'localtime' and friends will apply zero + DST bias, which is incorrect. (When TZ is not defined, '_tzset' + does update _dstbias using values obtained from Windows API + GetTimeZoneInformation.) Here we fix that blunder by detecting + this situation and forcing _dstbias to be 1 hour. */ + if (tz_env && _daylight && !_dstbias) + _dstbias = -3600; +} + /* The Windows CRT functions are "optimized for speed", so they don't check for timezone and DST changes if they were last called less than 1 minute ago (see http://support.microsoft.com/kb/821231). So @@ -10299,6 +10323,7 @@ struct tm * sys_localtime (const time_t *t) { tzset (); + w32_fix_tzset (); return localtime (t); } diff --git a/src/w32.h b/src/w32.h index ae3999ffcfd..9d9887ec782 100644 --- a/src/w32.h +++ b/src/w32.h @@ -234,6 +234,7 @@ extern int openat (int, const char *, int, int); extern int fchmodat (int, char const *, mode_t, int); extern int lchmod (char const *, mode_t); extern bool symlinks_supported (const char *); +extern void w32_fix_tzset (void); /* Return total and free memory info. */ -- 2.39.5