From db1ee11e2168af7137db53289a92e306d2277b0b Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Sun, 30 Apr 2017 16:34:54 +0200 Subject: [PATCH] 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. --- ChangeLog | 21 ++++++++++++ doc/posix-functions/mktime.texi | 6 ++-- lib/mktime.c | 47 ++++++++++++++++++++++++-- m4/mktime.m4 | 59 ++++++++++++++++++++++----------- m4/timegm.m4 | 6 ++-- modules/mktime-internal | 2 +- 6 files changed, 112 insertions(+), 29 deletions(-) diff --git a/ChangeLog b/ChangeLog index af7f731c4e..01cc13788f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,24 @@ +2017-04-30 Bruno Haible + + 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. + 2017-04-30 Bruno Haible localtime: New module. diff --git a/doc/posix-functions/mktime.texi b/doc/posix-functions/mktime.texi index ffb7b79e11..35a9a41e7e 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 2efd44a226..a78d960e53 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 #endif @@ -51,6 +64,13 @@ # define mktime my_mktime #endif +#if NEED_MKTIME_WINDOWS /* on native Windows */ +# include +# include +#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 d594ddc58b..31da65e8b2 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 510e25ab4b..1f18552e9f 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 f9cf4600d1..1465c90dcd 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 -- 2.39.5