Function _localtime64 is available since msvcr70.dll. For older msvcrt
versions provide mingw-w64 emulation via _gmtime64() function and
adjustment of _timezone variable (filled by _tzset() call). To check
whether daylight bias adjustment is required (via _dstbias variable), use
the _localtime32() function for the same day and month as passed timestamp
but with changed year to fit into the signed 32-bit timestamp. This expects
that the DST start and end days of the last addressable year matches also
for all other future years.
---
 mingw-w64-crt/Makefile.am              |  2 +
 mingw-w64-crt/lib-common/msvcrt.def.in |  2 +-
 mingw-w64-crt/misc/_localtime64.c      | 88 ++++++++++++++++++++++++++
 3 files changed, 91 insertions(+), 1 deletion(-)
 create mode 100644 mingw-w64-crt/misc/_localtime64.c

diff --git a/mingw-w64-crt/Makefile.am b/mingw-w64-crt/Makefile.am
index e673e85d1b15..6869679f18ec 100644
--- a/mingw-w64-crt/Makefile.am
+++ b/mingw-w64-crt/Makefile.am
@@ -583,6 +583,7 @@ src_msvcrt32=\
   misc/_get_fmode.c \
   misc/_gmtime64.c \
   misc/_initterm_e.c \
+  misc/_localtime64.c \
   misc/_mkgmtime32.c \
   misc/_mkgmtime64.c \
   misc/_set_doserrno.c \
@@ -897,6 +898,7 @@ src_pre_msvcr70=\
   misc/_ftime64.c \
   misc/_futime64.c \
   misc/_gmtime64.c \
+  misc/_localtime64.c \
   misc/_time64.c \
   misc/_utime64.c \
   misc/_wutime64.c \
diff --git a/mingw-w64-crt/lib-common/msvcrt.def.in 
b/mingw-w64-crt/lib-common/msvcrt.def.in
index 1ea6206d6371..b034db07085e 100644
--- a/mingw-w64-crt/lib-common/msvcrt.def.in
+++ b/mingw-w64-crt/lib-common/msvcrt.def.in
@@ -1157,7 +1157,7 @@ F_NON_I386(_fstat64) ; i386 _fstat64 replaced by emu
 F_NON_I386(_ftime64) ; i386 _ftime64 replaced by emu
 F_NON_I386(_futime64) ; i386 _futime64 replaced by emu
 F_NON_I386(_gmtime64) ; i386 _gmtime64 replaced by emu
-_localtime64
+F_NON_I386(_localtime64) ; i386 _localtime64 replaced by emu
 _mktime64
 F_X86_ANY(_osplatform DATA)
 F_NON_I386(_stat64) ; i386 _stat64 replaced by emu
diff --git a/mingw-w64-crt/misc/_localtime64.c 
b/mingw-w64-crt/misc/_localtime64.c
new file mode 100644
index 000000000000..b49ce209563e
--- /dev/null
+++ b/mingw-w64-crt/misc/_localtime64.c
@@ -0,0 +1,88 @@
+/**
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER.PD within this package.
+ */
+
+#include <windows.h>
+#include <time.h>
+
+static struct tm *__cdecl emu__localtime64(const __time64_t *timeptr)
+{
+    struct tm *tmptr;
+    struct tm tm32;
+    int local_daylight;
+    long local_dstbias;
+    long local_timezone;
+    __time64_t t64;
+    __time32_t t32;
+
+    /* _tzset() initialize _daylight, _dstbias and _timezone variables,
+     * so it needs to be called before accessing those variables.
+     * If those variables are already initialized then _tzset() does
+     * not need to be called again. As _tzset() is an expensive call,
+     * guard repeated calls by static variable. As the _tzset() is a
+     * thread-safe call, the race condition is not a problem.
+     */
+    {
+        static volatile long tzset_called = 0;
+        if (!tzset_called) {
+            _tzset();
+            (void)InterlockedExchange(&tzset_called, 1);
+        }
+    }
+    local_daylight = *__daylight();
+    local_dstbias = *__dstbias();
+    local_timezone = *__timezone();
+
+    /* __localtime64() for the case when the timezone does not use DST */
+    t64 = *timeptr - local_timezone;
+    tmptr = _gmtime64(&t64);
+    if (!tmptr)
+        return NULL;
+
+    /* If the timezone use DST then it is needed to check if the DST is active
+     * for passed timestamp. To do that use the existing _localtime32() 
function
+     * and its tm_isdst member of return value. As the _localtime32() function
+     * works only for time structure which can be represented by signed 32-bit
+     * time_t type, change year of the passed timestamp, so the timestamp can
+     * be represented by 32-bit type. This expects that the DST start and end
+     * days of the last addressable year matches also for all other future 
years.
+     */
+    if (local_daylight) {
+        /* Prepare struct tm to be representable by 32-bit time_t value, just 
by changing year */
+        tm32 = *tmptr;
+        if (tm32.tm_year > 2037-1900)
+            tm32.tm_year = 2037-1900;
+        else if (tm32.tm_year < 1971-1900)
+            tm32.tm_year = 1971-1900;
+
+        /* Use _localtime32()'s tm_isdst to determinate if the DST is active 
for passed timestamp */
+        t32 = _mkgmtime32(&tm32);
+        if (t32 == -1)
+            return NULL;
+        t32 += local_timezone; /* Remove the fake timezone offset */
+        tmptr = _localtime32(&t32);
+        if (!tmptr)
+            return NULL;
+
+        /* If the DST is active for passed timestamp then recalculate the 
struct tm according to DST bias */
+        if (tmptr->tm_isdst) {
+            t64 -= local_dstbias;
+            tmptr = _gmtime64(&t64);
+            if (!tmptr)
+                return NULL;
+            tmptr->tm_isdst = 1;
+        } else {
+            tmptr = _gmtime64(&t64);
+        }
+    }
+
+    return tmptr;
+}
+
+#define RETT struct tm *
+#define FUNC _localtime64
+#define ARGS const __time64_t *timeptr
+#define CALL timeptr
+#include "msvcrt_or_emu_glue.h"
-- 
2.20.1



_______________________________________________
Mingw-w64-public mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/mingw-w64-public

Reply via email to