setsystz utility: set the kernel's system time zone

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

 



// Hi.  I felt the need to write the following little utility (which
// is mostly comments, really), to prevent my digital camera's image
// files to have incorrect modification when I mount them under Linux.
// Comments are welcome.

// Enjoy!

/// cut after ///
/* setsystz: set the Linux kernel's idea of the time zone */
/* David A. Madore <[email protected]>, 2007-02-19.  Public Domain */

/* Rationale: the Linux kernel needs to have some idea of time zone,
   notably because some filesystems (e.g. FAT) store file
   modification/access times in local time rather than UTC(=GMT)
   (which Unix uses internally for all timestamps).  This kernel
   (system) time zone is set through the settimeofday() system call;
   unfortunately, there does not seem to be a practical way to do it,
   and some (all?) Linux distributions get it wrong: e.g., simply
   because my CMOS clock is set to GMT (as recommended), my Debian
   init scripts apparently assume that any FAT filesystems I'll be
   mounting will have GMT timestamps (uh?).  Note: IMHO, the whole
   idea of having a per-system global time zone is probably wrong, and
   FAT mounts should probably better use an adhoc option to specify
   GMT offset (defaulting to the libc time zone for the mount
   process), and CMOS clock thingies should be kept separate. */

/* What this does: called without arguments, setsystz sets the
   kernel's time zone to the userland's time zone (typically from the
   /etc/localtime file, overridden by the TZ environment variable if
   it exists).  With an explicit argument, setsystz sets the kernel's
   time zone to that many minutes west of GMT (see settimeofday(2) man
   page for explanations).  This program takes care _not_ to
   change/warp the system clock while changing the time zone: see
   comments on avoid_linux_braindeadness() below. */

/* How to use: probably just call "setsystz" (as root) before mounting
   a FAT filesystem, if the files it contains are in your usual system
   time zone.  If they are, e.g., from the Shanghai time zone, then
   use "TZ=Asia/Shanghai setsystz" before mounting.  Note: it's
   probably wiser not to do this while there are existing mounted FAT
   filesystems. */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>

int
auto_minutes (void)
     /* Determine localtime GMT offset and return it in minutes west
	of GMT (as expected by a struct timezone).  This will
	typically use the TZ environment variable if it is defined or,
	as a fallback, the contents of /etc/localtime (see libc
	documentation for more details). */
{
  time_t now = time(NULL);
  struct tm *lt = localtime (&now);
  long int gmtoff = lt->tm_gmtoff;
  fprintf (stderr, "GMT offset=%lds\n", gmtoff);
  if ( gmtoff%60 )
    fprintf (stderr, "warning: GMT offset %lds "
	     "is not an integer number of minutes\n", gmtoff);
  gmtoff /= 60;
  return -gmtoff;
}

void
avoid_linux_braindeadness (void)
     /* We ___DO NOT___ want to change the system time, only the
	system time zone!  Since Linux does something special
	(warp_clock() semantics) the very first time settimeofday() is
	called with tz!=NULL, we call it once with tz pointing to a
	GMT-filled structure, i.e., tz->tz_minuteswest==0 (so the
	clock won't be warped).  The settimeofday(2) man page claims
	that tz->tz_minuteswest==0 will not count toward cancelling
	the warp_clock() semantics, i.e., that our trick does not
	work: fortunately, it is wrong (at least under 2.6.19 and
	whereabouts) and our trick works.  Note however that this
	still resets the time interpolator the first time:
	unfortunately there does not seem to be a way around this
	problem.  See /usr/src/linux/kernel/time.c for details
	about the whole mess.  -- David A. Madore 2007-02-19 */
{
  struct timezone tz;
  memset (&tz, 0, sizeof(struct timezone));
  tz.tz_minuteswest = 0;
  tz.tz_dsttime = 0;
  settimeofday (NULL, &tz);
}

int
main (int argc, char *argv[])
{
  int minuteswest;
  if ( argc == 1 )
    minuteswest = auto_minutes();
  else if ( argc == 2 )
    {
      if ( sscanf (argv[1], "%d", &minuteswest) != 1 )
	{
	  fprintf (stderr, "invalid argument: %s\n", argv[1]);
	  exit (2);
	}
    }
  else
    {
      fprintf (stderr, "wrong number or arguments\n");
      exit (2);
    }
  struct timezone tz;
  memset (&tz, 0, sizeof(struct timezone));
  tz.tz_minuteswest = minuteswest;
  tz.tz_dsttime = 0;
  fprintf (stderr, "setting system time zone to tz_minuteswest=%d\n",
	   minuteswest);
#if 1
  avoid_linux_braindeadness ();
  if ( settimeofday (NULL, &tz) == -1 )
    {
      perror ("settimeofday()");
      exit (EXIT_FAILURE);
    }
#endif
  return 0;
}
/// cut before ///
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux