diff mbox

[1/2] Merge mktime, timegm from upstream Gnulib

Message ID 20180909160625.15214-1-eggert@cs.ucla.edu
State New, archived
Headers show

Commit Message

Paul Eggert Sept. 9, 2018, 4:06 p.m. UTC
[BZ #23603][BZ #16346]
This fixes some obscure problems with integer overflow.
Although it looks scary, it is almost all a byte-for-byte copy
from Gnulib, and the Gnulib code has been tested reasonably well.
* include/intprops.h: New file, copied from Gnulib.
* include/verify.h, time/mktime-internal.h:
New tiny files, simplified from Gnulib.
* time/mktime.c: Copy from Gnulib.  This has the following changes:
Do not include config.h if DEBUG_MKTIME is nonzero.
Include stdbool.h, intprops.h, verify.h.
Include string.h only if needed.
Include stdlib.h on MS-Windows.
Include mktime-internal.h.
(DEBUG_MKTIME): Default to 0, and simplify later uses.
(NEED_MKTIME_INTERNAL, NEED_MKTIME_WINDOWS)
(NEED_MKTIME_WORKING): Give default values to pacify -Wundef,
which glibc uses.  Default NEED_MKTIME_WORKING to DEBUG_MKTIME, to
simplify later conditionals; default the others to zero.  Use
these conditionals to express only the code needed on the current
platform.  In uses of these conditionals, explicitly spell out how
_LIBC affects things, so it’s easier to review from a glibc
viewpoint.
(WRAPV): Remove; no longer needed now that we have
systematic overflow checking.
(my_tzset, __tzset) [!_LIBC]: New function and macro, to better
compartmentalize tzset issues.  Move system-dependent tzsettish
code here from mktime.
(verify): Remove; now done by verify.h.  All uses changed.
(long_int): Use a more-conservative definition, to avoid
integer overflow.
(SHR): Remove, replacing with ...
(shr): New function, which means we needn’t worry about side
effects in args, and conversion analysis is simpler.
(TYPE_IS_INTEGER, TYPE_TWOS_COMPLEMENT, TYPE_SIGNED, TYPE_MINIMUM)
(TYPE_MAXIMUM, TIME_T_MIN, TIME_T_MAX, TIME_T_MIDPOINT)
(time_t_avg, time_t_add_ok): Remove.
(mktime_min, mktime_max): New constants.
(leapyear, isdst_differ): Use bool for booleans.
(ydhms_diff, guess_time_tm, ranged_convert, __mktime_internal):
Use long_int, not time_t, for mktime differences.
(long_int_avg): New function, replacing time_t_avg.
INT_ADD_WRAPV replaces time_t_add_ok.
(guess_time_tm): 6th arg is now long_int, not time_t const *.
All uses changed.
(convert_time): New function.
(ranged_convert): Use it.
(__mktime_internal): Last arg now points to mktime_offset_t, not
time_t.  All uses changed.  This is a no-op on glibc, where
mktime_offset_t is always time_t.  Use int, not time_t, for UTC
offset guess.  Directly check for integer overflow instead of
using a heuristic that works only 99.9...% of the time.
Access *OFFSET only once, to avoid an unlikely race if the
compiler delays a load and if this cascades into a signed integer
overflow.
(mktime): Move tzsettish code to my_tzset, and move
localtime_offset to within mktime so that it doesn’t
need a separate ifdef.
(main) [DEBUG_MKTIME]: Speed up by using localtime_r
instead of localtime.
* time/timegm.c: Copy from Gnulib.  This has the following changes:
Include mktime-internal.h.
[!_LIBC]: Include config.h and time.h.  Do not include
timegm.h or time_r.h.  Make __mktime_internal a macro,
and include mktime-internal.h to get its declaration.
(timegm): Temporary is now mktime_offset_t, not time_t.
This affects only Gnulib.
---
 ChangeLog              |  70 ++++++
 include/intprops.h     | 455 ++++++++++++++++++++++++++++++++++
 include/verify.h       |   2 +
 time/mktime-internal.h |   1 +
 time/mktime.c          | 545 +++++++++++++++++++----------------------
 time/timegm.c          |  23 +-
 6 files changed, 780 insertions(+), 316 deletions(-)
 create mode 100644 include/intprops.h
 create mode 100644 include/verify.h
 create mode 100644 time/mktime-internal.h

Comments

Albert ARIBAUD Oct. 4, 2018, 11:41 a.m. UTC | #1
Hi Paul,

On Sun,  9 Sep 2018 09:06:24 -0700, Paul Eggert <eggert@cs.ucla.edu>
wrote :

> [BZ #23603][BZ #16346]
> This fixes some obscure problems with integer overflow.
> Although it looks scary, it is almost all a byte-for-byte copy
> from Gnulib, and the Gnulib code has been tested reasonably well.

I am having a weird issue with this one.

If I commit this patch above release/2.28.master and run a make check,
it hangs indefinitely at the timezone/test-tz check, with
ld-linux-x86-64 taking up 100% of the CPU.

Of course, same make check on release/2.28.master just below the
committed patch runs fine, with results which I find normal:

UNSUPPORTED: elf/tst-audit10
UNSUPPORTED: elf/tst-avx512
XPASS: elf/tst-protected1a
XPASS: elf/tst-protected1b
UNSUPPORTED: math/test-double-libmvec-alias-avx512
UNSUPPORTED: math/test-double-libmvec-alias-avx512-main
UNSUPPORTED: math/test-double-libmvec-sincos-avx512
UNSUPPORTED: math/test-float-libmvec-alias-avx512
UNSUPPORTED: math/test-float-libmvec-alias-avx512-main
UNSUPPORTED: math/test-float-libmvec-sincosf-avx512
UNSUPPORTED: misc/tst-pkey
FAIL: resolv/tst-resolv-ai_idn
FAIL: resolv/tst-resolv-ai_idn-latin1
Summary of test results:
      2 FAIL
   5924 PASS
      9 UNSUPPORTED
     17 XFAIL
      2 XPASS

Any clue as to why this happens?

Cordialement,
Albert ARIBAUD
3ADEV
Paul Eggert Oct. 8, 2018, 11:23 p.m. UTC | #2
On 10/4/18 4:41 AM, Albert ARIBAUD wrote:
> Any clue as to why this happens?

I'm not quite sure what you meant by "above release/2.28.master" and "on 
release/2.28.master just below the committed patch". However, one 
possible explanation for an infloop if the patch is improperly installed 
so that time/mktime.c is compiled for glibc without _LIBC being defined; 
in that case mktime's body could be just '__tzset (); return mktime 
(tp);' which would infloop. You can investigate this by disassembling 
mktime.
Albert ARIBAUD Oct. 9, 2018, 7:44 p.m. UTC | #3
Hi Paul,

On Mon, 8 Oct 2018 16:23:35 -0700, Paul Eggert <eggert@cs.ucla.edu>
wrote :

> On 10/4/18 4:41 AM, Albert ARIBAUD wrote:
> > Any clue as to why this happens?  
> 
> I'm not quite sure what you meant by "above release/2.28.master" and "on 
> release/2.28.master just below the committed patch". However, one 
> possible explanation for an infloop if the patch is improperly installed 
> so that time/mktime.c is compiled for glibc without _LIBC being defined; 
> in that case mktime's body could be just '__tzset (); return mktime 
> (tp);' which would infloop. You can investigate this by disassembling 
> mktime.

What I meant (apart from the typo in the branch name, sorry) is that I
had checked out the glibc branch 'release/2.28/master' into a local
branch of mine, and then applied your patch, extracted from your post
to the mailing list on September 9th at 18:06.

I have just compared this patch to the one obtained by running a 'git
diff HEAD^' at commit 8e6fd2bd, which contains your change as applied
to the glibc master branch, and I see one difference: the mailing
list diff has a line in the time/mktime.c part which reads

	+# if defined __LIBC || NEED_MKTIME_WORKING

whereas the git diff HEAD^ from commit 8e6fd2bd has the line

	# if defined _LIBC || NEED_MKTIME_WORKING

Clearly there was a change in the number of underscores before "LIBC",
two in the mailing list version, one in the git version; this is the
only difference between the two diffs.

The "__LIBC" in the patch appears to be a typo, and does in fact make
mktime() call itself recursively when building for glibc, consistent
with the freeze I encountered.

I've just triple-checked the mailing list to make sure I did not
miss any reply to your patch or any announcement of it being hot-fixed
while applied, or any post at all regarding that typo. There was none,
so I had no way of knowing that the patch was in fact incorrect.

Now that I have identified the typo, and fixed it in my own branch, I
have re-run make check and got exactly the same results as the 2.28
master branch gets.

Cordialement,
Albert ARIBAUD
3ADEV
Florian Weimer Oct. 18, 2018, 8:01 p.m. UTC | #4
* Paul Eggert:

> [BZ #23603][BZ #16346]
> This fixes some obscure problems with integer overflow.

This patch breaks time/tst-mktime2 on armv7l-unknown-linux-gnueabihf,
with GCC 7.3:

tst-mktime2.c:70: numeric comparison failure
   left: -1 (0xffffffff); from: mktime (lt)
  right: 2147483647 (0x7fffffff); from: now
tst-mktime2.c:70: numeric comparison failure
   left: -1 (0xffffffff); from: mktime (lt)
  right: -2147483648 (0x80000000); from: now
tst-mktime2.c:70: numeric comparison failure
   left: -1 (0xffffffff); from: mktime (lt)
  right: 2147483646 (0x7ffffffe); from: now
tst-mktime2.c:70: numeric comparison failure
   left: -1 (0xffffffff); from: mktime (lt)
  right: -2147483647 (0x80000001); from: now
error: 4 test failures

This is after patching the test like this:

 65 static void
 66 mktime_test1 (time_t now)
 67 {
 68   struct tm *lt = localtime (&now);
 69   TEST_VERIFY_EXIT (lt != NULL);
 70   TEST_COMPARE (mktime (lt), now);
 71 }

(I will submit the test changes separately.)

Is this the expected new behavior on 32-bit architectures?

Thanks,
Florian
Florian Weimer Oct. 18, 2018, 8:12 p.m. UTC | #5
* Florian Weimer:

> * Paul Eggert:
>
>> [BZ #23603][BZ #16346]
>> This fixes some obscure problems with integer overflow.
>
> This patch breaks time/tst-mktime2 on armv7l-unknown-linux-gnueabihf,
> with GCC 7.3:
>
> tst-mktime2.c:70: numeric comparison failure
>    left: -1 (0xffffffff); from: mktime (lt)
>   right: 2147483647 (0x7fffffff); from: now
> tst-mktime2.c:70: numeric comparison failure
>    left: -1 (0xffffffff); from: mktime (lt)
>   right: -2147483648 (0x80000000); from: now
> tst-mktime2.c:70: numeric comparison failure
>    left: -1 (0xffffffff); from: mktime (lt)
>   right: 2147483646 (0x7ffffffe); from: now
> tst-mktime2.c:70: numeric comparison failure
>    left: -1 (0xffffffff); from: mktime (lt)
>   right: -2147483647 (0x80000001); from: now
> error: 4 test failures
>
> This is after patching the test like this:
>
>  65 static void
>  66 mktime_test1 (time_t now)
>  67 {
>  68   struct tm *lt = localtime (&now);
>  69   TEST_VERIFY_EXIT (lt != NULL);
>  70   TEST_COMPARE (mktime (lt), now);
>  71 }

It happens for the /usr/share/zoneinfo/America/Sao_Paulo file from
tzdata-2018e-1.fc27.  So the test case is not properly isolated, either.

Thanks,
Florian
Paul Eggert Nov. 10, 2018, 3:46 a.m. UTC | #6
Florian Weimer wrote:
> Is this the expected new behavior on 32-bit architectures?

No, tst-mktime2.c should still work on 32-bit architectures, and it does work 
for me with glibc master on i686 with GCC 7.3. So I'm a bit puzzled why it 
doesn't work for armv7l-unknown-linux-gnueabihf with GCC 7.3. Almost surely this 
is a bug I introduced, though it is possible it's a bug in localtime that the 
new mktime unearthed.

With a bit of luck the patches I just proposed in 
<https://www.sourceware.org/ml/libc-alpha/2018-11/msg00239.html> will fix the 
problem you reported, as there are indeed some obscure bugs in the current 
master near the Y2038 boundary.
Paul Eggert Nov. 10, 2018, 3:51 a.m. UTC | #7
Florian Weimer wrote:
> It happens for the /usr/share/zoneinfo/America/Sao_Paulo file from
> tzdata-2018e-1.fc27.  So the test case is not properly isolated, either.

The test case should work regardless of what's in that file, and should work 
even if that file does not exist or if there's an I/O error reading it. As I 
vaguely recall, the original bug report was against TZ=America/Sao_Paulo and 
that's why the test case uses that particular setting.

It'd probably be good to add "<-03>3<-02>,M11.1.0/0,M2.3.0/0" to the tz_strings 
array in that test case, to do a southern-hemisphere-style test even if the 
Sao_Paulo file contains garbage or does not exist. I don't recall the original 
problem well enough to know whether such an addition to tz_strings would suss 
out the Sao_Paulo bug.
diff mbox

Patch

diff --git a/ChangeLog b/ChangeLog
index 611caf9bd8..68f6728290 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,73 @@ 
+2018-09-09  Paul Eggert  <eggert@cs.ucla.edu>
+
+	Merge mktime, timegm from upstream Gnulib
+	[BZ #23603][BZ #16346]
+	This fixes some obscure problems with integer overflow.
+	Although it looks scary, it is almost all a byte-for-byte copy
+	from Gnulib, and the Gnulib code has been tested reasonably well.
+	* include/intprops.h: New file, copied from Gnulib.
+	* include/verify.h, time/mktime-internal.h:
+	New tiny files, simplified from Gnulib.
+	* time/mktime.c: Copy from Gnulib.  This has the following changes:
+	Do not include config.h if DEBUG_MKTIME is nonzero.
+	Include stdbool.h, intprops.h, verify.h.
+	Include string.h only if needed.
+	Include stdlib.h on MS-Windows.
+	Include mktime-internal.h.
+	(DEBUG_MKTIME): Default to 0, and simplify later uses.
+	(NEED_MKTIME_INTERNAL, NEED_MKTIME_WINDOWS)
+	(NEED_MKTIME_WORKING): Give default values to pacify -Wundef,
+	which glibc uses.  Default NEED_MKTIME_WORKING to DEBUG_MKTIME, to
+	simplify later conditionals; default the others to zero.  Use
+	these conditionals to express only the code needed on the current
+	platform.  In uses of these conditionals, explicitly spell out how
+	_LIBC affects things, so it’s easier to review from a glibc
+	viewpoint.
+	(WRAPV): Remove; no longer needed now that we have
+	systematic overflow checking.
+	(my_tzset, __tzset) [!_LIBC]: New function and macro, to better
+	compartmentalize tzset issues.  Move system-dependent tzsettish
+	code here from mktime.
+	(verify): Remove; now done by verify.h.  All uses changed.
+	(long_int): Use a more-conservative definition, to avoid
+	integer overflow.
+	(SHR): Remove, replacing with ...
+	(shr): New function, which means we needn’t worry about side
+	effects in args, and conversion analysis is simpler.
+	(TYPE_IS_INTEGER, TYPE_TWOS_COMPLEMENT, TYPE_SIGNED, TYPE_MINIMUM)
+	(TYPE_MAXIMUM, TIME_T_MIN, TIME_T_MAX, TIME_T_MIDPOINT)
+	(time_t_avg, time_t_add_ok): Remove.
+	(mktime_min, mktime_max): New constants.
+	(leapyear, isdst_differ): Use bool for booleans.
+	(ydhms_diff, guess_time_tm, ranged_convert, __mktime_internal):
+	Use long_int, not time_t, for mktime differences.
+	(long_int_avg): New function, replacing time_t_avg.
+	INT_ADD_WRAPV replaces time_t_add_ok.
+	(guess_time_tm): 6th arg is now long_int, not time_t const *.
+	All uses changed.
+	(convert_time): New function.
+	(ranged_convert): Use it.
+	(__mktime_internal): Last arg now points to mktime_offset_t, not
+	time_t.  All uses changed.  This is a no-op on glibc, where
+	mktime_offset_t is always time_t.  Use int, not time_t, for UTC
+	offset guess.  Directly check for integer overflow instead of
+	using a heuristic that works only 99.9...% of the time.
+	Access *OFFSET only once, to avoid an unlikely race if the
+	compiler delays a load and if this cascades into a signed integer
+	overflow.
+	(mktime): Move tzsettish code to my_tzset, and move
+	localtime_offset to within mktime so that it doesn’t
+	need a separate ifdef.
+	(main) [DEBUG_MKTIME]: Speed up by using localtime_r
+	instead of localtime.
+	* time/timegm.c: Copy from Gnulib.  This has the following changes:
+	Include mktime-internal.h.
+	[!_LIBC]: Include config.h and time.h.  Do not include
+	timegm.h or time_r.h.  Make __mktime_internal a macro,
+	and include mktime-internal.h to get its declaration.
+	(timegm): Temporary is now mktime_offset_t, not time_t.
+	This affects only Gnulib.
+
 2018-09-06  Stefan Liebler  <stli@linux.ibm.com>
 
 	* sysdeps/s390/fpu/libm-test-ulps: Regenerated.
diff --git a/include/intprops.h b/include/intprops.h
new file mode 100644
index 0000000000..9702aec4c6
--- /dev/null
+++ b/include/intprops.h
@@ -0,0 +1,455 @@ 
+/* intprops.h -- properties of integer types
+
+   Copyright (C) 2001-2018 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify it
+   under the terms of the GNU Lesser General Public License as published
+   by the Free Software Foundation; either version 2.1 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Paul Eggert.  */
+
+#ifndef _GL_INTPROPS_H
+#define _GL_INTPROPS_H
+
+#include <limits.h>
+
+/* Return a value with the common real type of E and V and the value of V.
+   Do not evaluate E.  */
+#define _GL_INT_CONVERT(e, v) ((1 ? 0 : (e)) + (v))
+
+/* Act like _GL_INT_CONVERT (E, -V) but work around a bug in IRIX 6.5 cc; see
+   <https://lists.gnu.org/r/bug-gnulib/2011-05/msg00406.html>.  */
+#define _GL_INT_NEGATE_CONVERT(e, v) ((1 ? 0 : (e)) - (v))
+
+/* The extra casts in the following macros work around compiler bugs,
+   e.g., in Cray C 5.0.3.0.  */
+
+/* True if the arithmetic type T is an integer type.  bool counts as
+   an integer.  */
+#define TYPE_IS_INTEGER(t) ((t) 1.5 == 1)
+
+/* True if the real type T is signed.  */
+#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
+
+/* Return 1 if the real expression E, after promotion, has a
+   signed or floating type.  Do not evaluate E.  */
+#define EXPR_SIGNED(e) (_GL_INT_NEGATE_CONVERT (e, 1) < 0)
+
+
+/* Minimum and maximum values for integer types and expressions.  */
+
+/* The width in bits of the integer type or expression T.
+   Do not evaluate T.
+   Padding bits are not supported; this is checked at compile-time below.  */
+#define TYPE_WIDTH(t) (sizeof (t) * CHAR_BIT)
+
+/* The maximum and minimum values for the integer type T.  */
+#define TYPE_MINIMUM(t) ((t) ~ TYPE_MAXIMUM (t))
+#define TYPE_MAXIMUM(t)                                                 \
+  ((t) (! TYPE_SIGNED (t)                                               \
+        ? (t) -1                                                        \
+        : ((((t) 1 << (TYPE_WIDTH (t) - 2)) - 1) * 2 + 1)))
+
+/* The maximum and minimum values for the type of the expression E,
+   after integer promotion.  E is not evaluated.  */
+#define _GL_INT_MINIMUM(e)                                              \
+  (EXPR_SIGNED (e)                                                      \
+   ? ~ _GL_SIGNED_INT_MAXIMUM (e)                                       \
+   : _GL_INT_CONVERT (e, 0))
+#define _GL_INT_MAXIMUM(e)                                              \
+  (EXPR_SIGNED (e)                                                      \
+   ? _GL_SIGNED_INT_MAXIMUM (e)                                         \
+   : _GL_INT_NEGATE_CONVERT (e, 1))
+#define _GL_SIGNED_INT_MAXIMUM(e)                                       \
+  (((_GL_INT_CONVERT (e, 1) << (TYPE_WIDTH ((e) + 0) - 2)) - 1) * 2 + 1)
+
+/* Work around OpenVMS incompatibility with C99.  */
+#if !defined LLONG_MAX && defined __INT64_MAX
+# define LLONG_MAX __INT64_MAX
+# define LLONG_MIN __INT64_MIN
+#endif
+
+/* This include file assumes that signed types are two's complement without
+   padding bits; the above macros have undefined behavior otherwise.
+   If this is a problem for you, please let us know how to fix it for your host.
+   This assumption is tested by the intprops-tests module.  */
+
+/* Does the __typeof__ keyword work?  This could be done by
+   'configure', but for now it's easier to do it by hand.  */
+#if (2 <= __GNUC__ \
+     || (1210 <= __IBMC__ && defined __IBM__TYPEOF__) \
+     || (0x5110 <= __SUNPRO_C && !__STDC__))
+# define _GL_HAVE___TYPEOF__ 1
+#else
+# define _GL_HAVE___TYPEOF__ 0
+#endif
+
+/* Return 1 if the integer type or expression T might be signed.  Return 0
+   if it is definitely unsigned.  This macro does not evaluate its argument,
+   and expands to an integer constant expression.  */
+#if _GL_HAVE___TYPEOF__
+# define _GL_SIGNED_TYPE_OR_EXPR(t) TYPE_SIGNED (__typeof__ (t))
+#else
+# define _GL_SIGNED_TYPE_OR_EXPR(t) 1
+#endif
+
+/* Bound on length of the string representing an unsigned integer
+   value representable in B bits.  log10 (2.0) < 146/485.  The
+   smallest value of B where this bound is not tight is 2621.  */
+#define INT_BITS_STRLEN_BOUND(b) (((b) * 146 + 484) / 485)
+
+/* Bound on length of the string representing an integer type or expression T.
+   Subtract 1 for the sign bit if T is signed, and then add 1 more for
+   a minus sign if needed.
+
+   Because _GL_SIGNED_TYPE_OR_EXPR sometimes returns 0 when its argument is
+   signed, this macro may overestimate the true bound by one byte when
+   applied to unsigned types of size 2, 4, 16, ... bytes.  */
+#define INT_STRLEN_BOUND(t)                                     \
+  (INT_BITS_STRLEN_BOUND (TYPE_WIDTH (t) - _GL_SIGNED_TYPE_OR_EXPR (t)) \
+   + _GL_SIGNED_TYPE_OR_EXPR (t))
+
+/* Bound on buffer size needed to represent an integer type or expression T,
+   including the terminating null.  */
+#define INT_BUFSIZE_BOUND(t) (INT_STRLEN_BOUND (t) + 1)
+
+
+/* Range overflow checks.
+
+   The INT_<op>_RANGE_OVERFLOW macros return 1 if the corresponding C
+   operators might not yield numerically correct answers due to
+   arithmetic overflow.  They do not rely on undefined or
+   implementation-defined behavior.  Their implementations are simple
+   and straightforward, but they are a bit harder to use than the
+   INT_<op>_OVERFLOW macros described below.
+
+   Example usage:
+
+     long int i = ...;
+     long int j = ...;
+     if (INT_MULTIPLY_RANGE_OVERFLOW (i, j, LONG_MIN, LONG_MAX))
+       printf ("multiply would overflow");
+     else
+       printf ("product is %ld", i * j);
+
+   Restrictions on *_RANGE_OVERFLOW macros:
+
+   These macros do not check for all possible numerical problems or
+   undefined or unspecified behavior: they do not check for division
+   by zero, for bad shift counts, or for shifting negative numbers.
+
+   These macros may evaluate their arguments zero or multiple times,
+   so the arguments should not have side effects.  The arithmetic
+   arguments (including the MIN and MAX arguments) must be of the same
+   integer type after the usual arithmetic conversions, and the type
+   must have minimum value MIN and maximum MAX.  Unsigned types should
+   use a zero MIN of the proper type.
+
+   These macros are tuned for constant MIN and MAX.  For commutative
+   operations such as A + B, they are also tuned for constant B.  */
+
+/* Return 1 if A + B would overflow in [MIN,MAX] arithmetic.
+   See above for restrictions.  */
+#define INT_ADD_RANGE_OVERFLOW(a, b, min, max)          \
+  ((b) < 0                                              \
+   ? (a) < (min) - (b)                                  \
+   : (max) - (b) < (a))
+
+/* Return 1 if A - B would overflow in [MIN,MAX] arithmetic.
+   See above for restrictions.  */
+#define INT_SUBTRACT_RANGE_OVERFLOW(a, b, min, max)     \
+  ((b) < 0                                              \
+   ? (max) + (b) < (a)                                  \
+   : (a) < (min) + (b))
+
+/* Return 1 if - A would overflow in [MIN,MAX] arithmetic.
+   See above for restrictions.  */
+#define INT_NEGATE_RANGE_OVERFLOW(a, min, max)          \
+  ((min) < 0                                            \
+   ? (a) < - (max)                                      \
+   : 0 < (a))
+
+/* Return 1 if A * B would overflow in [MIN,MAX] arithmetic.
+   See above for restrictions.  Avoid && and || as they tickle
+   bugs in Sun C 5.11 2010/08/13 and other compilers; see
+   <https://lists.gnu.org/r/bug-gnulib/2011-05/msg00401.html>.  */
+#define INT_MULTIPLY_RANGE_OVERFLOW(a, b, min, max)     \
+  ((b) < 0                                              \
+   ? ((a) < 0                                           \
+      ? (a) < (max) / (b)                               \
+      : (b) == -1                                       \
+      ? 0                                               \
+      : (min) / (b) < (a))                              \
+   : (b) == 0                                           \
+   ? 0                                                  \
+   : ((a) < 0                                           \
+      ? (a) < (min) / (b)                               \
+      : (max) / (b) < (a)))
+
+/* Return 1 if A / B would overflow in [MIN,MAX] arithmetic.
+   See above for restrictions.  Do not check for division by zero.  */
+#define INT_DIVIDE_RANGE_OVERFLOW(a, b, min, max)       \
+  ((min) < 0 && (b) == -1 && (a) < - (max))
+
+/* Return 1 if A % B would overflow in [MIN,MAX] arithmetic.
+   See above for restrictions.  Do not check for division by zero.
+   Mathematically, % should never overflow, but on x86-like hosts
+   INT_MIN % -1 traps, and the C standard permits this, so treat this
+   as an overflow too.  */
+#define INT_REMAINDER_RANGE_OVERFLOW(a, b, min, max)    \
+  INT_DIVIDE_RANGE_OVERFLOW (a, b, min, max)
+
+/* Return 1 if A << B would overflow in [MIN,MAX] arithmetic.
+   See above for restrictions.  Here, MIN and MAX are for A only, and B need
+   not be of the same type as the other arguments.  The C standard says that
+   behavior is undefined for shifts unless 0 <= B < wordwidth, and that when
+   A is negative then A << B has undefined behavior and A >> B has
+   implementation-defined behavior, but do not check these other
+   restrictions.  */
+#define INT_LEFT_SHIFT_RANGE_OVERFLOW(a, b, min, max)   \
+  ((a) < 0                                              \
+   ? (a) < (min) >> (b)                                 \
+   : (max) >> (b) < (a))
+
+/* True if __builtin_add_overflow (A, B, P) works when P is non-null.  */
+#if 5 <= __GNUC__ && !defined __ICC
+# define _GL_HAS_BUILTIN_OVERFLOW 1
+#else
+# define _GL_HAS_BUILTIN_OVERFLOW 0
+#endif
+
+/* True if __builtin_add_overflow_p (A, B, C) works.  */
+#define _GL_HAS_BUILTIN_OVERFLOW_P (7 <= __GNUC__)
+
+/* The _GL*_OVERFLOW macros have the same restrictions as the
+   *_RANGE_OVERFLOW macros, except that they do not assume that operands
+   (e.g., A and B) have the same type as MIN and MAX.  Instead, they assume
+   that the result (e.g., A + B) has that type.  */
+#if _GL_HAS_BUILTIN_OVERFLOW_P
+# define _GL_ADD_OVERFLOW(a, b, min, max)                               \
+   __builtin_add_overflow_p (a, b, (__typeof__ ((a) + (b))) 0)
+# define _GL_SUBTRACT_OVERFLOW(a, b, min, max)                          \
+   __builtin_sub_overflow_p (a, b, (__typeof__ ((a) - (b))) 0)
+# define _GL_MULTIPLY_OVERFLOW(a, b, min, max)                          \
+   __builtin_mul_overflow_p (a, b, (__typeof__ ((a) * (b))) 0)
+#else
+# define _GL_ADD_OVERFLOW(a, b, min, max)                                \
+   ((min) < 0 ? INT_ADD_RANGE_OVERFLOW (a, b, min, max)                  \
+    : (a) < 0 ? (b) <= (a) + (b)                                         \
+    : (b) < 0 ? (a) <= (a) + (b)                                         \
+    : (a) + (b) < (b))
+# define _GL_SUBTRACT_OVERFLOW(a, b, min, max)                           \
+   ((min) < 0 ? INT_SUBTRACT_RANGE_OVERFLOW (a, b, min, max)             \
+    : (a) < 0 ? 1                                                        \
+    : (b) < 0 ? (a) - (b) <= (a)                                         \
+    : (a) < (b))
+# define _GL_MULTIPLY_OVERFLOW(a, b, min, max)                           \
+   (((min) == 0 && (((a) < 0 && 0 < (b)) || ((b) < 0 && 0 < (a))))       \
+    || INT_MULTIPLY_RANGE_OVERFLOW (a, b, min, max))
+#endif
+#define _GL_DIVIDE_OVERFLOW(a, b, min, max)                             \
+  ((min) < 0 ? (b) == _GL_INT_NEGATE_CONVERT (min, 1) && (a) < - (max)  \
+   : (a) < 0 ? (b) <= (a) + (b) - 1                                     \
+   : (b) < 0 && (a) + (b) <= (a))
+#define _GL_REMAINDER_OVERFLOW(a, b, min, max)                          \
+  ((min) < 0 ? (b) == _GL_INT_NEGATE_CONVERT (min, 1) && (a) < - (max)  \
+   : (a) < 0 ? (a) % (b) != ((max) - (b) + 1) % (b)                     \
+   : (b) < 0 && ! _GL_UNSIGNED_NEG_MULTIPLE (a, b, max))
+
+/* Return a nonzero value if A is a mathematical multiple of B, where
+   A is unsigned, B is negative, and MAX is the maximum value of A's
+   type.  A's type must be the same as (A % B)'s type.  Normally (A %
+   -B == 0) suffices, but things get tricky if -B would overflow.  */
+#define _GL_UNSIGNED_NEG_MULTIPLE(a, b, max)                            \
+  (((b) < -_GL_SIGNED_INT_MAXIMUM (b)                                   \
+    ? (_GL_SIGNED_INT_MAXIMUM (b) == (max)                              \
+       ? (a)                                                            \
+       : (a) % (_GL_INT_CONVERT (a, _GL_SIGNED_INT_MAXIMUM (b)) + 1))   \
+    : (a) % - (b))                                                      \
+   == 0)
+
+/* Check for integer overflow, and report low order bits of answer.
+
+   The INT_<op>_OVERFLOW macros return 1 if the corresponding C operators
+   might not yield numerically correct answers due to arithmetic overflow.
+   The INT_<op>_WRAPV macros also store the low-order bits of the answer.
+   These macros work correctly on all known practical hosts, and do not rely
+   on undefined behavior due to signed arithmetic overflow.
+
+   Example usage, assuming A and B are long int:
+
+     if (INT_MULTIPLY_OVERFLOW (a, b))
+       printf ("result would overflow\n");
+     else
+       printf ("result is %ld (no overflow)\n", a * b);
+
+   Example usage with WRAPV flavor:
+
+     long int result;
+     bool overflow = INT_MULTIPLY_WRAPV (a, b, &result);
+     printf ("result is %ld (%s)\n", result,
+             overflow ? "after overflow" : "no overflow");
+
+   Restrictions on these macros:
+
+   These macros do not check for all possible numerical problems or
+   undefined or unspecified behavior: they do not check for division
+   by zero, for bad shift counts, or for shifting negative numbers.
+
+   These macros may evaluate their arguments zero or multiple times, so the
+   arguments should not have side effects.
+
+   The WRAPV macros are not constant expressions.  They support only
+   +, binary -, and *.  The result type must be signed.
+
+   These macros are tuned for their last argument being a constant.
+
+   Return 1 if the integer expressions A * B, A - B, -A, A * B, A / B,
+   A % B, and A << B would overflow, respectively.  */
+
+#define INT_ADD_OVERFLOW(a, b) \
+  _GL_BINARY_OP_OVERFLOW (a, b, _GL_ADD_OVERFLOW)
+#define INT_SUBTRACT_OVERFLOW(a, b) \
+  _GL_BINARY_OP_OVERFLOW (a, b, _GL_SUBTRACT_OVERFLOW)
+#if _GL_HAS_BUILTIN_OVERFLOW_P
+# define INT_NEGATE_OVERFLOW(a) INT_SUBTRACT_OVERFLOW (0, a)
+#else
+# define INT_NEGATE_OVERFLOW(a) \
+   INT_NEGATE_RANGE_OVERFLOW (a, _GL_INT_MINIMUM (a), _GL_INT_MAXIMUM (a))
+#endif
+#define INT_MULTIPLY_OVERFLOW(a, b) \
+  _GL_BINARY_OP_OVERFLOW (a, b, _GL_MULTIPLY_OVERFLOW)
+#define INT_DIVIDE_OVERFLOW(a, b) \
+  _GL_BINARY_OP_OVERFLOW (a, b, _GL_DIVIDE_OVERFLOW)
+#define INT_REMAINDER_OVERFLOW(a, b) \
+  _GL_BINARY_OP_OVERFLOW (a, b, _GL_REMAINDER_OVERFLOW)
+#define INT_LEFT_SHIFT_OVERFLOW(a, b) \
+  INT_LEFT_SHIFT_RANGE_OVERFLOW (a, b, \
+                                 _GL_INT_MINIMUM (a), _GL_INT_MAXIMUM (a))
+
+/* Return 1 if the expression A <op> B would overflow,
+   where OP_RESULT_OVERFLOW (A, B, MIN, MAX) does the actual test,
+   assuming MIN and MAX are the minimum and maximum for the result type.
+   Arguments should be free of side effects.  */
+#define _GL_BINARY_OP_OVERFLOW(a, b, op_result_overflow)        \
+  op_result_overflow (a, b,                                     \
+                      _GL_INT_MINIMUM (_GL_INT_CONVERT (a, b)), \
+                      _GL_INT_MAXIMUM (_GL_INT_CONVERT (a, b)))
+
+/* Store the low-order bits of A + B, A - B, A * B, respectively, into *R.
+   Return 1 if the result overflows.  See above for restrictions.  */
+#define INT_ADD_WRAPV(a, b, r) \
+  _GL_INT_OP_WRAPV (a, b, r, +, __builtin_add_overflow, INT_ADD_OVERFLOW)
+#define INT_SUBTRACT_WRAPV(a, b, r) \
+  _GL_INT_OP_WRAPV (a, b, r, -, __builtin_sub_overflow, INT_SUBTRACT_OVERFLOW)
+#define INT_MULTIPLY_WRAPV(a, b, r) \
+  _GL_INT_OP_WRAPV (a, b, r, *, __builtin_mul_overflow, INT_MULTIPLY_OVERFLOW)
+
+/* Nonzero if this compiler has GCC bug 68193 or Clang bug 25390.  See:
+   https://gcc.gnu.org/bugzilla/show_bug.cgi?id=68193
+   https://llvm.org/bugs/show_bug.cgi?id=25390
+   For now, assume all versions of GCC-like compilers generate bogus
+   warnings for _Generic.  This matters only for older compilers that
+   lack __builtin_add_overflow.  */
+#if __GNUC__
+# define _GL__GENERIC_BOGUS 1
+#else
+# define _GL__GENERIC_BOGUS 0
+#endif
+
+/* Store the low-order bits of A <op> B into *R, where OP specifies
+   the operation.  BUILTIN is the builtin operation, and OVERFLOW the
+   overflow predicate.  Return 1 if the result overflows.  See above
+   for restrictions.  */
+#if _GL_HAS_BUILTIN_OVERFLOW
+# define _GL_INT_OP_WRAPV(a, b, r, op, builtin, overflow) builtin (a, b, r)
+#elif 201112 <= __STDC_VERSION__ && !_GL__GENERIC_BOGUS
+# define _GL_INT_OP_WRAPV(a, b, r, op, builtin, overflow) \
+   (_Generic \
+    (*(r), \
+     signed char: \
+       _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned int, \
+                        signed char, SCHAR_MIN, SCHAR_MAX), \
+     short int: \
+       _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned int, \
+                        short int, SHRT_MIN, SHRT_MAX), \
+     int: \
+       _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned int, \
+                        int, INT_MIN, INT_MAX), \
+     long int: \
+       _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned long int, \
+                        long int, LONG_MIN, LONG_MAX), \
+     long long int: \
+       _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned long long int, \
+                        long long int, LLONG_MIN, LLONG_MAX)))
+#else
+# define _GL_INT_OP_WRAPV(a, b, r, op, builtin, overflow) \
+   (sizeof *(r) == sizeof (signed char) \
+    ? _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned int, \
+                       signed char, SCHAR_MIN, SCHAR_MAX) \
+    : sizeof *(r) == sizeof (short int) \
+    ? _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned int, \
+                       short int, SHRT_MIN, SHRT_MAX) \
+    : sizeof *(r) == sizeof (int) \
+    ? _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned int, \
+                       int, INT_MIN, INT_MAX) \
+    : _GL_INT_OP_WRAPV_LONGISH(a, b, r, op, overflow))
+# ifdef LLONG_MAX
+#  define _GL_INT_OP_WRAPV_LONGISH(a, b, r, op, overflow) \
+    (sizeof *(r) == sizeof (long int) \
+     ? _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned long int, \
+                        long int, LONG_MIN, LONG_MAX) \
+     : _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned long long int, \
+                        long long int, LLONG_MIN, LLONG_MAX))
+# else
+#  define _GL_INT_OP_WRAPV_LONGISH(a, b, r, op, overflow) \
+    _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned long int, \
+                     long int, LONG_MIN, LONG_MAX)
+# endif
+#endif
+
+/* Store the low-order bits of A <op> B into *R, where the operation
+   is given by OP.  Use the unsigned type UT for calculation to avoid
+   overflow problems.  *R's type is T, with extrema TMIN and TMAX.
+   T must be a signed integer type.  Return 1 if the result overflows.  */
+#define _GL_INT_OP_CALC(a, b, r, op, overflow, ut, t, tmin, tmax) \
+  (sizeof ((a) op (b)) < sizeof (t) \
+   ? _GL_INT_OP_CALC1 ((t) (a), (t) (b), r, op, overflow, ut, t, tmin, tmax) \
+   : _GL_INT_OP_CALC1 (a, b, r, op, overflow, ut, t, tmin, tmax))
+#define _GL_INT_OP_CALC1(a, b, r, op, overflow, ut, t, tmin, tmax) \
+  ((overflow (a, b) \
+    || (EXPR_SIGNED ((a) op (b)) && ((a) op (b)) < (tmin)) \
+    || (tmax) < ((a) op (b))) \
+   ? (*(r) = _GL_INT_OP_WRAPV_VIA_UNSIGNED (a, b, op, ut, t), 1) \
+   : (*(r) = _GL_INT_OP_WRAPV_VIA_UNSIGNED (a, b, op, ut, t), 0))
+
+/* Return the low-order bits of A <op> B, where the operation is given
+   by OP.  Use the unsigned type UT for calculation to avoid undefined
+   behavior on signed integer overflow, and convert the result to type T.
+   UT is at least as wide as T and is no narrower than unsigned int,
+   T is two's complement, and there is no padding or trap representations.
+   Assume that converting UT to T yields the low-order bits, as is
+   done in all known two's-complement C compilers.  E.g., see:
+   https://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html
+
+   According to the C standard, converting UT to T yields an
+   implementation-defined result or signal for values outside T's
+   range.  However, code that works around this theoretical problem
+   runs afoul of a compiler bug in Oracle Studio 12.3 x86.  See:
+   https://lists.gnu.org/r/bug-gnulib/2017-04/msg00049.html
+   As the compiler bug is real, don't try to work around the
+   theoretical problem.  */
+
+#define _GL_INT_OP_WRAPV_VIA_UNSIGNED(a, b, op, ut, t) \
+  ((t) ((ut) (a) op (ut) (b)))
+
+#endif /* _GL_INTPROPS_H */
diff --git a/include/verify.h b/include/verify.h
new file mode 100644
index 0000000000..00e78d3f8c
--- /dev/null
+++ b/include/verify.h
@@ -0,0 +1,2 @@ 
+/* Gnulib <verify.h>, simplified by assuming GCC 4.6 or later.  */
+#define verify(R) _Static_assert (R, "verify (" #R ")")
diff --git a/time/mktime-internal.h b/time/mktime-internal.h
new file mode 100644
index 0000000000..ba1d9c7aab
--- /dev/null
+++ b/time/mktime-internal.h
@@ -0,0 +1 @@ 
+typedef time_t mktime_offset_t;
diff --git a/time/mktime.c b/time/mktime.c
index 5f038a212f..4ff74901e4 100644
--- a/time/mktime.c
+++ b/time/mktime.c
@@ -15,13 +15,30 @@ 
 
    You should have received a copy of the GNU Lesser General Public
    License along with the GNU C Library; if not, see
