File 0845-erts-Fix-local_to_universal-time-without-dst.patch of Package erlang

From fa642ed498a6c05e91fe651a8d36b4afdbacb16b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Lukas=20Backstr=C3=B6m?= <lukas@erlang.org>
Date: Mon, 16 Dec 2024 13:59:53 +0100
Subject: [PATCH 1/3] erts: Fix local_to_universal time without dst

With glibc 2.37, the behaviour of mktime was changed to
no longer return an error if tm_isdst = 1 but the TZ actually
does not have any dst data. For example if TZ=UTC.

Instead of trusting the timezone, and not assuming that dst
is in effect, glibc decided to trust the caller and assume
that dst is in effect. This means that Erlang and glibc now
have different behaviours in this regard. So in order to
keep backward compatability with how Erlang has always worked
we instead check is the TZ has daylight handling at all, and if
not we set isdst to 0.

The change was introduced by this commit: https://sourceware.org/git/?p=glibc.git;a=commit;h=83859e1115269cf56d21669361d4ddbe2687831c and
a bug report was filed with glibc here:
https://sourceware.org/bugzilla/show_bug.cgi?id=31144, but
it seems like the new behaviour is desired.
---
 erts/config.h.in                      |  4 ++++
 erts/configure                        | 11 +++++++++++
 erts/configure.ac                     |  1 +
 erts/emulator/beam/erl_time_sup.c     | 24 ++++++++---------------
 erts/emulator/sys/unix/erl_unix_sys.h |  6 ++++++
 erts/emulator/sys/unix/sys_time.c     | 28 +++++++++++++++++++++++++++
 erts/emulator/sys/win32/erl_win_sys.h |  1 +
 erts/emulator/sys/win32/sys_time.c    |  7 ++++---
 8 files changed, 63 insertions(+), 19 deletions(-)

diff --git a/erts/config.h.in b/erts/config.h.in
index 8a1d0cdb5c..d87466dd43 100644
--- a/erts/config.h.in
+++ b/erts/config.h.in
@@ -471,6 +471,10 @@
 /* Define if you have cpuset_getaffinity/cpuset_setaffinity */
 #undef HAVE_CPUSET_xETAFFINITY
 
+/* Define to 1 if you have the declaration of 'daylight', and to 0 if you
+   don't. */
+#undef HAVE_DECL_DAYLIGHT
+
 /* Define to 1 if you have the declaration of `getrlimit', and to 0 if you
    don't. */
 #undef HAVE_DECL_GETRLIMIT
diff --git a/erts/configure b/erts/configure
index 8cd1ab6d9f..5db404f899 100755
--- a/erts/configure
+++ b/erts/configure
@@ -21120,6 +21120,17 @@ esac
 fi
 printf "%s\n" "#define HAVE_DECL_TIME2POSIX $ac_have_decl" >>confdefs.h
 
+ac_fn_check_decl "$LINENO" "daylight" "ac_cv_have_decl_daylight" "#include <time.h>
+" "$ac_c_undeclared_builtin_options" "CFLAGS"
+if test "x$ac_cv_have_decl_daylight" = xyes
+then :
+  ac_have_decl=1
+else case e in #(
+  e) ac_have_decl=0 ;;
+esac
+fi
+printf "%s\n" "#define HAVE_DECL_DAYLIGHT $ac_have_decl" >>confdefs.h
+
 
 ac_func=
 for ac_item in $ac_func_c_list
