bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

mktime: add native Windows workaround


From: Bruno Haible
Subject: mktime: add native Windows workaround
Date: Sun, 30 Apr 2017 19:51:24 +0200
User-agent: KMail/5.1.3 (Linux/4.4.0-75-generic; KDE/5.18.0; x86_64; ; )

> * Some which should obey TZ, just that they should ignore the values set by
>   Cygwin (instead of exhibiting garbage behaviour):
> 
>   mktime, _mktime*
>   https://msdn.microsoft.com/en-us/library/d1y53h2a.aspx
>   http://pubs.opengroup.org/onlinepubs/9699919799/functions/mktime.html

This patch adds the workaround against wrong interpretation of TZ on native
Windows to the 'mktime' function.

Also it fixes a three macrology problems:
  - The use of module 'timegm' or 'mktime-internal' without module 'mktime'
    could lead to a link error w.r.t. to symbol 'rpl_mktime' when the user
    wants to use the mktime() function. (I think. Haven't checked.)
  - The use of module 'mktime-internal' without module 'mktime' would still
    define a function 'mktime' in mktime.o.
  - When cross-compiling, it now prints "checking for working mktime... 
guessing no"
    instead of "checking for working mktime... no".

Ultimately, this macrology complexity is due to the fact that so much stuff
is contained in a single source file, lib/mktime.c. It would be simpler if
the mktime_internal function was a different compilation unit; then the
'mktime-internal' module could do AC_LIBOBJ([mktime-internal]) instead of
AC_LIBOBJ([mktime]).


2017-04-30  Bruno Haible  <address@hidden>

        mktime: Work around TZ problem on native Windows.
        * lib/mktime.c: Add #ifs to make the algorithmic workaround independent
        from the native Windows workaround.
        * m4/mktime.m4 (gl_FUNC_MKTIME_WORKS): New macro, extracted from
        gl_FUNC_MKTIME. If guessing, set gl_cv_func_working_mktime to
        'guessing no'.
        (gl_FUNC_MKTIME): Require it. Require AC_CANONICAL_HOST.
        Set REPLACE_MKTIME to 1 on native Windows. Define NEED_MKTIME_WORKING,
        NEED_MKTIME_WINDOWS.
        (gl_FUNC_MKTIME_INTERNAL): Require gl_FUNC_MKTIME_WORKS, not
        gl_FUNC_MKTIME. Set WANT_MKTIME_INTERNAL, not REPLACE_MKTIME. Define
        NEED_MKTIME_INTERNAL.
        * m4/timegm.m4 (gl_FUNC_TIMEGM): Require gl_FUNC_MKTIME_WORKS, not
        gl_FUNC_MKTIME. Cope with 'guessing yes' value.
        * modules/mktime-internal (configure.ac): Test WANT_MKTIME_INTERNAL,
        not REPLACE_MKTIME.
        * doc/posix-functions/mktime.texi: Mention the native Windows
        workaround.

diff --git a/doc/posix-functions/mktime.texi b/doc/posix-functions/mktime.texi
index ffb7b79..35a9a41 100644
--- a/doc/posix-functions/mktime.texi
+++ b/doc/posix-functions/mktime.texi
@@ -9,6 +9,9 @@ Gnulib module: mktime
 Portability problems fixed by Gnulib:
 @itemize
 @item
+On native Windows platforms (mingw, MSVC), this function works incorrectly
+when the environment variable @code{TZ} has been set by Cygwin.
address@hidden
 @code{mktime} may go into an endless loop on some platforms.
 @item
 @code{mktime} may occasionally return wrong results on some platforms.
@@ -16,7 +19,4 @@ Portability problems fixed by Gnulib:
 
 Portability problems not fixed by Gnulib:
 @itemize
address@hidden
-On native Windows platforms (mingw, MSVC), this function works incorrectly
-when the environment variable @code{TZ} has been set by Cygwin.
 @end itemize
diff --git a/lib/mktime.c b/lib/mktime.c
index 2efd44a..a78d960 100644
--- a/lib/mktime.c
+++ b/lib/mktime.c
@@ -23,6 +23,19 @@
 # define DEBUG_MKTIME 0
 #endif
 
+/* The following macros influence what gets defined when this file is compiled:
+
+   Macro/expression            Which gnulib module    This compilation unit
+                                                      should define
+
+   NEED_MKTIME_WORKING         mktime                 rpl_mktime
+   || NEED_MKTIME_WINDOWS
+
+   NEED_MKTIME_INTERNAL        mktime-internal        mktime_internal
+
+   DEBUG_MKTIME                (defined manually)     my_mktime, main
+ */
+
 #if !defined _LIBC && !DEBUG_MKTIME
 # include <config.h>
 #endif
@@ -51,6 +64,13 @@
 # define mktime my_mktime
 #endif
 
+#if NEED_MKTIME_WINDOWS /* on native Windows */
+# include <stdlib.h>
+# include <string.h>
+#endif
+
+#if NEED_MKTIME_WORKING || NEED_MKTIME_INTERNAL || DEBUG_MKTIME
+
 /* A signed type that can represent an integer number of years
    multiplied by three times the number of seconds in a year.  It is
    needed when converting a tm_year value times the number of seconds
@@ -458,25 +478,46 @@ __mktime_internal (struct tm *tp,
   return t;
 }
 
+#endif /* NEED_MKTIME_WORKING || NEED_MKTIME_INTERNAL || DEBUG_MKTIME */
+
+#if NEED_MKTIME_WORKING || NEED_MKTIME_WINDOWS || DEBUG_MKTIME
 
+# if NEED_MKTIME_WORKING || DEBUG_MKTIME
 static mktime_offset_t localtime_offset;
+# endif
 
 /* Convert *TP to a time_t value.  */
 time_t
 mktime (struct tm *tp)
 {
-#ifdef _LIBC
+# if NEED_MKTIME_WINDOWS
+  /* If the environment variable TZ has been set by Cygwin, neutralize it.
+     The Microsoft CRT interprets TZ differently than Cygwin and produces
+     incorrect results if TZ has the syntax used by Cygwin.  */
+  const char *tz = getenv ("TZ");
+  if (tz != NULL && strchr (tz, '/') != NULL)
+    _putenv ("TZ=");
+# endif
+
+# if NEED_MKTIME_WORKING || DEBUG_MKTIME
+#  ifdef _LIBC
   /* POSIX.1 8.1.1 requires that whenever mktime() is called, the
      time zone names contained in the external variable 'tzname' shall
      be set as if the tzset() function had been called.  */
   __tzset ();
-#elif HAVE_TZSET
+#  elif HAVE_TZSET
   tzset ();
-#endif
+#  endif
 
   return __mktime_internal (tp, __localtime_r, &localtime_offset);
+# else
+#  undef mktime
+  return mktime (tp);
+# endif
 }
 
+#endif /* NEED_MKTIME_WORKING || NEED_MKTIME_WINDOWS || DEBUG_MKTIME */
+
 #ifdef weak_alias
 weak_alias (mktime, timelocal)
 #endif
diff --git a/m4/mktime.m4 b/m4/mktime.m4
index d594ddc..31da65e 100644
--- a/m4/mktime.m4
+++ b/m4/mktime.m4
@@ -1,4 +1,4 @@
-# serial 27
+# serial 28
 dnl Copyright (C) 2002-2003, 2005-2007, 2009-2017 Free Software Foundation,
 dnl Inc.
 dnl This file is free software; the Free Software Foundation
@@ -21,9 +21,9 @@ AC_DEFUN([gl_TIME_T_IS_SIGNED],
   fi
 ])
 