-   <http://www.gnu.org/licenses/>.  */
+   <https://www.gnu.org/licenses/>.  */
 
-/* Define this to have a standalone program to test this implementation of
+/* Define this to 1 to have a standalone program to test this implementation of
    mktime.  */
-/* #define DEBUG_MKTIME 1 */
+#ifndef DEBUG_MKTIME
+# define DEBUG_MKTIME 0
+#endif
 
-#ifndef _LIBC
+/* The following macros influence what gets defined when this file is compiled:
+
+   Macro/expression            Which gnulib module    This compilation unit
+                                                      should define
+
+   _LIBC                       (glibc proper)         mktime
+
+   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
 
@@ -35,114 +52,128 @@ 
 #include <time.h>
 
 #include <limits.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
 
-#include <string.h>		/* For the real memcpy prototype.  */
+#include <intprops.h>
+#include <verify.h>
 
-#if defined DEBUG_MKTIME && DEBUG_MKTIME
+#if DEBUG_MKTIME
 # include <stdio.h>
-# include <stdlib.h>
 /* Make it work even if the system's libc has its own mktime routine.  */
 # undef mktime
 # define mktime my_mktime
 #endif /* DEBUG_MKTIME */
 
-/* Some of the code in this file assumes that signed integer overflow
-   silently wraps around.  This assumption can't easily be programmed
-   around, nor can it be checked for portably at compile-time or
-   easily eliminated at run-time.
-
-   Define WRAPV to 1 if the assumption is valid and if
-     #pragma GCC optimize ("wrapv")
-   does not trigger GCC bug 51793
-   <http://gcc.gnu.org/bugzilla/show_bug.cgi?id=51793>.
-   Otherwise, define it to 0; this forces the use of slower code that,
-   while not guaranteed by the C Standard, works on all production
-   platforms that we know about.  */
-#ifndef WRAPV
-# if (((__GNUC__ == 4 && 4 <= __GNUC_MINOR__) || 4 < __GNUC__) \
-      && defined __GLIBC__)
-#  pragma GCC optimize ("wrapv")
-#  define WRAPV 1
-# else
-#  define WRAPV 0
+#ifndef NEED_MKTIME_INTERNAL
+# define NEED_MKTIME_INTERNAL 0
+#endif
+#ifndef NEED_MKTIME_WINDOWS
+# define NEED_MKTIME_WINDOWS 0
+#endif
+#ifndef NEED_MKTIME_WORKING
+# define NEED_MKTIME_WORKING DEBUG_MKTIME
+#endif
+
+#include "mktime-internal.h"
+
+#ifndef _LIBC
+static void
+my_tzset (void)
+{
+# if NEED_MKTIME_WINDOWS
+  /* Rectify the value of the environment variable TZ.
+     There are four possible kinds of such values:
+       - Traditional US time zone names, e.g. "PST8PDT".  Syntax: see
+         <https://msdn.microsoft.com/en-us/library/90s5c885.aspx>
+       - Time zone names based on geography, that contain one or more
+         slashes, e.g. "Europe/Moscow".
+       - Time zone names based on geography, without slashes, e.g.
+         "Singapore".
+       - Time zone names that contain explicit DST rules.  Syntax: see
+         <http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03>
+     The Microsoft CRT understands only the first kind.  It produces incorrect
+     results if the value of TZ is of the other kinds.
+     But in a Cygwin environment, /etc/profile.d/tzset.sh sets TZ to a value
+     of the second kind for most geographies, or of the first kind in a few
+     other geographies.  If it is of the second kind, neutralize it.  For the
+     Microsoft CRT, an absent or empty TZ means the time zone that the user
+     has set in the Windows Control Panel.
+     If the value of TZ is of the third or fourth kind -- Cygwin programs
+     understand these syntaxes as well --, it does not matter whether we
+     neutralize it or not, since these values occur only when a Cygwin user
+     has set TZ explicitly; this case is 1. rare and 2. under the user's
+     responsibility.  */
+  const char *tz = getenv ("TZ");
+  if (tz != NULL && strchr (tz, '/') != NULL)
+    _putenv ("TZ=");
+# elif HAVE_TZSET
+  tzset ();
 # endif
+}
+# undef __tzset
+# define __tzset() my_tzset ()
 #endif
 
