> * 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 <br...@clisp.org> 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. +@item @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 -@item -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