diff --git a/erts/configure.ac b/erts/configure.ac
index f551d5ad35..a282efcabf 100644
--- a/erts/configure.ac
+++ b/erts/configure.ac
@@ -2258,6 +2258,7 @@ case $host_os in
 esac
 
 AC_CHECK_DECLS([posix2time, time2posix],,,[#include <time.h>])
+AC_CHECK_DECLS([daylight],,,[#include <time.h>])
 
 AC_FUNC_VPRINTF
 
diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c
index d8e3913923..9051dc5aa0 100644
--- a/erts/emulator/beam/erl_time_sup.c
+++ b/erts/emulator/beam/erl_time_sup.c
@@ -1759,24 +1759,16 @@ local_to_univ(Sint *year, Sint *month, Sint *day,
     t.tm_sec = *second;
     t.tm_isdst = isdst;
 
-    /* the nature of mktime makes this a bit interesting,
-     * up to four mktime calls could happen here
-     */
+    if (!sys_daylight) {
+        /* If this is a timezone without DST and the OS (correctly)
+	   refuses to give us a DST time, we simulate the Linux/Solaris
+	   behaviour of giving the same data as if is_dst was not set. */
+        t.tm_isdst = 0;
+    }
 
     if (erl_mktime(&the_clock, &t) < 0) {
-	if (isdst) {
-	    /* If this is a timezone without DST and the OS (correctly)
-	       refuses to give us a DST time, we simulate the Linux/Solaris
-	       behaviour of giving the same data as if is_dst was not set. */
-	    t.tm_isdst = 0;
-	    if (erl_mktime(&the_clock, &t) < 0) {
-		/* Failed anyway, something else is bad - will be a badarg */
-		return 0;
-	    }
-	} else {
-	    /* Something else is the matter, badarg. */
-	    return 0;
-	}
+        /* Something is the matter, badarg. */
+        return 0;
     }
 
 #ifdef HAVE_TIME2POSIX
diff --git a/erts/emulator/sys/unix/erl_unix_sys.h b/erts/emulator/sys/unix/erl_unix_sys.h
index 9b926f9aeb..b9c5a5b2ac 100644
--- a/erts/emulator/sys/unix/erl_unix_sys.h
+++ b/erts/emulator/sys/unix/erl_unix_sys.h
@@ -148,6 +148,12 @@ typedef struct tms SysTimes;
 
 #define sys_times(Arg) times(Arg)
 
+#if !HAVE_DECL_DAYLIGHT
+extern int sys_daylight;
+#else
+#define sys_daylight daylight
+#endif
+
 #if SIZEOF_LONG == 8
 typedef long ErtsMonotonicTime;
 typedef long ErtsSysHrTime;
diff --git a/erts/emulator/sys/unix/sys_time.c b/erts/emulator/sys/unix/sys_time.c
index 06643407b4..d13a7bc36b 100644
--- a/erts/emulator/sys/unix/sys_time.c
+++ b/erts/emulator/sys/unix/sys_time.c
@@ -105,6 +105,10 @@ static void clock_gettime_times_raw(ErtsMonotonicTime *, ErtsSystemTime *);
 
 #endif /* defined(__linux__) && defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) */
 
+#if !HAVE_DECL_DAYLIGHT
+int sys_daylight = -1;
+#endif
+
 #ifdef ERTS_MACH_CLOCKS
 #  define ERTS_SYS_TIME_INTERNAL_STATE_READ_ONLY__
 typedef struct {
@@ -196,6 +200,30 @@ sys_init_time(ErtsSysInitTimeResult *init_resp)
 #if defined(ERTS_MACH_CLOCKS)
     mach_clocks_init();
 #endif
+#if !HAVE_DECL_DAYLIGHT
+    /* If the system does not have the daylight variable,
+       we create it by looping through the current year
+       in the current timezone and check if isdst ever
+       changes. */
+    time_t the_clock = time(NULL);
+    struct tm *tm, tmbuf;
+    tm = sys_localtime_r(&the_clock, &tmbuf);
+    tm->tm_hour = 0;
+    tm->tm_min = 0;
+    tm->tm_sec = 0;
+    tm->tm_mday = 1;
+    sys_daylight = 0;
+    for (int i = 0; i < 12; i++) {
+        struct tm *local_tm, local_tmbuf;
+        tm->tm_mon = i;
+        the_clock = mktime(tm);
+        local_tm = sys_localtime_r(&the_clock, &local_tmbuf);
+        if (local_tm->tm_isdst) {
+            sys_daylight = 1;
+            break;
+        }
+    }
+#endif
 #if !defined(ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT)
 
     init_resp->have_os_monotonic_time = 0;
diff --git a/erts/emulator/sys/win32/erl_win_sys.h b/erts/emulator/sys/win32/erl_win_sys.h
index 4d0c72c728..863f1925af 100644
--- a/erts/emulator/sys/win32/erl_win_sys.h
+++ b/erts/emulator/sys/win32/erl_win_sys.h
@@ -132,6 +132,7 @@ time_t sys_mktime( struct tm *ptm);
 #define gmtime_r sys_gmtime_r
 #define HAVE_GMTIME_R
 #define mktime sys_mktime
+extern int sys_daylight;
 
 typedef struct {
     erts_time_t tv_sec;
diff --git a/erts/emulator/sys/win32/sys_time.c b/erts/emulator/sys/win32/sys_time.c
index 52fb20c070..c8c8b6bba6 100644
--- a/erts/emulator/sys/win32/sys_time.c
+++ b/erts/emulator/sys/win32/sys_time.c
@@ -76,6 +76,7 @@
 
 static TIME_ZONE_INFORMATION static_tzi;
 static int have_static_tzi = 0;
+int sys_daylight = 0;
 
 static int days_in_month[2][13] = {
     {0,31,28,31,30,31,30,31,31,30,31,30,31},
@@ -406,10 +407,10 @@ sys_init_time(ErtsSysInitTimeResult *init_resp)
     init_resp->os_system_time_info.resolution = 100;
     init_resp->os_system_time_info.locked_use = 0;
 
-    if(GetTimeZoneInformation(&static_tzi) && 
-       static_tzi.StandardDate.wMonth != 0 &&
-       static_tzi.DaylightDate.wMonth != 0) {
+    if(GetTimeZoneInformation(&static_tzi)) {
 	have_static_tzi = 1;
+        sys_daylight = static_tzi.StandardDate.wMonth != 0 &&
+                    static_tzi.DaylightDate.wMonth != 0;
     }
 }
 
-- 
2.43.0

openSUSE Build Service is sponsored by