-/* Verify a requirement at compile-time (unlike assert, which is runtime).  */
-#define verify(name, assertion) struct name { char a[(assertion) ? 1 : -1]; }
+#if defined _LIBC || NEED_MKTIME_WORKING || NEED_MKTIME_INTERNAL
+
+/* 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
+   in a year.  The factor of three comes because these products need
+   to be subtracted from each other, and sometimes with an offset
+   added to them, without worrying about overflow.
+
+   Much of the code uses long_int to represent time_t values, to
+   lessen the hassle of dealing with platforms where time_t is
+   unsigned, and because long_int should suffice to represent all
+   time_t values that mktime can generate even on platforms where
+   time_t is excessively wide.  */
 
-/* A signed type that is at least one bit wider than int.  */
-#if INT_MAX <= LONG_MAX / 2
+#if INT_MAX <= LONG_MAX / 3 / 366 / 24 / 60 / 60
 typedef long int long_int;
 #else
 typedef long long int long_int;
 #endif
-verify (long_int_is_wide_enough, INT_MAX == INT_MAX * (long_int) 2 / 2);
+verify (INT_MAX <= TYPE_MAXIMUM (long_int) / 3 / 366 / 24 / 60 / 60);
 
 /* Shift A right by B bits portably, by dividing A by 2**B and
-   truncating towards minus infinity.  A and B should be free of side
-   effects, and B should be in the range 0 <= B <= INT_BITS - 2, where
-   INT_BITS is the number of useful bits in an int.  GNU code can
-   assume that INT_BITS is at least 32.
+   truncating towards minus infinity.  B should be in the range 0 <= B
+   <= LONG_INT_BITS - 2, where LONG_INT_BITS is the number of useful
+   bits in a long_int.  LONG_INT_BITS is at least 32.
 
    ISO C99 says that A >> B is implementation-defined if A < 0.  Some
    implementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shift
    right in the usual way when A < 0, so SHR falls back on division if
    ordinary A >> B doesn't seem to be the usual signed shift.  */