-AC_DEFUN([gl_FUNC_MKTIME],
+dnl Test whether mktime works. Set gl_cv_func_working_mktime.
+AC_DEFUN([gl_FUNC_MKTIME_WORKS],
 [
-  AC_REQUIRE([gl_HEADER_TIME_H_DEFAULTS])
   AC_REQUIRE([gl_TIME_T_IS_SIGNED])
 
   dnl We don't use AC_FUNC_MKTIME any more, because it is no longer maintained
@@ -239,29 +239,50 @@ main ()
 }]])],
        [gl_cv_func_working_mktime=yes],
        [gl_cv_func_working_mktime=no],
-       [gl_cv_func_working_mktime=no])
+       [gl_cv_func_working_mktime="guessing no"])
     ])
+])
+
+dnl Main macro of module 'mktime'.
+AC_DEFUN([gl_FUNC_MKTIME],
+[
+  AC_REQUIRE([gl_HEADER_TIME_H_DEFAULTS])
+  AC_REQUIRE([AC_CANONICAL_HOST])
+  AC_REQUIRE([gl_FUNC_MKTIME_WORKS])
 
-  if test $gl_cv_func_working_mktime = no; then
+  REPLACE_MKTIME=0
+  if test "$gl_cv_func_working_mktime" != yes; then
     REPLACE_MKTIME=1
-  else
-    REPLACE_MKTIME=0
+    AC_DEFINE([NEED_MKTIME_WORKING], [1],
+      [Define if the compilation of mktime.c should define 'mktime'
+       with the algorithmic workarounds.])
   fi
+  case "$host_os" in
+    mingw*)
+      REPLACE_MKTIME=1
+      AC_DEFINE([NEED_MKTIME_WINDOWS], [1],
+        [Define if the compilation of mktime.c should define 'mktime'
+         with the native Windows TZ workaround.])
+      ;;
+  esac
 ])
 
