[tz] [PROPOSED PATCH 4/9] mktime: guess better near transitions where tm_isdst does not change
Paul Eggert
eggert at cs.ucla.edu
Mon Aug 25 06:59:48 UTC 2014
* localtime.c (SMALLEST): New macro.
(time2sub) [TM_GMTOFF && !UNINIT_TRAP]:
If the UTC offset doesn't match the request, try the requested offset.
This catches a problem caught by -DTYPECHECK with a time stamp
near a transition from LMT to standard time, where both sides of
the transition have tm_isdst == 0. If !defined TM_GMTOFF ||
UNINIT_TRAP you're out of luck: mktime will still conform
to its spec but it'll be more likely to guess wrong on these
ambiguous inputs.
* private.h (UNINIT_TRAP): New macro that defaults to 0.
* Makefile, NEWS: Document this.
---
Makefile | 2 ++
NEWS | 8 ++++++++
localtime.c | 30 ++++++++++++++++++++++++++++++
private.h | 4 ++++
4 files changed, 44 insertions(+)
diff --git a/Makefile b/Makefile
index 26e8ab7..968773f 100644
--- a/Makefile
+++ b/Makefile
@@ -134,6 +134,8 @@ LDLIBS=
# the default is system-supplied, typically "/usr/lib/locale"
# -DTZDEFRULESTRING=\",date/time,date/time\" to default to the specified
# DST transitions if the time zone files cannot be accessed
+# -DUNINIT_TRAP=1 if reading uninitialized storage can cause problems
+# other than simply getting garbage data
# -DZIC_MAX_ABBR_LEN_WO_WARN=3
# (or some other number) to set the maximum time zone abbreviation length
# that zic will accept without a warning (the default is 6)
diff --git a/NEWS b/NEWS
index 9912856..3d69b1f 100644
--- a/NEWS
+++ b/NEWS
@@ -59,6 +59,14 @@ Unreleased, experimental changes
already defined, to make it easier to configure on common platforms.
Define NO_TM_GMTOFF and NO_TM_ZONE to suppress this.
+ Unless the new macro UNINIT_TRAP is defined to 0, the tz code now
+ assumes that reading uninitialized memory yields garbage values
+ but does not cause other problems such as traps.
+
+ If TM_GMTOFF is defined and UNINIT_TRAP is not 0, mktime is now
+ more likely to guess right for ambiguous time stamps near
+ transitions where tm_isdst does not change.
+
tzselect -c now uses a hybrid distance measure that works better
in Africa. (Thanks to Alan Barrett for noting the problem.)
diff --git a/localtime.c b/localtime.c
index 7b22f72..e60842e 100644
--- a/localtime.c
+++ b/localtime.c
@@ -103,6 +103,7 @@ struct lsinfo { /* leap second information */
int_fast64_t ls_corr; /* correction to apply */
};
+#define SMALLEST(a, b) (((a) < (b)) ? (a) : (b))
#define BIGGEST(a, b) (((a) > (b)) ? (a) : (b))
#ifdef TZNAME_MAX
@@ -1817,6 +1818,35 @@ time2sub(struct tm *const tmp,
else lo = t;
continue;
}
+#if defined TM_GMTOFF && ! UNINIT_TRAP
+ if (mytm.TM_GMTOFF != yourtm.TM_GMTOFF
+ && (yourtm.TM_GMTOFF < 0
+ ? (-SECSPERDAY <= yourtm.TM_GMTOFF
+ && (mytm.TM_GMTOFF <=
+ (SMALLEST (INT_FAST32_MAX, LONG_MAX)
+ + yourtm.TM_GMTOFF)))
+ : (yourtm.TM_GMTOFF <= SECSPERDAY
+ && ((BIGGEST (INT_FAST32_MIN, LONG_MIN)
+ + yourtm.TM_GMTOFF)
+ <= mytm.TM_GMTOFF)))) {
+ /* MYTM matches YOURTM except with the wrong UTC offset.
+ YOURTM.TM_GMTOFF is plausible, so try it instead.
+ It's OK if YOURTM.TM_GMTOFF contains uninitialized data,
+ since the guess gets checked. */
+ time_t altt = t;
+ int_fast32_t diff = mytm.TM_GMTOFF - yourtm.TM_GMTOFF;
+ if (!increment_overflow_time(&altt, diff)) {
+ struct tm alttm;
+ if (funcp(&altt, offset, &alttm)
+ && alttm.tm_isdst == mytm.tm_isdst
+ && alttm.TM_GMTOFF == yourtm.TM_GMTOFF
+ && tmcomp(&alttm, &yourtm) == 0) {
+ t = altt;
+ mytm = alttm;
+ }
+ }
+ }
+#endif
if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst)
break;
/*
diff --git a/private.h b/private.h
index 14a9d53..31239e1 100644
--- a/private.h
+++ b/private.h
@@ -413,6 +413,10 @@ static time_t const time_t_max =
# define INITIALIZE(x)
#endif
+#ifndef UNINIT_TRAP
+# define UNINIT_TRAP 0
+#endif
+
/*
** For the benefit of GNU folk...
** '_(MSGID)' uses the current locale's message library string for MSGID.
--
1.9.1
More information about the tz
mailing list