-#define SHR(a, b)                                               \
-  ((-1 >> 1 == -1                                               \
-    && (long_int) -1 >> 1 == -1                                 \
-    && ((time_t) -1 >> 1 == -1 || ! TYPE_SIGNED (time_t)))      \
-   ? (a) >> (b)                                                 \
-   : (a) / (1 << (b)) - ((a) % (1 << (b)) < 0))
-
-/* The extra casts in the following macros work around compiler bugs,
-   e.g., in Cray C 5.0.3.0.  */
-
-/* True if the arithmetic type T is an integer type.  bool counts as
-   an integer.  */
-#define TYPE_IS_INTEGER(t) ((t) 1.5 == 1)
-
-/* True if negative values of the signed integer type T use two's
-   complement, or if T is an unsigned integer type.  */
-#define TYPE_TWOS_COMPLEMENT(t) ((t) ~ (t) 0 == (t) -1)
-
-/* True if the arithmetic type T is signed.  */
-#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
-
-/* The maximum and minimum values for the integer type T.  These
-   macros have undefined behavior if T is signed and has padding bits.
-   If this is a problem for you, please let us know how to fix it for
-   your host.  */
-#define TYPE_MINIMUM(t) \
-  ((t) (! TYPE_SIGNED (t) \
-	? (t) 0 \
-	: ~ TYPE_MAXIMUM (t)))
-#define TYPE_MAXIMUM(t) \
-  ((t) (! TYPE_SIGNED (t) \
-	? (t) -1 \
-	: ((((t) 1 << (sizeof (t) * CHAR_BIT - 2)) - 1) * 2 + 1)))
-
-#ifndef TIME_T_MIN
-# define TIME_T_MIN TYPE_MINIMUM (time_t)
-#endif
-#ifndef TIME_T_MAX
-# define TIME_T_MAX TYPE_MAXIMUM (time_t)
-#endif
-#define TIME_T_MIDPOINT (SHR (TIME_T_MIN + TIME_T_MAX, 1) + 1)
 
