FW: zdump /zoneinfo question

John Cowan jcowan at reutershealth.com
Mon Nov 22 19:57:47 UTC 1999

John Reynolds wrote:

> My reason for asking is, how can I tell if the curent zoneinfo file is
> accurate given that governments can change the rules at any time.
> I've tried some experiments creating my own zoneinfo files then using zdump
> to display information about it but I see no way of determining the rules.

This seems as good a time as any to release the "zrd" utility: it is public
domain, and allows reading a binary timezone file.  It is written in very
portable C, and does not depend on any part of the tz library except the
timezone files themselves.

I developed it in order to help me interpret timezone files from Java, rather
than redoing all of zic in Java.  I hope it is helpful to you.

If the tz tribal elders wish to make this part of the regular tz distribution
(perhaps in a "contrib" directory?) I certainly have no objection.  There is
no readme or man page: the command line syntax is "zrd continent/city".

-----------------cut here-------------------------------
/* zrd - a routine to dump the contents of a binary timezone file
 * Define ZONEINFO to be the directory where the timezone files are,
 * typically "/usr/share/zoneinfo".
 * This program requires only stdio and "gmtime".
 * Public domain source by John Cowan <cowan at ccil.org>

# define ZONEINFO "/usr/share/zoneinfo/"
# include <stdio.h>
# include <time.h>

/* A handy routine that converts from network order
 * (big-endian) 32-bit values to local order.
 * It tests what local order is, and is not the most
 * efficient conceivable routine, but works OK
 * for this application.

long my_ntohl(long s) {
        long r;
        char *ps;
        char *pr;
        long test = 0x01020304;

        if ((char *)&test == "\0x01\0x02\0x03\0x04") return s;
        ps = (char *) &s;
        pr = (char *) &r;
        pr[0] = ps[3];
        pr[1] = ps[2];
        pr[2] = ps[1];
        pr[3] = ps[0];
        return r;

/* The header of a timezone file.
 * After the header, the following objects may be found:
 *      tzh_timecnt 32-bit values representing time transitions;
 *      tzh_timecnt 1-byte indexes into the time type table;
 *      the time type table, containing tzh_typecnt "struct ttinfo" elements;
 *      the leap-second table, containing tzh_leapcnt "struct ttleap" elements;
 *      tzh.isstdcnt 1-byte flags indicating if transitions are
 *              based on local standard time or local current ("wall") time
 *      tz.isgmtcnt 1-byte flags indicating if transitions are
 *              based on UTC or local time

struct tzhead {
        char    tzh_magic[4];           /* magic number */
        char    tzh_reserved[16];       /* reserved for future use */
        long    tzh_ttisgmtcnt;      /* coded number of trans. time flags */
        long    tzh_ttisstdcnt;      /* coded number of trans. time flags */
        long    tzh_leapcnt;         /* coded number of leap seconds */
        long    tzh_timecnt;         /* coded number of transition times */
        long    tzh_typecnt;         /* coded number of local time types */
        long    tzh_charcnt;         /* coded number of abbr. chars */

/* A time type object

struct ttinfo {
        long tt_gmtoff;         /* offset of this time type from UTC */
        char tt_isdst;          /* whether this is a DST time type */
        char tt_abbrind;        /* index into the timezone name abbreviations */

/* A leap second object

struct ttleap {
        long tt_time;           /* The leap second */
        long tt_seconds;        /* New value of TAI - UTC - 10 */

char tzfilename[1024];          /* Filename of the timezone file */
FILE *tz;                       /* File stream for reading the file */
struct tzhead t;                /* The file's header */
char tzdata[4096];              /* The rest of the file */
long tzsize;                    /* Size of tzdata in use */

