The zrd zone file reader

John Cowan jcowan at reutershealth.com
Fri Jul 18 17:04:40 UTC 2003


I am submitting the following short program for possible inclusion in the
next release of tzcode.  Zrd (zone reader) is a program that dumps the
contents of a binary zoneinfo file.  It does not depend on the existing
library in any way (except that it calls gmtime, but any gmtime will do),
and provides a direct view into the contents of zoneinfo files.
I have dedicated the program to the public domain.

===== 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.
 * It is followed by:
 *	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]);
		exit(1);
		}
	strcpy(tzfilename, ZONEINFO);
	strcat(tzfilename, argv[1]);
	tz = fopen(tzfilename, "rb");
	if (!tz) {
		fprintf(stderr, "%s: %s unknown\n", argv[0], argv[1]);
		exit(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);
	fclose(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",
			magic);

	/* 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+1, 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;
		else
			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",
			typep->tt_abbrind,
			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");
		printf("\n");
		}

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

	/* Print leap second table */
	printf("%ld leap second entries:\n", t.tzh_leapcnt);
	for (i = 0; i < t.tzh_leapcnt; i++) {
		clock = my_ntohl(leapp[i].tt_time);
		d = gmtime(&clock);
		printf("\tat %4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d UTC (%ld), TAI - UTC = %ld seconds\n",
			d->tm_year + 1900, d->tm_mon+1, d->tm_mday,
			d->tm_hour, d->tm_min, d->tm_sec, clock, 
			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);
	exit(0);
	}

===== cut here =====

-- 
He made the Legislature meet at one-horse       John Cowan
tank-towns out in the alfalfa belt, so that     jcowan at reutershealth.com
hardly nobody could get there and most of       http://www.reutershealth.com
the leaders would stay home and let him go      http://www.ccil.org/~cowan
to work and do things as he pleased.    --Mencken, _Declaration of Independence_



More information about the tz mailing list