-verify (time_t_is_integer, TYPE_IS_INTEGER (time_t));
-verify (twos_complement_arithmetic,
-	(TYPE_TWOS_COMPLEMENT (int)
-	 && TYPE_TWOS_COMPLEMENT (long_int)
-	 && TYPE_TWOS_COMPLEMENT (time_t)));
+static long_int
+shr (long_int a, int b)
+{
+  long_int one = 1;
+  return (-one >> 1 == -1
+	  ? a >> b
+	  : a / (one << b) - (a % (one << b) < 0));
+}
+
+/* Bounds for the intersection of time_t and long_int.  */
+
+static long_int const mktime_min
+  = ((TYPE_SIGNED (time_t) && TYPE_MINIMUM (time_t) < TYPE_MINIMUM (long_int))
+     ? TYPE_MINIMUM (long_int) : TYPE_MINIMUM (time_t));
+static long_int const mktime_max
+  = (TYPE_MAXIMUM (long_int) < TYPE_MAXIMUM (time_t)
+     ? TYPE_MAXIMUM (long_int) : TYPE_MAXIMUM (time_t));
+
+verify (TYPE_IS_INTEGER (time_t));
 
 #define EPOCH_YEAR 1970
 #define TM_YEAR_BASE 1900
-verify (base_year_is_a_multiple_of_100, TM_YEAR_BASE % 100 == 0);
+verify (TM_YEAR_BASE % 100 == 0);
 