+dnl Main macro of module 'mktime-internal'.
 AC_DEFUN([gl_FUNC_MKTIME_INTERNAL], [
-  AC_REQUIRE([gl_FUNC_MKTIME])
-  if test $REPLACE_MKTIME = 0; then
-    dnl BeOS has __mktime_internal in libc, but other platforms don't.
-    AC_CHECK_FUNC([__mktime_internal],
-      [AC_DEFINE([mktime_internal], [__mktime_internal],
-         [Define to the real name of the mktime_internal function.])
-      ],
-      [dnl mktime works but it doesn't export __mktime_internal,
-       dnl so we need to substitute our own mktime implementation.
-       REPLACE_MKTIME=1
-      ])
-  fi
+  AC_REQUIRE([gl_FUNC_MKTIME_WORKS])
+
+  WANT_MKTIME_INTERNAL=0
+  dnl BeOS has __mktime_internal in libc, but other platforms don't.
+  AC_CHECK_FUNC([__mktime_internal],
+    [AC_DEFINE([mktime_internal], [__mktime_internal],
+       [Define to the real name of the mktime_internal function.])
+    ],
+    [dnl mktime works but it doesn't export __mktime_internal,
+     dnl so we need to substitute our own mktime implementation.
+     WANT_MKTIME_INTERNAL=1
+     AC_DEFINE([NEED_MKTIME_INTERNAL], [1],
+       [Define if the compilation of mktime.c should define 
'mktime_internal'.])
+    ])
 ])
 
 # Prerequisites of lib/mktime.c.
diff --git a/m4/timegm.m4 b/m4/timegm.m4
index 510e25a..1f18552 100644
--- a/m4/timegm.m4
+++ b/m4/timegm.m4
@@ -1,4 +1,4 @@
-# timegm.m4 serial 11
+# timegm.m4 serial 12
 dnl Copyright (C) 2003, 2007, 2009-2017 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -7,11 +7,11 @@ dnl with or without modifications, as long as this notice is 
preserved.
 AC_DEFUN([gl_FUNC_TIMEGM],
 [
   AC_REQUIRE([gl_HEADER_TIME_H_DEFAULTS])
-  AC_REQUIRE([gl_FUNC_MKTIME])
+  AC_REQUIRE([gl_FUNC_MKTIME_WORKS])
   REPLACE_TIMEGM=0
   AC_CHECK_FUNCS_ONCE([timegm])
   if test $ac_cv_func_timegm = yes; then
-    if test $gl_cv_func_working_mktime = no; then
+    if test "$gl_cv_func_working_mktime" != yes; then
       # Assume that timegm is buggy if mktime is.
       REPLACE_TIMEGM=1
     fi
diff --git a/modules/mktime-internal b/modules/mktime-internal
index f9cf460..1465c90 100644
--- a/modules/mktime-internal
+++ b/modules/mktime-internal
@@ -10,7 +10,7 @@ mktime
 
 configure.ac:
 gl_FUNC_MKTIME_INTERNAL
-if test $REPLACE_MKTIME = 1; then
+if test $WANT_MKTIME_INTERNAL = 1; then
   AC_LIBOBJ([mktime])
   gl_PREREQ_MKTIME
 fi




reply via email to

[Prev in Thread] Current Thread [Next in Thread]