1192890Sedwin/*
2192890Sedwin** This file is in the public domain, so clarified as of
3192890Sedwin** 2009-05-17 by Arthur David Olson.
4192890Sedwin*/
5192890Sedwin
630829Scharnier#ifndef lint
730829Scharnierstatic const char rcsid[] =
850479Speter  "$FreeBSD: stable/11/contrib/tzcode/zic/zdump.c 307358 2016-10-15 12:37:57Z bapt $";
9198831Sedwinstatic char	elsieid[] = "@(#)zdump.c	8.10";
1030829Scharnier#endif /* not lint */
1130829Scharnier
122702Swollman/*
132702Swollman** This code has been made independent of the rest of the time
142702Swollman** conversion package to increase confidence in the verification it provides.
152702Swollman** You can use this code to help in verifying other implementations.
162702Swollman*/
172702Swollman
1830829Scharnier#include <err.h>
1930829Scharnier#include <stdio.h>	/* for stdout, stderr */
2030829Scharnier#include <stdlib.h>	/* for exit, malloc, atoi */
2130829Scharnier#include <string.h>	/* for strcpy */
2230829Scharnier#include <sys/types.h>	/* for time_t */
2330829Scharnier#include <time.h>	/* for struct tm */
2430829Scharnier#include <unistd.h>
25192625Sedwin#include <float.h>	/* for FLT_MAX and DBL_MAX */
26192625Sedwin#include <ctype.h>	/* for isalpha et al. */
27192625Sedwin#ifndef isascii
28192625Sedwin#define isascii(x) 1
29192625Sedwin#endif /* !defined isascii */
302702Swollman
31192625Sedwin#ifndef ZDUMP_LO_YEAR
32192625Sedwin#define ZDUMP_LO_YEAR	(-500)
33192625Sedwin#endif /* !defined ZDUMP_LO_YEAR */
34192625Sedwin
35192625Sedwin#ifndef ZDUMP_HI_YEAR
36192625Sedwin#define ZDUMP_HI_YEAR	2500
37192625Sedwin#endif /* !defined ZDUMP_HI_YEAR */
38192625Sedwin
392702Swollman#ifndef MAX_STRING_LENGTH
402702Swollman#define MAX_STRING_LENGTH	1024
412702Swollman#endif /* !defined MAX_STRING_LENGTH */
422702Swollman
432702Swollman#ifndef TRUE
442702Swollman#define TRUE		1
452702Swollman#endif /* !defined TRUE */
462702Swollman
472702Swollman#ifndef FALSE
482702Swollman#define FALSE		0
492702Swollman#endif /* !defined FALSE */
502702Swollman
512702Swollman#ifndef EXIT_SUCCESS
522702Swollman#define EXIT_SUCCESS	0
532702Swollman#endif /* !defined EXIT_SUCCESS */
542702Swollman
552702Swollman#ifndef EXIT_FAILURE
562702Swollman#define EXIT_FAILURE	1
572702Swollman#endif /* !defined EXIT_FAILURE */
582702Swollman
592702Swollman#ifndef SECSPERMIN
602702Swollman#define SECSPERMIN	60
612702Swollman#endif /* !defined SECSPERMIN */
622702Swollman
632702Swollman#ifndef MINSPERHOUR
642702Swollman#define MINSPERHOUR	60
652702Swollman#endif /* !defined MINSPERHOUR */
662702Swollman
672702Swollman#ifndef SECSPERHOUR
682702Swollman#define SECSPERHOUR	(SECSPERMIN * MINSPERHOUR)
692702Swollman#endif /* !defined SECSPERHOUR */
702702Swollman
712702Swollman#ifndef HOURSPERDAY
722702Swollman#define HOURSPERDAY	24
732702Swollman#endif /* !defined HOURSPERDAY */
742702Swollman
752702Swollman#ifndef EPOCH_YEAR
762702Swollman#define EPOCH_YEAR	1970
772702Swollman#endif /* !defined EPOCH_YEAR */
782702Swollman
792702Swollman#ifndef TM_YEAR_BASE
802702Swollman#define TM_YEAR_BASE	1900
812702Swollman#endif /* !defined TM_YEAR_BASE */
822702Swollman
832702Swollman#ifndef DAYSPERNYEAR
842702Swollman#define DAYSPERNYEAR	365
852702Swollman#endif /* !defined DAYSPERNYEAR */
862702Swollman
872702Swollman#ifndef isleap
88192625Sedwin#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
892702Swollman#endif /* !defined isleap */
902702Swollman
91192625Sedwin#ifndef isleap_sum
92192625Sedwin/*
93192625Sedwin** See tzfile.h for details on isleap_sum.
94192625Sedwin*/
95192625Sedwin#define isleap_sum(a, b)	isleap((a) % 400 + (b) % 400)
96192625Sedwin#endif /* !defined isleap_sum */
97192625Sedwin
98192625Sedwin#define SECSPERDAY	((long) SECSPERHOUR * HOURSPERDAY)
99192625Sedwin#define SECSPERNYEAR	(SECSPERDAY * DAYSPERNYEAR)
100192625Sedwin#define SECSPERLYEAR	(SECSPERNYEAR + SECSPERDAY)
101192625Sedwin
102192625Sedwin#ifndef HAVE_GETTEXT
103192625Sedwin#define HAVE_GETTEXT 0
104192625Sedwin#endif
105192625Sedwin#if HAVE_GETTEXT
10617214Swollman#include "locale.h"	/* for setlocale */
10717214Swollman#include "libintl.h"
108192625Sedwin#endif /* HAVE_GETTEXT */
10917214Swollman
1109937Swollman#ifndef GNUC_or_lint
1119937Swollman#ifdef lint
1129937Swollman#define GNUC_or_lint
113192625Sedwin#else /* !defined lint */
1149937Swollman#ifdef __GNUC__
1159937Swollman#define GNUC_or_lint
1169937Swollman#endif /* defined __GNUC__ */
1179937Swollman#endif /* !defined lint */
1189937Swollman#endif /* !defined GNUC_or_lint */
1199937Swollman
1209937Swollman#ifndef INITIALIZE
1219937Swollman#ifdef GNUC_or_lint
1229937Swollman#define INITIALIZE(x)	((x) = 0)
123192625Sedwin#else /* !defined GNUC_or_lint */
1249937Swollman#define INITIALIZE(x)
1259937Swollman#endif /* !defined GNUC_or_lint */
1269937Swollman#endif /* !defined INITIALIZE */
1279937Swollman
12817214Swollman/*
12917214Swollman** For the benefit of GNU folk...
13017214Swollman** `_(MSGID)' uses the current locale's message library string for MSGID.
13117214Swollman** The default is to use gettext if available, and use MSGID otherwise.
13217214Swollman*/
13317214Swollman
13417214Swollman#ifndef _
135192625Sedwin#if HAVE_GETTEXT
13617214Swollman#define _(msgid) gettext(msgid)
137192625Sedwin#else /* !(HAVE_GETTEXT) */
13817214Swollman#define _(msgid) msgid
139192625Sedwin#endif /* !(HAVE_GETTEXT) */
14017214Swollman#endif /* !defined _ */
14117214Swollman
14217214Swollman#ifndef TZ_DOMAIN
14317214Swollman#define TZ_DOMAIN "tz"
14417214Swollman#endif /* !defined TZ_DOMAIN */
14517214Swollman
1462702Swollmanextern char **	environ;
1472702Swollmanextern char *	tzname[2];
1482702Swollman
149192625Sedwinstatic time_t	absolute_min_time;
150192625Sedwinstatic time_t	absolute_max_time;
15142997Swollmanstatic size_t	longest;
152192625Sedwinstatic char *	progname;
153192625Sedwinstatic int	warned;
1542702Swollman
155198831Sedwinstatic void	usage(FILE *stream, int status);
156192625Sedwinstatic char *	abbr(struct tm * tmp);
157192625Sedwinstatic void	abbrok(const char * abbrp, const char * zone);
158192625Sedwinstatic long	delta(struct tm * newp, struct tm * oldp);
159192625Sedwinstatic void	dumptime(const struct tm * tmp);
160192625Sedwinstatic time_t	hunt(char * name, time_t lot, time_t	hit);
161192625Sedwinstatic void	setabsolutes(void);
162192625Sedwinstatic void	show(char * zone, time_t t, int v);
163192625Sedwinstatic const char *	tformat(void);
164192625Sedwinstatic time_t	yeartot(long y);
165192625Sedwin
166192625Sedwin#ifndef TYPECHECK
167192625Sedwin#define my_localtime	localtime
168192625Sedwin#else /* !defined TYPECHECK */
169192625Sedwinstatic struct tm *
170192625Sedwinmy_localtime(tp)
171192625Sedwintime_t *	tp;
172192625Sedwin{
173192625Sedwin	register struct tm *	tmp;
174192625Sedwin
175192625Sedwin	tmp = localtime(tp);
176192625Sedwin	if (tp != NULL && tmp != NULL) {
177192625Sedwin		struct tm	tm;
178192625Sedwin		register time_t	t;
179192625Sedwin
180192625Sedwin		tm = *tmp;
181192625Sedwin		t = mktime(&tm);
182192625Sedwin		if (t - *tp >= 1 || *tp - t >= 1) {
183192625Sedwin			(void) fflush(stdout);
184192625Sedwin			(void) fprintf(stderr, "\n%s: ", progname);
185192625Sedwin			(void) fprintf(stderr, tformat(), *tp);
186192625Sedwin			(void) fprintf(stderr, " ->");
187192625Sedwin			(void) fprintf(stderr, " year=%d", tmp->tm_year);
188192625Sedwin			(void) fprintf(stderr, " mon=%d", tmp->tm_mon);
189192625Sedwin			(void) fprintf(stderr, " mday=%d", tmp->tm_mday);
190192625Sedwin			(void) fprintf(stderr, " hour=%d", tmp->tm_hour);
191192625Sedwin			(void) fprintf(stderr, " min=%d", tmp->tm_min);
192192625Sedwin			(void) fprintf(stderr, " sec=%d", tmp->tm_sec);
193192625Sedwin			(void) fprintf(stderr, " isdst=%d", tmp->tm_isdst);
194192625Sedwin			(void) fprintf(stderr, " -> ");
195192625Sedwin			(void) fprintf(stderr, tformat(), t);
196192625Sedwin			(void) fprintf(stderr, "\n");
197192625Sedwin		}
198192625Sedwin	}
199192625Sedwin	return tmp;
200192625Sedwin}
201192625Sedwin#endif /* !defined TYPECHECK */
202192625Sedwin
203192625Sedwinstatic void
204192625Sedwinabbrok(abbrp, zone)
205192625Sedwinconst char * const	abbrp;
206192625Sedwinconst char * const	zone;
207192625Sedwin{
208192625Sedwin	register const char *	cp;
209192625Sedwin	register char *		wp;
210192625Sedwin
211192625Sedwin	if (warned)
212192625Sedwin		return;
213192625Sedwin	cp = abbrp;
214192625Sedwin	wp = NULL;
215307358Sbapt	while (isascii((unsigned char) *cp) &&
216307358Sbapt		(isalnum((unsigned char)*cp) || *cp == '-' || *cp == '+'))
217192625Sedwin		++cp;
218307358Sbapt	if (cp - abbrp < 3)
219307358Sbapt		wp = _("has fewer than 3 characters");
220192625Sedwin	else if (cp - abbrp > 6)
221307358Sbapt		wp = _("has more than 6 characters");
222307358Sbapt	else if (*cp)
223307358Sbapt		wp = "has characters other than ASCII alphanumerics, '-' or '+'";
224307358Sbapt	else
225192625Sedwin		return;
226192625Sedwin	(void) fflush(stdout);
227192625Sedwin	(void) fprintf(stderr,
228192625Sedwin		_("%s: warning: zone \"%s\" abbreviation \"%s\" %s\n"),
229192625Sedwin		progname, zone, abbrp, wp);
230192625Sedwin	warned = TRUE;
231192625Sedwin}
232192625Sedwin
2332702Swollmanint
2342702Swollmanmain(argc, argv)
2352702Swollmanint	argc;
2362702Swollmanchar *	argv[];
2372702Swollman{
2389937Swollman	register int		i;
2399937Swollman	register int		c;
2409937Swollman	register int		vflag;
241192625Sedwin	register char *		cutarg;
242192625Sedwin	register long		cutloyear = ZDUMP_LO_YEAR;
243192625Sedwin	register long		cuthiyear = ZDUMP_HI_YEAR;
244192625Sedwin	register time_t		cutlotime;
245192625Sedwin	register time_t		cuthitime;
246192625Sedwin	register char **	fakeenv;
2479937Swollman	time_t			now;
2489937Swollman	time_t			t;
2499937Swollman	time_t			newt;
2509937Swollman	struct tm		tm;
2519937Swollman	struct tm		newtm;
252192625Sedwin	register struct tm *	tmp;
253192625Sedwin	register struct tm *	newtmp;
2542702Swollman
255228342Seadler	progname=argv[0];
256192625Sedwin	INITIALIZE(cutlotime);
257192625Sedwin	INITIALIZE(cuthitime);
258192625Sedwin#if HAVE_GETTEXT
25917214Swollman	(void) setlocale(LC_MESSAGES, "");
26017214Swollman#ifdef TZ_DOMAINDIR
26117214Swollman	(void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
262192625Sedwin#endif /* TEXTDOMAINDIR */
26317214Swollman	(void) textdomain(TZ_DOMAIN);
264192625Sedwin#endif /* HAVE_GETTEXT */
265130819Sstefanf	for (i = 1; i < argc; ++i)
266130819Sstefanf		if (strcmp(argv[i], "--version") == 0) {
267130819Sstefanf			errx(EXIT_SUCCESS, "%s", elsieid);
268192625Sedwin		} else if (strcmp(argv[i], "--help") == 0) {
269198831Sedwin			usage(stdout, EXIT_SUCCESS);
270130819Sstefanf		}
2712702Swollman	vflag = 0;
272192625Sedwin	cutarg = NULL;
2732702Swollman	while ((c = getopt(argc, argv, "c:v")) == 'c' || c == 'v')
2742702Swollman		if (c == 'v')
2752702Swollman			vflag = 1;
276192625Sedwin		else	cutarg = optarg;
277176407Sru	if ((c != -1) ||
2782702Swollman		(optind == argc - 1 && strcmp(argv[optind], "=") == 0)) {
279198831Sedwin			usage(stderr, EXIT_FAILURE);
2802702Swollman	}
281192625Sedwin	if (vflag) {
282192625Sedwin		if (cutarg != NULL) {
283192625Sedwin			long	lo;
284192625Sedwin			long	hi;
285192625Sedwin			char	dummy;
2862702Swollman
287192625Sedwin			if (sscanf(cutarg, "%ld%c", &hi, &dummy) == 1) {
288192625Sedwin				cuthiyear = hi;
289192625Sedwin			} else if (sscanf(cutarg, "%ld,%ld%c",
290192625Sedwin				&lo, &hi, &dummy) == 2) {
291192625Sedwin					cutloyear = lo;
292192625Sedwin					cuthiyear = hi;
293192625Sedwin			} else {
294192625Sedwin(void) fprintf(stderr, _("%s: wild -c argument %s\n"),
295192625Sedwin					progname, cutarg);
296192625Sedwin				exit(EXIT_FAILURE);
297192625Sedwin			}
298192625Sedwin		}
299192625Sedwin		setabsolutes();
300192625Sedwin		cutlotime = yeartot(cutloyear);
301192625Sedwin		cuthitime = yeartot(cuthiyear);
3022702Swollman	}
3032702Swollman	(void) time(&now);
3042702Swollman	longest = 0;
3052702Swollman	for (i = optind; i < argc; ++i)
3062702Swollman		if (strlen(argv[i]) > longest)
3072702Swollman			longest = strlen(argv[i]);
3089937Swollman	{
3099937Swollman		register int	from;
3109937Swollman		register int	to;
3112702Swollman
312192625Sedwin		for (i = 0;  environ[i] != NULL; ++i)
3139937Swollman			continue;
3149937Swollman		fakeenv = (char **) malloc((size_t) ((i + 2) *
3159937Swollman			sizeof *fakeenv));
3169937Swollman		if (fakeenv == NULL ||
3179937Swollman			(fakeenv[0] = (char *) malloc((size_t) (longest +
31830829Scharnier				4))) == NULL)
31930829Scharnier					errx(EXIT_FAILURE,
32030829Scharnier					     _("malloc() failed"));
3219937Swollman		to = 0;
3229937Swollman		(void) strcpy(fakeenv[to++], "TZ=");
3239937Swollman		for (from = 0; environ[from] != NULL; ++from)
3249937Swollman			if (strncmp(environ[from], "TZ=", 3) != 0)
3259937Swollman				fakeenv[to++] = environ[from];
3269937Swollman		fakeenv[to] = NULL;
3272702Swollman		environ = fakeenv;
3289937Swollman	}
3299937Swollman	for (i = optind; i < argc; ++i) {
3309937Swollman		static char	buf[MAX_STRING_LENGTH];
3319937Swollman
3329937Swollman		(void) strcpy(&fakeenv[0][3], argv[i]);
33317214Swollman		if (!vflag) {
33417214Swollman			show(argv[i], now, FALSE);
3352702Swollman			continue;
33617214Swollman		}
337192625Sedwin		warned = FALSE;
338192625Sedwin		t = absolute_min_time;
3392702Swollman		show(argv[i], t, TRUE);
3402702Swollman		t += SECSPERHOUR * HOURSPERDAY;
3412702Swollman		show(argv[i], t, TRUE);
342192625Sedwin		if (t < cutlotime)
343192625Sedwin			t = cutlotime;
344192625Sedwin		tmp = my_localtime(&t);
345192625Sedwin		if (tmp != NULL) {
346192625Sedwin			tm = *tmp;
347192625Sedwin			(void) strncpy(buf, abbr(&tm), (sizeof buf) - 1);
348192625Sedwin		}
3492702Swollman		for ( ; ; ) {
350192625Sedwin			if (t >= cuthitime || t >= cuthitime - SECSPERHOUR * 12)
3512702Swollman				break;
3522702Swollman			newt = t + SECSPERHOUR * 12;
353192625Sedwin			newtmp = localtime(&newt);
354192625Sedwin			if (newtmp != NULL)
355192625Sedwin				newtm = *newtmp;
356192625Sedwin			if ((tmp == NULL || newtmp == NULL) ? (tmp != newtmp) :
357192625Sedwin				(delta(&newtm, &tm) != (newt - t) ||
3582702Swollman				newtm.tm_isdst != tm.tm_isdst ||
359192625Sedwin				strcmp(abbr(&newtm), buf) != 0)) {
3602702Swollman					newt = hunt(argv[i], t, newt);
361192625Sedwin					newtmp = localtime(&newt);
362192625Sedwin					if (newtmp != NULL) {
363192625Sedwin						newtm = *newtmp;
364192625Sedwin						(void) strncpy(buf,
365192625Sedwin							abbr(&newtm),
366192625Sedwin							(sizeof buf) - 1);
367192625Sedwin					}
3682702Swollman			}
3692702Swollman			t = newt;
3702702Swollman			tm = newtm;
371192625Sedwin			tmp = newtmp;
3722702Swollman		}
373192625Sedwin		t = absolute_max_time;
3742702Swollman		t -= SECSPERHOUR * HOURSPERDAY;
3752702Swollman		show(argv[i], t, TRUE);
3762702Swollman		t += SECSPERHOUR * HOURSPERDAY;
3772702Swollman		show(argv[i], t, TRUE);
3782702Swollman	}
37930829Scharnier	if (fflush(stdout) || ferror(stdout))
38030829Scharnier		errx(EXIT_FAILURE, _("error writing standard output"));
3812702Swollman	exit(EXIT_SUCCESS);
382192625Sedwin	/* If exit fails to exit... */
383192625Sedwin	return(EXIT_FAILURE);
384192625Sedwin}
3852702Swollman
386192625Sedwinstatic void
387192625Sedwinsetabsolutes(void)
388192625Sedwin{
389192625Sedwin	if (0.5 == (time_t) 0.5) {
390192625Sedwin		/*
391192625Sedwin		** time_t is floating.
392192625Sedwin		*/
393192625Sedwin		if (sizeof (time_t) == sizeof (float)) {
394192625Sedwin			absolute_min_time = (time_t) -FLT_MAX;
395192625Sedwin			absolute_max_time = (time_t) FLT_MAX;
396192625Sedwin		} else if (sizeof (time_t) == sizeof (double)) {
397192625Sedwin			absolute_min_time = (time_t) -DBL_MAX;
398192625Sedwin			absolute_max_time = (time_t) DBL_MAX;
399192625Sedwin		} else {
400192625Sedwin			(void) fprintf(stderr,
401192625Sedwin_("%s: use of -v on system with floating time_t other than float or double\n"),
402192625Sedwin				progname);
403192625Sedwin			exit(EXIT_FAILURE);
404192625Sedwin		}
405192625Sedwin	} else if (0 > (time_t) -1) {
406192625Sedwin		/*
407192625Sedwin		** time_t is signed.  Assume overflow wraps around.
408192625Sedwin		*/
409192625Sedwin		time_t t = 0;
410192625Sedwin		time_t t1 = 1;
411192625Sedwin
412192625Sedwin		while (t < t1) {
413192625Sedwin			t = t1;
414192625Sedwin			t1 = 2 * t1 + 1;
415192625Sedwin		}
416192625Sedwin
417192625Sedwin		absolute_max_time = t;
418192625Sedwin		t = -t;
419192625Sedwin		absolute_min_time = t - 1;
420192625Sedwin		if (t < absolute_min_time)
421192625Sedwin			absolute_min_time = t;
422192625Sedwin	} else {
423192625Sedwin		/*
424192625Sedwin		** time_t is unsigned.
425192625Sedwin		*/
426192625Sedwin		absolute_min_time = 0;
427192625Sedwin		absolute_max_time = absolute_min_time - 1;
428192625Sedwin	}
4292702Swollman}
4302702Swollman
431192625Sedwinstatic time_t
432192625Sedwinyeartot(y)
433192625Sedwinconst long	y;
434192625Sedwin{
435192625Sedwin	register long	myy;
436192625Sedwin	register long	seconds;
437192625Sedwin	register time_t	t;
438192625Sedwin
439192625Sedwin	myy = EPOCH_YEAR;
440192625Sedwin	t = 0;
441192625Sedwin	while (myy != y) {
442192625Sedwin		if (myy < y) {
443192625Sedwin			seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
444192625Sedwin			++myy;
445192625Sedwin			if (t > absolute_max_time - seconds) {
446192625Sedwin				t = absolute_max_time;
447192625Sedwin				break;
448192625Sedwin			}
449192625Sedwin			t += seconds;
450192625Sedwin		} else {
451192625Sedwin			--myy;
452192625Sedwin			seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
453192625Sedwin			if (t < absolute_min_time + seconds) {
454192625Sedwin				t = absolute_min_time;
455192625Sedwin				break;
456192625Sedwin			}
457192625Sedwin			t -= seconds;
458192625Sedwin		}
459192625Sedwin	}
460192625Sedwin	return t;
461192625Sedwin}
462192625Sedwin
46330829Scharnierstatic void
464198831Sedwinusage(FILE *stream, int status)
46530829Scharnier{
466192625Sedwin	fprintf(stream,
467192625Sedwin_("usage: %s [--version] [-v] [--help] [-c [loyear,]hiyear] zonename ...\n\
468192625Sedwin\n\
469192625SedwinReport bugs to tz@elsie.nci.nih.gov.\n"), progname);
470192625Sedwin	exit(status);
47130829Scharnier}
47230829Scharnier
4732702Swollmanstatic time_t
474192625Sedwinhunt(char *name, time_t lot, time_t hit)
4752702Swollman{
476192625Sedwin	time_t			t;
477192625Sedwin	long			diff;
478192625Sedwin	struct tm		lotm;
479192625Sedwin	register struct tm *	lotmp;
480192625Sedwin	struct tm		tm;
481192625Sedwin	register struct tm *	tmp;
482192625Sedwin	char			loab[MAX_STRING_LENGTH];
4832702Swollman
484192625Sedwin	lotmp = my_localtime(&lot);
485192625Sedwin	if (lotmp != NULL) {
486192625Sedwin		lotm = *lotmp;
487192625Sedwin		(void) strncpy(loab, abbr(&lotm), (sizeof loab) - 1);
488192625Sedwin	}
489192625Sedwin	for ( ; ; ) {
490192625Sedwin		diff = (long) (hit - lot);
491192625Sedwin		if (diff < 2)
492192625Sedwin			break;
493192625Sedwin		t = lot;
494192625Sedwin		t += diff / 2;
4952702Swollman		if (t <= lot)
4962702Swollman			++t;
4972702Swollman		else if (t >= hit)
4982702Swollman			--t;
499192625Sedwin		tmp = my_localtime(&t);
500192625Sedwin		if (tmp != NULL)
501192625Sedwin			tm = *tmp;
502192625Sedwin		if ((lotmp == NULL || tmp == NULL) ? (lotmp == tmp) :
503192625Sedwin			(delta(&tm, &lotm) == (t - lot) &&
5042702Swollman			tm.tm_isdst == lotm.tm_isdst &&
505192625Sedwin			strcmp(abbr(&tm), loab) == 0)) {
5062702Swollman				lot = t;
5072702Swollman				lotm = tm;
508192625Sedwin				lotmp = tmp;
5092702Swollman		} else	hit = t;
5102702Swollman	}
5112702Swollman	show(name, lot, TRUE);
5122702Swollman	show(name, hit, TRUE);
5132702Swollman	return hit;
5142702Swollman}
5152702Swollman
5162702Swollman/*
517192625Sedwin** Thanks to Paul Eggert for logic used in delta.
5182702Swollman*/
5192702Swollman
5202702Swollmanstatic long
5212702Swollmandelta(newp, oldp)
5222702Swollmanstruct tm *	newp;
5232702Swollmanstruct tm *	oldp;
5242702Swollman{
525192625Sedwin	register long	result;
526192625Sedwin	register int	tmy;
5272702Swollman
5282702Swollman	if (newp->tm_year < oldp->tm_year)
5292702Swollman		return -delta(oldp, newp);
5302702Swollman	result = 0;
5312702Swollman	for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy)
532192625Sedwin		result += DAYSPERNYEAR + isleap_sum(tmy, TM_YEAR_BASE);
5332702Swollman	result += newp->tm_yday - oldp->tm_yday;
5342702Swollman	result *= HOURSPERDAY;
5352702Swollman	result += newp->tm_hour - oldp->tm_hour;
5362702Swollman	result *= MINSPERHOUR;
5372702Swollman	result += newp->tm_min - oldp->tm_min;
5382702Swollman	result *= SECSPERMIN;
5392702Swollman	result += newp->tm_sec - oldp->tm_sec;
5402702Swollman	return result;
5412702Swollman}
5422702Swollman
5432702Swollmanstatic void
544192625Sedwinshow(char *zone, time_t t, int v)
5452702Swollman{
546192625Sedwin	register struct tm *	tmp;
5472702Swollman
54842997Swollman	(void) printf("%-*s  ", (int) longest, zone);
5492702Swollman	if (v) {
550192625Sedwin		tmp = gmtime(&t);
551192625Sedwin		if (tmp == NULL) {
552192625Sedwin			(void) printf(tformat(), t);
553192625Sedwin		} else {
554192625Sedwin			dumptime(tmp);
555192625Sedwin			(void) printf(" UTC");
556192625Sedwin		}
557192625Sedwin		(void) printf(" = ");
558192625Sedwin	}
559192625Sedwin	tmp = my_localtime(&t);
560192625Sedwin	dumptime(tmp);
561192625Sedwin	if (tmp != NULL) {
562192625Sedwin		if (*abbr(tmp) != '\0')
563192625Sedwin			(void) printf(" %s", abbr(tmp));
564192625Sedwin		if (v) {
565192625Sedwin			(void) printf(" isdst=%d", tmp->tm_isdst);
5662702Swollman#ifdef TM_GMTOFF
567192625Sedwin			(void) printf(" gmtoff=%ld", tmp->TM_GMTOFF);
5682702Swollman#endif /* defined TM_GMTOFF */
569192625Sedwin		}
5702702Swollman	}
5712702Swollman	(void) printf("\n");
572192625Sedwin	if (tmp != NULL && *abbr(tmp) != '\0')
573192625Sedwin		abbrok(abbr(tmp), zone);
5742702Swollman}
5752702Swollman
5762702Swollmanstatic char *
5772702Swollmanabbr(tmp)
5782702Swollmanstruct tm *	tmp;
5792702Swollman{
5802702Swollman	register char *	result;
5819937Swollman	static char	nada;
5822702Swollman
5832702Swollman	if (tmp->tm_isdst != 0 && tmp->tm_isdst != 1)
5849937Swollman		return &nada;
5852702Swollman	result = tzname[tmp->tm_isdst];
5869937Swollman	return (result == NULL) ? &nada : result;
5872702Swollman}
588192625Sedwin
589192625Sedwin/*
590192625Sedwin** The code below can fail on certain theoretical systems;
591192625Sedwin** it works on all known real-world systems as of 2004-12-30.
592192625Sedwin*/
593192625Sedwin
594192625Sedwinstatic const char *
595192625Sedwintformat(void)
596192625Sedwin{
597192625Sedwin	if (0.5 == (time_t) 0.5) {	/* floating */
598192625Sedwin		if (sizeof (time_t) > sizeof (double))
599192625Sedwin			return "%Lg";
600192625Sedwin		return "%g";
601192625Sedwin	}
602192625Sedwin	if (0 > (time_t) -1) {		/* signed */
603192625Sedwin		if (sizeof (time_t) > sizeof (long))
604192625Sedwin			return "%lld";
605192625Sedwin		if (sizeof (time_t) > sizeof (int))
606192625Sedwin			return "%ld";
607192625Sedwin		return "%d";
608192625Sedwin	}
609192625Sedwin	if (sizeof (time_t) > sizeof (unsigned long))
610192625Sedwin		return "%llu";
611192625Sedwin	if (sizeof (time_t) > sizeof (unsigned int))
612192625Sedwin		return "%lu";
613192625Sedwin	return "%u";
614192625Sedwin}
615192625Sedwin
616192625Sedwinstatic void
617192625Sedwindumptime(timeptr)
618192625Sedwinregister const struct tm *	timeptr;
619192625Sedwin{
620192625Sedwin	static const char	wday_name[][3] = {
621192625Sedwin		"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
622192625Sedwin	};
623192625Sedwin	static const char	mon_name[][3] = {
624192625Sedwin		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
625192625Sedwin		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
626192625Sedwin	};
627192625Sedwin	register const char *	wn;
628192625Sedwin	register const char *	mn;
629192625Sedwin	register int		lead;
630192625Sedwin	register int		trail;
631192625Sedwin
632192625Sedwin	if (timeptr == NULL) {
633192625Sedwin		(void) printf("NULL");
634192625Sedwin		return;
635192625Sedwin	}
636192625Sedwin	/*
637192625Sedwin	** The packaged versions of localtime and gmtime never put out-of-range
638192625Sedwin	** values in tm_wday or tm_mon, but since this code might be compiled
639192625Sedwin	** with other (perhaps experimental) versions, paranoia is in order.
640192625Sedwin	*/
641192625Sedwin	if (timeptr->tm_wday < 0 || timeptr->tm_wday >=
642192625Sedwin		(int) (sizeof wday_name / sizeof wday_name[0]))
643192625Sedwin			wn = "???";
644192625Sedwin	else		wn = wday_name[timeptr->tm_wday];
645192625Sedwin	if (timeptr->tm_mon < 0 || timeptr->tm_mon >=
646192625Sedwin		(int) (sizeof mon_name / sizeof mon_name[0]))
647192625Sedwin			mn = "???";
648192625Sedwin	else		mn = mon_name[timeptr->tm_mon];
649192625Sedwin	(void) printf("%.3s %.3s%3d %.2d:%.2d:%.2d ",
650192625Sedwin		wn, mn,
651192625Sedwin		timeptr->tm_mday, timeptr->tm_hour,
652192625Sedwin		timeptr->tm_min, timeptr->tm_sec);
653192625Sedwin#define DIVISOR	10
654192625Sedwin	trail = timeptr->tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR;
655192625Sedwin	lead = timeptr->tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR +
656192625Sedwin		trail / DIVISOR;
657192625Sedwin	trail %= DIVISOR;
658192625Sedwin	if (trail < 0 && lead > 0) {
659192625Sedwin		trail += DIVISOR;
660192625Sedwin		--lead;
661192625Sedwin	} else if (lead < 0 && trail > 0) {
662192625Sedwin		trail -= DIVISOR;
663192625Sedwin		++lead;
664192625Sedwin	}
665192625Sedwin	if (lead == 0)
666192625Sedwin		(void) printf("%d", trail);
667192625Sedwin	else	(void) printf("%d%d", lead, ((trail < 0) ? -trail : trail));
668192625Sedwin}
669