-/* Return 1 if YEAR + TM_YEAR_BASE is a leap year.  */
-static int
+/* Is YEAR + TM_YEAR_BASE a leap year?  */
+static bool
 leapyear (long_int year)
 {
   /* Don't add YEAR to TM_YEAR_BASE, as that might overflow.
@@ -166,20 +197,9 @@  const unsigned short int __mon_yday[2][13] =
   };
 
 
-#ifndef _LIBC
-/* Portable standalone applications should supply a <time.h> that
-   declares a POSIX-compliant localtime_r, for the benefit of older
-   implementations that lack localtime_r or have a nonstandard one.
-   See the gnulib time_r module for one way to implement this.  */
-# undef __localtime_r
-# define __localtime_r localtime_r
-# define __mktime_internal mktime_internal
-# include "mktime-internal.h"
-#endif
-
-/* Return 1 if the values A and B differ according to the rules for
-   tm_isdst: A and B differ if one is zero and the other positive.  */
-static int
+/* Do the values A and B differ according to the rules for tm_isdst?
+   A and B differ if one is zero and the other positive.  */
+static bool
 isdst_differ (int a, int b)
 {
   return (!a != !b) && (0 <= a) && (0 <= b);
@@ -187,107 +207,68 @@  isdst_differ (int a, int b)
 
 /* Return an integer value measuring (YEAR1-YDAY1 HOUR1:MIN1:SEC1) -
    (YEAR0-YDAY0 HOUR0:MIN0:SEC0) in seconds, assuming that the clocks
-   were not adjusted between the time stamps.
+   were not adjusted between the timestamps.
 
    The YEAR values uses the same numbering as TP->tm_year.  Values
-   need not be in the usual range.  However, YEAR1 must not be less
-   than 2 * INT_MIN or greater than 2 * INT_MAX.
-
-   The result may overflow.  It is the caller's responsibility to
-   detect overflow.  */
+   need not be in the usual range.  However, YEAR1 must not overflow
+   when multiplied by three times the number of seconds in a year, and
+   likewise for YDAY1 and three times the number of seconds in a day.  */
 
-static time_t
+static long_int
 ydhms_diff (long_int year1, long_int yday1, int hour1, int min1, int sec1,
 	    int year0, int yday0, int hour0, int min0, int sec0)
 {
-  verify (C99_integer_division, -1 / 2 == 0);
+  verify (-1 / 2 == 0);
 
   /* Compute intervening leap days correctly even if year is negative.
      Take care to avoid integer overflow here.  */
-  int a4 = SHR (year1, 2) + SHR (TM_YEAR_BASE, 2) - ! (year1 & 3);
-  int b4 = SHR (year0, 2) + SHR (TM_YEAR_BASE, 2) - ! (year0 & 3);
+  int a4 = shr (year1, 2) + shr (TM_YEAR_BASE, 2) - ! (year1 & 3);
+  int b4 = shr (year0, 2) + shr (TM_YEAR_BASE, 2) - ! (year0 & 3);
   int a100 = a4 / 25 - (a4 % 25 < 0);
   int b100 = b4 / 25 - (b4 % 25 < 0);
-  int a400 = SHR (a100, 2);
-  int b400 = SHR (b100, 2);
+  int a400 = shr (a100, 2);
+  int b400 = shr (b100, 2);
   int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
 
-  /* Compute the desired time in time_t precision.  Overflow might
-     occur here.  */
-  time_t tyear1 = year1;
-  time_t years = tyear1 - year0;
-  time_t days = 365 * years + yday1 - yday0 + intervening_leap_days;
-  time_t hours = 24 * days + hour1 - hour0;
-  time_t minutes = 60 * hours + min1 - min0;
-  time_t seconds = 60 * minutes + sec1 - sec0;
+  /* Compute the desired time without overflowing.  */
+  long_int years = year1 - year0;
+  long_int days = 365 * years + yday1 - yday0 + intervening_leap_days;
+  long_int hours = 24 * days + hour1 - hour0;
+  long_int minutes = 60 * hours + min1 - min0;
+  long_int seconds = 60 * minutes + sec1 - sec0;
   return seconds;
 }
 
-/* Return the average of A and B, even if A + B would overflow.  */
-static time_t
-time_t_avg (time_t a, time_t b)
+/* Return the average of A and B, even if A + B would overflow.
+   Round toward positive infinity.  */
+static long_int
+long_int_avg (long_int a, long_int b)
 {
-  return SHR (a, 1) + SHR (b, 1) + (a & b & 1);
-}
-
-/* Return 1 if A + B does not overflow.  If time_t is unsigned and if
-   B's top bit is set, assume that the sum represents A - -B, and
-   return 1 if the subtraction does not wrap around.  */
-static int
-time_t_add_ok (time_t a, time_t b)
-{
-  if (! TYPE_SIGNED (time_t))
-    {
-      time_t sum = a + b;
-      return (sum < a) == (TIME_T_MIDPOINT <= b);
-    }
-  else if (WRAPV)
-    {
-      time_t sum = a + b;
-      return (sum < a) == (b < 0);
-    }
-  else
-    {
-      time_t avg = time_t_avg (a, b);
-      return TIME_T_MIN / 2 <= avg && avg <= TIME_T_MAX / 2;
-    }
-}
-
-/* Return 1 if A + B does not overflow.  */
-static int
-time_t_int_add_ok (time_t a, int b)
-{
-  verify (int_no_wider_than_time_t, INT_MAX <= TIME_T_MAX);
-  if (WRAPV)
-    {
-      time_t sum = a + b;
-      return (sum < a) == (b < 0);
-    }
-  else
-    {
-      int a_odd = a & 1;
-      time_t avg = SHR (a, 1) + (SHR (b, 1) + (a_odd & b));
-      return TIME_T_MIN / 2 <= avg && avg <= TIME_T_MAX / 2;
-    }
+  return shr (a, 1) + shr (b, 1) + ((a | b) & 1);
 }
 
 /* Return a time_t value corresponding to (YEAR-YDAY HOUR:MIN:SEC),
-   assuming that *T corresponds to *TP and that no clock adjustments
+   assuming that T corresponds to *TP and that no clock adjustments
    occurred between *TP and the desired time.
-   If TP is null, return a value not equal to *T; this avoids false matches.
-   If overflow occurs, yield the minimal or maximal value, except do not
-   yield a value equal to *T.  */
-static time_t
+   Although T and the returned value are of type long_int,
+   they represent time_t values and must be in time_t range.
+   If TP is null, return a value not equal to T; this avoids false matches.
+   YEAR and YDAY must not be so large that multiplying them by three times the
+   number of seconds in a year (or day, respectively) would overflow long_int.
+   If the returned value would be out of range, yield the minimal or
+   maximal in-range value, except do not yield a value equal to T.  */
+static long_int
 guess_time_tm (long_int year, long_int yday, int hour, int min, int sec,
-	       const time_t *t, const struct tm *tp)
+	       long_int t, const struct tm *tp)
 {
   if (tp)
     {
-      time_t d = ydhms_diff (year, yday, hour, min, sec,
-			     tp->tm_year, tp->tm_yday,
-			     tp->tm_hour, tp->tm_min, tp->tm_sec);
-      if (time_t_add_ok (*t, d))
-	return *t + d;
+      long_int result;
+      long_int d = ydhms_diff (year, yday, hour, min, sec,
+			       tp->tm_year, tp->tm_yday,
+			       tp->tm_hour, tp->tm_min, tp->tm_sec);
+      if (! INT_ADD_WRAPV (t, d, &result))
+	return result;
     }
 
   /* Overflow occurred one way or another.  Return the nearest result
@@ -295,32 +276,51 @@  guess_time_tm (long_int year, long_int yday, int hour, int min, int sec,
      if the actual difference is nonzero, as that would cause a false
      match; and don't oscillate between two values, as that would
      confuse the spring-forward gap detector.  */
-  return (*t < TIME_T_MIDPOINT
-	  ? (*t <= TIME_T_MIN + 1 ? *t + 1 : TIME_T_MIN)
-	  : (TIME_T_MAX - 1 <= *t ? *t - 1 : TIME_T_MAX));
+  return (t < long_int_avg (mktime_min, mktime_max)
+	  ? (t <= mktime_min + 1 ? t + 1 : mktime_min)
+	  : (mktime_max - 1 <= t ? t - 1 : mktime_max));
+}
+
+/* Use CONVERT to convert T to a struct tm value in *TM.  T must be in
+   range for time_t.  Return TM if successful, NULL if T is out of
+   range for CONVERT.  */
+static struct tm *
+convert_time (struct tm *(*convert) (const time_t *, struct tm *),
+	      long_int t, struct tm *tm)
+{
+  time_t x = t;
+  return convert (&x, tm);
 }
 
 /* Use CONVERT to convert *T to a broken down time in *TP.
    If *T is out of range for conversion, adjust it so that
-   it is the nearest in-range value and then convert that.  */
+   it is the nearest in-range value and then convert that.
+   A value is in range if it fits in both time_t and long_int.  */
 static struct tm *
 ranged_convert (struct tm *(*convert) (const time_t *, struct tm *),
-		time_t *t, struct tm *tp)
+		long_int *t, struct tm *tp)
 {
-  struct tm *r = convert (t, tp);
+  struct tm *r;
+  if (*t < mktime_min)
+    *t = mktime_min;
+  else if (mktime_max < *t)
+    *t = mktime_max;
+  r = convert_time (convert, *t, tp);
 
   if (!r && *t)
     {
-      time_t bad = *t;
-      time_t ok = 0;
+      long_int bad = *t;
+      long_int ok = 0;
 
-      /* BAD is a known unconvertible time_t, and OK is a known good one.
+      /* BAD is a known unconvertible value, and OK is a known good one.
 	 Use binary search to narrow the range between BAD and OK until
 	 they differ by 1.  */
-      while (bad != ok + (bad < 0 ? -1 : 1))
+      while (true)
 	{
-	  time_t mid = *t = time_t_avg (ok, bad);
-	  r = convert (t, tp);
+	  long_int mid = long_int_avg (ok, bad);
+	  if (mid != ok && mid != bad)
+	    break;
+	  r = convert_time (convert, mid, tp);
 	  if (r)
 	    ok = mid;
 	  else
@@ -331,8 +331,7 @@  ranged_convert (struct tm *(*convert) (const time_t *, struct tm *),
 	{
 	  /* The last conversion attempt failed;
 	     revert to the most recent successful attempt.  */
-	  *t = ok;
-	  r = convert (t, tp);
+	  r = convert_time (convert, ok, tp);
 	}
     }
 
@@ -349,9 +348,9 @@  ranged_convert (struct tm *(*convert) (const time_t *, struct tm *),
 time_t
 __mktime_internal (struct tm *tp,
 		   struct tm *(*convert) (const time_t *, struct tm *),
-		   time_t *offset)
+		   mktime_offset_t *offset)
 {
-  time_t t, gt, t0, t1, t2;
+  long_int t, gt, t0, t1, t2, dt;
   struct tm tm;
 
   /* The maximum number of probes (calls to CONVERT) should be enough
@@ -381,9 +380,7 @@  __mktime_internal (struct tm *tp,
   long_int year = lyear_requested + mon_years;
 
   /* The other values need not be in range:
-     the remaining code handles minor overflows correctly,
-     assuming int and time_t arithmetic wraps around.
-     Major overflows are caught at the end.  */
+     the remaining code handles overflows correctly.  */
 
   /* Calculate day of year from year, month, and day of month.
      The result need not be in range.  */
@@ -393,7 +390,8 @@  __mktime_internal (struct tm *tp,
   long_int lmday = mday;
   long_int yday = mon_yday + lmday;
 
-  time_t guessed_offset = *offset;
+  mktime_offset_t off = *offset;
+  int negative_offset_guess;
 
   int sec_requested = sec;
 
@@ -410,71 +408,14 @@  __mktime_internal (struct tm *tp,
   /* Invert CONVERT by probing.  First assume the same offset as last
      time.  */
 
+  INT_SUBTRACT_WRAPV (0, off, &negative_offset_guess);
   t0 = ydhms_diff (year, yday, hour, min, sec,
-		   EPOCH_YEAR - TM_YEAR_BASE, 0, 0, 0, - guessed_offset);
-
-  if (TIME_T_MAX / INT_MAX / 366 / 24 / 60 / 60 < 3)
-    {
-      /* time_t isn't large enough to rule out overflows, so check
-	 for major overflows.  A gross check suffices, since if t0
-	 has overflowed, it is off by a multiple of TIME_T_MAX -
-	 TIME_T_MIN + 1.  So ignore any component of the difference
-	 that is bounded by a small value.  */
-
-      /* Approximate log base 2 of the number of time units per
-	 biennium.  A biennium is 2 years; use this unit instead of
-	 years to avoid integer overflow.  For example, 2 average
-	 Gregorian years are 2 * 365.2425 * 24 * 60 * 60 seconds,
-	 which is 63113904 seconds, and rint (log2 (63113904)) is
-	 26.  */
-      int ALOG2_SECONDS_PER_BIENNIUM = 26;
-      int ALOG2_MINUTES_PER_BIENNIUM = 20;
-      int ALOG2_HOURS_PER_BIENNIUM = 14;
-      int ALOG2_DAYS_PER_BIENNIUM = 10;
-      int LOG2_YEARS_PER_BIENNIUM = 1;
-
-      int approx_requested_biennia =
-	(SHR (year_requested, LOG2_YEARS_PER_BIENNIUM)
-	 - SHR (EPOCH_YEAR - TM_YEAR_BASE, LOG2_YEARS_PER_BIENNIUM)
-	 + SHR (mday, ALOG2_DAYS_PER_BIENNIUM)
-	 + SHR (hour, ALOG2_HOURS_PER_BIENNIUM)
-	 + SHR (min, ALOG2_MINUTES_PER_BIENNIUM)
-	 + (LEAP_SECONDS_POSSIBLE
-	    ? 0
-	    : SHR (sec, ALOG2_SECONDS_PER_BIENNIUM)));
-
-      int approx_biennia = SHR (t0, ALOG2_SECONDS_PER_BIENNIUM);
-      int diff = approx_biennia - approx_requested_biennia;
-      int approx_abs_diff = diff < 0 ? -1 - diff : diff;
-
-      /* IRIX 4.0.5 cc miscalculates TIME_T_MIN / 3: it erroneously
-	 gives a positive value of 715827882.  Setting a variable
-	 first then doing math on it seems to work.
-	 (ghazi@caip.rutgers.edu) */
-      time_t time_t_max = TIME_T_MAX;
-      time_t time_t_min = TIME_T_MIN;
-      time_t overflow_threshold =
-	(time_t_max / 3 - time_t_min / 3) >> ALOG2_SECONDS_PER_BIENNIUM;
-
-      if (overflow_threshold < approx_abs_diff)
-	{
-	  /* Overflow occurred.  Try repairing it; this might work if
-	     the time zone offset is enough to undo the overflow.  */
-	  time_t repaired_t0 = -1 - t0;
-	  approx_biennia = SHR (repaired_t0, ALOG2_SECONDS_PER_BIENNIUM);
-	  diff = approx_biennia - approx_requested_biennia;
-	  approx_abs_diff = diff < 0 ? -1 - diff : diff;
-	  if (overflow_threshold < approx_abs_diff)
-	    return -1;
-	  guessed_offset += repaired_t0 - t0;
-	  t0 = repaired_t0;
-	}
-    }
+		   EPOCH_YEAR - TM_YEAR_BASE, 0, 0, 0, negative_offset_guess);
 
   /* Repeatedly use the error to improve the guess.  */
 
   for (t = t1 = t2 = t0, dst2 = 0;
-       (gt = guess_time_tm (year, yday, hour, min, sec, &t,
+       (gt = guess_time_tm (year, yday, hour, min, sec, t,
 			    ranged_convert (convert, &t, &tm)),
 	t != gt);
        t1 = t2, t2 = t, t = gt, dst2 = tm.tm_isdst != 0)
@@ -531,65 +472,70 @@  __mktime_internal (struct tm *tp,
 
       for (delta = stride; delta < delta_bound; delta += stride)
 	for (direction = -1; direction <= 1; direction += 2)
-	  if (time_t_int_add_ok (t, delta * direction))
-	    {
-	      time_t ot = t + delta * direction;
-	      struct tm otm;
-	      ranged_convert (convert, &ot, &otm);
-	      if (! isdst_differ (isdst, otm.tm_isdst))
-		{
-		  /* We found the desired tm_isdst.
-		     Extrapolate back to the desired time.  */
-		  t = guess_time_tm (year, yday, hour, min, sec, &ot, &otm);
-		  ranged_convert (convert, &t, &tm);
-		  goto offset_found;
-		}
-	    }
+	  {
+	    long_int ot;
+	    if (! INT_ADD_WRAPV (t, delta * direction, &ot))
+	      {
+		struct tm otm;
+		ranged_convert (convert, &ot, &otm);
+		if (! isdst_differ (isdst, otm.tm_isdst))
+		  {
+		    /* We found the desired tm_isdst.
+		       Extrapolate back to the desired time.  */
+		    t = guess_time_tm (year, yday, hour, min, sec, ot, &otm);
+		    ranged_convert (convert, &t, &tm);
+		    goto offset_found;
+		  }
+	      }
+	  }
     }
 
  offset_found:
-  *offset = guessed_offset + t - t0;
+  /* Set *OFFSET to the low-order bits of T - T0 - NEGATIVE_OFFSET_GUESS.
+     This is just a heuristic to speed up the next mktime call, and
+     correctness is unaffected if integer overflow occurs here.  */
+  INT_SUBTRACT_WRAPV (t, t0, &dt);
+  INT_SUBTRACT_WRAPV (dt, negative_offset_guess, offset);
 
   if (LEAP_SECONDS_POSSIBLE && sec_requested != tm.tm_sec)
     {
       /* Adjust time to reflect the tm_sec requested, not the normalized value.
 	 Also, repair any damage from a false match due to a leap second.  */
-      int sec_adjustment = (sec == 0 && tm.tm_sec == 60) - sec;
-      if (! time_t_int_add_ok (t, sec_requested))
-	return -1;
-      t1 = t + sec_requested;
-      if (! time_t_int_add_ok (t1, sec_adjustment))
-	return -1;
-      t2 = t1 + sec_adjustment;
-      if (! convert (&t2, &tm))
+      long_int sec_adjustment = sec == 0 && tm.tm_sec == 60;
+      sec_adjustment -= sec;
+      sec_adjustment += sec_requested;
+      if (INT_ADD_WRAPV (t, sec_adjustment, &t)
+	  || ! (mktime_min <= t && t <= mktime_max)
+	  || ! convert_time (convert, t, &tm))
 	return -1;
-      t = t2;
     }
 
   *tp = tm;
   return t;
 }
 
+#endif /* _LIBC || NEED_MKTIME_WORKING || NEED_MKTIME_INTERNAL */
 
-/* FIXME: This should use a signed type wide enough to hold any UTC
-   offset in seconds.  'int' should be good enough for GNU code.  We
-   can't fix this unilaterally though, as other modules invoke
-   __mktime_internal.  */
-static time_t localtime_offset;
+#if defined _LIBC || NEED_MKTIME_WORKING || NEED_MKTIME_WINDOWS
 
 /* Convert *TP to a time_t value.  */
 time_t
 mktime (struct tm *tp)
 {
-#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 ();
-#endif
 
+# if defined __LIBC || NEED_MKTIME_WORKING
+  static mktime_offset_t localtime_offset;
   return __mktime_internal (tp, __localtime_r, &localtime_offset);
+# else
+#  undef mktime
+  return mktime (tp);
+# endif
 }
+#endif /* _LIBC || NEED_MKTIME_WORKING || NEED_MKTIME_WINDOWS */
 
 #ifdef weak_alias
 weak_alias (mktime, timelocal)
@@ -600,7 +546,7 @@  libc_hidden_def (mktime)
 libc_hidden_weak (timelocal)
 #endif
 
-#if defined DEBUG_MKTIME && DEBUG_MKTIME
+#if DEBUG_MKTIME
 
 static int
 not_equal_tm (const struct tm *a, const struct tm *b)
@@ -652,6 +598,14 @@  main (int argc, char **argv)
   time_t tk, tl, tl1;
   char trailer;
 
+  /* Sanity check, plus call tzset.  */
+  tl = 0;
+  if (! localtime (&tl))
+    {
+      printf ("localtime (0) fails\n");
+      status = 1;
+    }
+
   if ((argc == 3 || argc == 4)
       && (sscanf (argv[1], "%d-%d-%d%c",
 		  &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &trailer)
@@ -665,12 +619,7 @@  main (int argc, char **argv)
       tm.tm_isdst = argc == 3 ? -1 : atoi (argv[3]);
       tmk = tm;
       tl = mktime (&tmk);
-      lt = localtime (&tl);
-      if (lt)
-	{
-	  tml = *lt;
-	  lt = &tml;
-	}
+      lt = localtime_r (&tl, &tml);
       printf ("mktime returns %ld == ", (long int) tl);
       print_tm (&tmk);
       printf ("\n");
@@ -685,16 +634,16 @@  main (int argc, char **argv)
       if (argc == 4)
 	for (tl = from; by < 0 ? to <= tl : tl <= to; tl = tl1)
 	  {
-	    lt = localtime (&tl);
+	    lt = localtime_r (&tl, &tml);
 	    if (lt)
 	      {
-		tmk = tml = *lt;
+		tmk = tml;
 		tk = mktime (&tmk);
 		status |= check_result (tk, tmk, tl, &tml);
 	      }
 	    else
 	      {
-		printf ("localtime (%ld) yields 0\n", (long int) tl);
+		printf ("localtime_r (%ld) yields 0\n", (long int) tl);
 		status = 1;
 	      }
 	    tl1 = tl + by;
@@ -705,16 +654,16 @@  main (int argc, char **argv)
 	for (tl = from; by < 0 ? to <= tl : tl <= to; tl = tl1)
 	  {
 	    /* Null benchmark.  */
-	    lt = localtime (&tl);
+	    lt = localtime_r (&tl, &tml);
 	    if (lt)
 	      {
-		tmk = tml = *lt;
+		tmk = tml;
 		tk = tl;
 		status |= check_result (tk, tmk, tl, &tml);
 	      }
 	    else
 	      {
-		printf ("localtime (%ld) yields 0\n", (long int) tl);
+		printf ("localtime_r (%ld) yields 0\n", (long int) tl);
 		status = 1;
 	      }
 	    tl1 = tl + by;
diff --git a/time/timegm.c b/time/timegm.c
index fb720e2d7d..229fff23c6 100644
--- a/time/timegm.c
+++ b/time/timegm.c
@@ -17,31 +17,18 @@ 
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
-#ifdef HAVE_CONFIG_H
+#ifndef _LIBC
 # include <config.h>
 #endif
 
-#ifdef _LIBC
-# include <time.h>
-#else
-# include "timegm.h"
-
-/* Portable standalone applications should supply a "time_r.h" that
-   declares a POSIX-compliant gmtime_r, for the benefit of older
-   implementations that lack gmtime_r or have a nonstandard one.
-   See the gnulib time_r module for one way to implement this.  */
-# include <time_r.h>
-# undef __gmtime_r
-# define __gmtime_r gmtime_r
-time_t __mktime_internal (struct tm *,
-			  struct tm * (*) (time_t const *, struct tm *),
-			  time_t *);
-#endif
+#include <time.h>
+
+#include "mktime-internal.h"
 
 time_t
 timegm (struct tm *tmp)
 {
-  static time_t gmtime_offset;
+  static mktime_offset_t gmtime_offset;
   tmp->tm_isdst = 0;
   return __mktime_internal (tmp, __gmtime_r, &gmtime_offset);
 }