/* Pointers to different parts of tzdata

long *timep;
char *timetypep;
char *xtypep;
struct ttinfo *typep;
char *charp;
struct ttleap *leapp;
char *stdp;
char *gmtp;

main(argc, argv)
int argc;
char *argv[];
        int i;
        long gmtoff;
        int gmtsign;
        long clock;
        struct tm *d;
        char magic[5];

        /* Validate command line and load header */
        if (argc != 2) {
                fprintf(stderr, "usage: %s continent/location\n", argv[0]);
        strcpy(tzfilename, ZONEINFO);
        strcat(tzfilename, argv[1]);
        tz = fopen(tzfilename, "rb");
        if (!tz) {
                fprintf(stderr, "%s: %s unknown\n", argv[0], argv[1]);
        fread(&t, sizeof(t), 1, tz);

        /* Localize values in the header */
        strncpy(magic, t.tzh_magic, 4);
        magic[4] = 0;
        t.tzh_ttisgmtcnt = my_ntohl(t.tzh_ttisgmtcnt);
        t.tzh_ttisstdcnt = my_ntohl(t.tzh_ttisstdcnt);
        t.tzh_leapcnt = my_ntohl(t.tzh_leapcnt);
        t.tzh_typecnt = my_ntohl(t.tzh_typecnt);
        t.tzh_timecnt = my_ntohl(t.tzh_timecnt);
        t.tzh_charcnt = my_ntohl(t.tzh_charcnt);

        /* Read the rest of the file */
        tzsize = fread(&tzdata, 1, sizeof(tzdata), tz);

        /* Set up pointers to various sections */
        timep = (long *)tzdata;
        timetypep = (char *)(timep + t.tzh_timecnt);
        xtypep = timetypep + t.tzh_timecnt;
        charp = xtypep + 6 * t.tzh_typecnt;  /* alignment problem */
        leapp = (struct ttleap *)(charp + t.tzh_charcnt);
        stdp = (char *)(leapp + t.tzh_leapcnt);
        gmtp = stdp + t.tzh_ttisstdcnt;

        /* Check for invalid file size */
        if (tzdata + tzsize != gmtp + t.tzh_ttisgmtcnt)
                fprintf(stderr, "Warning: file is %ld bytes, expected %ld\n",
                        tzsize + sizeof(t),
                        (gmtp + t.tzh_ttisgmtcnt) - tzdata);

        /* Print magic number */
        if (magic[0])
                printf("TZ magic number is \"%s\" (normally \"TZif\")\n",

        /* Print transition time table */
        printf("%ld transition times:\n", t.tzh_timecnt);
        for (i = 0; i < t.tzh_timecnt; i++) {
                clock = my_ntohl(timep[i]);
/*              printf("\tat %ld use type %d\n", clock, timetypep[i]);  */
                d = gmtime(&clock);
                printf("\tat %4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d UTC (%ld) use type %d\n",
                        d->tm_year + 1900, d->tm_mon, d->tm_mday,
                        d->tm_hour, d->tm_min, d->tm_sec, clock, timetypep[i]);

        /* Print time types table */
        printf("%ld local time types:\n", t.tzh_typecnt);
        for (i = 0; i < t.tzh_typecnt; i++) {
                typep = (struct ttinfo *)(xtypep + i * 6);
                gmtoff = my_ntohl(typep->tt_gmtoff);
                if (gmtoff < 0) {
                        gmtsign = -1;
                        gmtoff = -gmtoff;
                else if (gmtoff > 0)
                        gmtsign = 1;
                        gmtsign = 0;
                printf("\ttype %d offset %2.2ld:%2.2ld:%2.2ld %s name (%d) %s",
                        i, gmtsign*(gmtoff/3600), (gmtoff%3600)/60, gmtoff%60,
                        typep->tt_isdst ? "isdst " : "notdst",
                        charp + typep->tt_abbrind);
                if (i < t.tzh_ttisstdcnt)
                        printf(" %s", stdp[i] ? "std" : "wall");
                if (i < t.tzh_ttisgmtcnt)
                        printf(" %s", gmtp[i] ? "gmt" : "local");

        /* Print time zone abbreviations */
        printf("%ld bytes of abbreviations: [", t.tzh_charcnt);
        for (i = 0; i < t.tzh_charcnt; i++)
                if (charp[i])
                else if (i != t.tzh_charcnt - 1)

        /* Print leap second table */
        printf("%ld leap second entries:\n", t.tzh_leapcnt);
        for (i = 0; i < t.tzh_leapcnt; i++)
                printf("\tat %ld, TAI - UTC = %ld seconds\n",
                                my_ntohl(leapp[i].tt_seconds) + 10);

        /* Print counts of gmt/local and std/wall flags (contents
         * have already been printed)
        printf("%ld std/wall flags\n", t.tzh_ttisstdcnt);
        printf("%ld gmt/local flags\n", t.tzh_ttisgmtcnt);
-----------------cut here-------------------------------


John Cowan	http://www.reutershealth.com		jcowan at reutershealth.com
Schlingt dreifach einen Kreis vom dies! / Schliess eurer Aug vor heiliger Schau
Den er genoss vom Honig-Tau / Und trank die Milch vom Paradies.
		-- Coleridge (tr. Politzer)

More information about the tz mailing list