/*
fix-sys_tz.c -- set Linux kernel timezone sys_tz correctly

When mounting FAT filesystems, the kernel timezone sys_tz needs to be
set correctly to ensure that timestamps are properly translated from
FAT (which uses local time) to the system time.

For details, see: /usr/src/linux/fs/fat/misc.c

Be aware of some pitfalls with settimeofday (from "man 2 gettimeofday"):

       Under  Linux  there are some peculiar "warp clock" semantics associated
       with the settimeofday() system call if on the very  first  call  (after
       booting)  that  has a non-NULL tz argument, the tv argument is NULL and
       the tz_minuteswest field is nonzero.  In such a case it is assumed that
       the  CMOS  clock is on local time, and that it has to be incremented by
       this amount to get UTC system time.  No doubt it is a bad idea  to  use
       this feature.

For details, see: /usr/src/linux/kernel/time.c

Usage:
  gcc -o fix-sys_tz fix-sys_tz.c   => build
  ./fix-sys_tz                     => show current timezone
  sudo ./fix-sys_tz -60            => set sys_tz to -60 minutes west
  sudo ./fix-sys_tz a              => set sys_tz automatically

This program is available under the terms of the GNU General Public
License (GPL). All usual disclaimers apply!

http://www.heikopurnhagen.net/software/fix-sys_tz.c

Heiko Purnhagen   (mail@heikopurnhagen.net)
2010-12-11 initial version
*/

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

struct timeval tv;
struct timezone tz;
int r;
char *endptr;

int main (int argc, char *argv[])
{
  tzset();
  printf("tzset()\n");
  printf(" tzname[0]: %s\n",tzname[0]);
  printf(" tzname[1]: %s\n",tzname[1]);
  printf(" timezone:  %ld\n",timezone);
  printf(" daylight:  %d\n",daylight);

  r = gettimeofday(&tv,&tz);
  printf("gettimeofday(): %s\n",r?"Error":"OK");
  printf(" tv_sec:         %d\n",tv.tv_sec);
  printf(" tz_usec:        %d\n",tv.tv_usec);
  printf(" tz_minuteswest: %d\n",tz.tz_minuteswest);
  printf(" tz_dsttime:     %d\n",tz.tz_dsttime);

  if (argc > 1) {
    /* always set tz_minuteswest to 0 first, to avoid warp_clock() */
    tz.tz_minuteswest = 0;
    tz.tz_dsttime = 0;
    printf("NEW TZ: minuteswest=%d dsttime=%d (to avoid warp_clock())\n",
	   tz.tz_minuteswest,tz.tz_dsttime);
    r = settimeofday(NULL,&tz);
    printf("settimeofday(): %s\n",r?"Error":"OK");

    tz.tz_minuteswest = strtol(argv[1],&endptr,10);
    if (argv[1] == endptr) {
      /* set tz_minuteswest automatically */
      tz.tz_minuteswest = timezone/60;
    }
    printf("NEW TZ: minuteswest=%d dsttime=%d\n",
	   tz.tz_minuteswest,tz.tz_dsttime);
    r = settimeofday(NULL,&tz);
    printf("settimeofday(): %s\n",r?"Error":"OK");

    r = gettimeofday(&tv,&tz);
    printf("gettimeofday(): %s\n",r?"Error":"OK");
    printf(" tv_sec:         %d\n",tv.tv_sec);
    printf(" tz_usec:        %d\n",tv.tv_usec);
    printf(" tz_minuteswest: %d\n",tz.tz_minuteswest);
    printf(" tz_dsttime:     %d\n",tz.tz_dsttime);
  }
}

