[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#48184: Solution



I've also just hit this problem using a laptop while flying from
Amsterdam to London - and the two bugs are basically the same thing.

The problem is with the routine tzset_internal(int) in tzset.c in libc6.

It does the following:

  /* Examine the TZ environment variable.  */
  tz = getenv ("TZ");
  if (tz == NULL)
    /* No user specification; use the site-wide default.  */
    tz = TZDEFAULT;
 
[..]

  /* Check whether the value changes since the last run.  */
  if (old_tz != NULL && tz != NULL && strcmp (tz, old_tz) == 0)
    /* No change, simply return.  */
    return;
 

Now, TZDEFAULT is /etc/localtime, which is a symlink on a Debian system.
Thus, when you call tzset() (or localtime(), which calls it) after
changing the system timezone with tzconfig, it has absolutely no effect
- it really should be looking at the name of the target of the symlink,
not the name of the symlink itself.

The result of this is that changing the timezone effectively requires
rebooting the machine to ensure all processes detect the change.

There is a way around it: if the code has something like the following
in its inner loop:

        if (!getenv("TZ"))
        {
            putenv("TZ=GMT");
            tzset();
            putenv("TZ");
        }

this will force the code to reevaluate the timezone file - twice. This
is however CPU-inefficient and would require all programs on the system
that deal with time (from syslog to Evolution) to jump through this
hoop.

A better solution would be the following patch to tzset.c which checks
/etc/localtime for modification each time it is called:

redferni@wilma:~$ diff -u tzset.c.orig tzset.c
--- tzset.c.orig        2002-03-12 09:19:36.000000000 +0000
+++ tzset.c     2003-07-24 16:57:33.000000000 +0100
@@ -23,6 +23,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/stat.h>
 #include <time.h>
  
  
@@ -147,11 +148,13 @@
      int always;
 {
   static int is_initialized;
+  static time_t file_lastmod;
   register const char *tz;
   register size_t l;
   char *tzbuf;
   unsigned short int hh, mm, ss;
   unsigned short int whichrule;
+  struct stat sbuf;
  
   if (is_initialized && !always)
     return;
@@ -174,8 +177,15 @@
  
   /* Check whether the value changes since the last run.  */
   if (old_tz != NULL && tz != NULL && strcmp (tz, old_tz) == 0)
-    /* No change, simply return.  */
-    return;
+  {
+    /* Check if symlink has new target */
+    if (!lstat(tz, &sbuf) && sbuf.st_mtime == file_lastmod)
+    {
+      /* No change, simply return.  */
+      return;
+    }
+    file_lastmod = sbuf.st_mtime;
+  }
  
   tz_rules[0].name = NULL;
   tz_rules[1].name = NULL;


It adds a call to stat() to each call to localtime, but ensures that
every program on the system changes when the timezone changes - quite
invaluable to those of us who travel with laptops and irrelevant to
anyone else. It can be switched off by setting the TZ variable
explicitly.

If the GNOME clock people can put the inner loop patch at the start of
clock.c:update_clock(), this would fix the problem for GNOME clock, but
I would prefer to see libc6 fix it globally.

NB This has nothing to do with Daylight Saving Time, which works
perfectly - this is all about reconfiguring a running system (e.g. with
tzconfig) to change its default timezone, say from Europe/Amsterdam to
Europe/London.

Thanks,
Ian Redfern.
-- 
Ian Redfern
Consultant
UK Telecoms Solutions
LogicaCMG
Tel: +44 20 7637 9111
Email: Ian.Redfern@LogicaCMG.com
www.logicacmg.com




Reply to: