[tz] Timezone for Moscow is -10800 after installing tzdata-2014g

Paul Eggert eggert at cs.ucla.edu
Sat Oct 4 06:05:19 UTC 2014

Alexander Koblov wrote:
> Why tzdata-2014g returns timezone for the Europe/Moscow -10800
> (3 hours)? As far as I understand timezone for Moscow should be changed on the
> night of 25/26 October.

Thanks for reporting the problem.  It is a bit confusing here.

When tzset is called, it does not know what time stamps you are interested in. 
It guesses, and its guess is based on time stamps that are farthest in the 
future.  Here, tzcode's tzset behaves like the GNU C library and (I expect) like 
many other libraries.  Since tzset is not required to prefer any particular time 
stamps, its behavior appears to be "correct", in the sense that it conforms to 
the standard and is reasonable for some applications (though evidently not for 

If you have a need for the UTC offset I suggest using the tm_gmtoff member of 
'struct tm' if available, as tm_gmtoff is more reliable than 'timezone'.

While looking into this issue I noticed a related problem: if your platform 
lacks tm_gmtoff and you are using single-threaded code, the 'timezone' variable 
should work better than it does.  'timezone' and 'altzone' should be compatible 
with what's in 'tzname', but in 2014h sometimes they are not.  The attached 
proposed patch should fix that.  This patch won't fix your test case, but it 
should fix some similar cases based on code that invokes 'localtime' or 'mktime' 
and which then inspects 'timezone' or 'altzone'.  (This is not thread-safe, of 
course; multithreaded programs should use tm_gmtoff.)
-------------- next part --------------
From 4cf114ec1acc1de955540c232d4d7340f259be60 Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert at cs.ucla.edu>
Date: Fri, 3 Oct 2014 22:22:50 -0700
Subject: [PROPOSED PATCH] Make 'timezone' and 'altzone' more compatible with

* localtime.c (localsub): Don't bother updating 'tzname' again
when localtime is GMT, as tzset's value should be fine.  When
updating 'tzname', also update 'timezone' and 'altzone'
consistently, on platforms that define the latter two vars.
* NEWS: Document this.
 NEWS        | 12 ++++++++++++
 localtime.c | 33 ++++++++++++++++++++++++---------
 2 files changed, 36 insertions(+), 9 deletions(-)

diff --git a/NEWS b/NEWS
index 5e33102..eccb0ed 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,17 @@
 News for the tz database
+Unreleased, experimental changes
+  Changes affecting code
+    If USG_COMPAT is defined and the requested time stamp is standard time,
+    the tz library's localtime and mktime functions now set the extern
+    variable timezone to a value appropriate for that time stamp; and
+    similarly for ALTZONE, daylight saving time, and the altzone variable.
+    This change is a companion to the tzname change in 2014h, and is
+    designed to make timezone and altzone more compatible with tzname.
 Release 2014h - 2014-09-25 18:59:03 -0700
   Changes affecting past time stamps
diff --git a/localtime.c b/localtime.c
index e692513..9ffdcda 100644
--- a/localtime.c
+++ b/localtime.c
@@ -1292,7 +1292,10 @@ tzfree(timezone_t sp)
 ** freely called. (And no, the PANS doesn't require the above behavior,
 ** but it *is* desirable.)
-** If OFFSET is nonzero, set tzname if successful.
+** If successful and OFFSET is nonzero,
+** set the applicable parts of tzname, timezone and altzone;
+** however, it's OK to omit this step if the time zone is POSIX-compatible,
+** since in that case tzset should have already done this step correctly.
 ** OFFSET's type is intfast32_t for compatibility with gmtsub.
@@ -1307,10 +1310,8 @@ localsub(struct state const *sp, time_t const *timep, int_fast32_t offset,
 	const time_t			t = *timep;
 	if (sp == NULL) {
-	  result = gmtsub(gmtptr, timep, 0, tmp);
-	  if (result && offset)
-	    tzname[0] = gmtptr ? gmtptr->chars : (char *) gmt;
-	  return result;
+	  /* Don't bother to set tzname etc.; tzset has already done it.  */
+	  return gmtsub(gmtptr, timep, 0, tmp);
 	if ((sp->goback && t < sp->ats[0]) ||
 		(sp->goahead && t > sp->ats[sp->timecnt - 1])) {
@@ -1368,12 +1369,26 @@ localsub(struct state const *sp, time_t const *timep, int_fast32_t offset,
 	result = timesub(&t, ttisp->tt_gmtoff, sp, tmp);
 	if (result) {
-	  result->tm_isdst = ttisp->tt_isdst;
-	  if (offset)
-	    tzname[result->tm_isdst] = (char *) &sp->chars[ttisp->tt_abbrind];
+	  bool tm_isdst = ttisp->tt_isdst;
+	  char *tm_zone = (char *) &sp->chars[ttisp->tt_abbrind];
+	  result->tm_isdst = tm_isdst;
 #ifdef TM_ZONE
-	  result->TM_ZONE = (char *) &sp->chars[ttisp->tt_abbrind];
+	  result->TM_ZONE = tm_zone;
 #endif /* defined TM_ZONE */
+	  if (offset) {
+	    /* Always set the tzname etc. vars whose values can easily
+	       be determined, as it's too much trouble to tell whether
+	       tzset has already done it correctly.  */
+	    tzname[tm_isdst] = tm_zone;
+#ifdef USG_COMPAT
+	    if (!tm_isdst)
+	      timezone = - ttisp->tt_gmtoff;
+#ifdef ALTZONE
+	    if (tm_isdst)
+	      altzone = - ttisp->tt_gmtoff;
+	  }
 	return result;

More information about the tz mailing list