xref: /openbsd/usr.sbin/zdump/zdump.c (revision 66e58127)
1*66e58127Stedu /*	$OpenBSD: zdump.c,v 1.3 2015/02/09 12:52:45 tedu Exp $ */
22059035fStedu /*
32059035fStedu ** This file is in the public domain, so clarified as of
42059035fStedu ** 2009-05-17 by Arthur David Olson.
52059035fStedu */
62059035fStedu 
72059035fStedu /*
82059035fStedu ** This code has been made independent of the rest of the time
92059035fStedu ** conversion package to increase confidence in the verification it provides.
102059035fStedu ** You can use this code to help in verifying other implementations.
112059035fStedu */
122059035fStedu 
13784122ecStedu #include <ctype.h>	/* for isalpha et al. */
14784122ecStedu #include <float.h>	/* for FLT_MAX and DBL_MAX */
15784122ecStedu #include <stdio.h>	/* for stdout, stderr, perror */
16784122ecStedu #include <string.h>	/* for strlcpy */
17784122ecStedu #include <stdlib.h>	/* for exit, malloc, atoi */
18784122ecStedu #include <time.h>	/* for struct tm */
192059035fStedu 
202059035fStedu #ifndef ZDUMP_LO_YEAR
212059035fStedu #define ZDUMP_LO_YEAR	(-500)
222059035fStedu #endif /* !defined ZDUMP_LO_YEAR */
232059035fStedu 
242059035fStedu #ifndef ZDUMP_HI_YEAR
252059035fStedu #define ZDUMP_HI_YEAR	2500
262059035fStedu #endif /* !defined ZDUMP_HI_YEAR */
272059035fStedu 
282059035fStedu #ifndef MAX_STRING_LENGTH
292059035fStedu #define MAX_STRING_LENGTH	1024
302059035fStedu #endif /* !defined MAX_STRING_LENGTH */
312059035fStedu 
322059035fStedu #ifndef TRUE
332059035fStedu #define TRUE		1
342059035fStedu #endif /* !defined TRUE */
352059035fStedu 
362059035fStedu #ifndef FALSE
372059035fStedu #define FALSE		0
382059035fStedu #endif /* !defined FALSE */
392059035fStedu 
402059035fStedu #ifndef SECSPERMIN
412059035fStedu #define SECSPERMIN	60
422059035fStedu #endif /* !defined SECSPERMIN */
432059035fStedu 
442059035fStedu #ifndef MINSPERHOUR
452059035fStedu #define MINSPERHOUR	60
462059035fStedu #endif /* !defined MINSPERHOUR */
472059035fStedu 
482059035fStedu #ifndef SECSPERHOUR
492059035fStedu #define SECSPERHOUR	(SECSPERMIN * MINSPERHOUR)
502059035fStedu #endif /* !defined SECSPERHOUR */
512059035fStedu 
522059035fStedu #ifndef HOURSPERDAY
532059035fStedu #define HOURSPERDAY	24
542059035fStedu #endif /* !defined HOURSPERDAY */
552059035fStedu 
562059035fStedu #ifndef EPOCH_YEAR
572059035fStedu #define EPOCH_YEAR	1970
582059035fStedu #endif /* !defined EPOCH_YEAR */
592059035fStedu 
602059035fStedu #ifndef TM_YEAR_BASE
612059035fStedu #define TM_YEAR_BASE	1900
622059035fStedu #endif /* !defined TM_YEAR_BASE */
632059035fStedu 
642059035fStedu #ifndef DAYSPERNYEAR
652059035fStedu #define DAYSPERNYEAR	365
662059035fStedu #endif /* !defined DAYSPERNYEAR */
672059035fStedu 
682059035fStedu #ifndef isleap
692059035fStedu #define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
702059035fStedu #endif /* !defined isleap */
712059035fStedu 
722059035fStedu #ifndef isleap_sum
732059035fStedu /*
742059035fStedu ** See tzfile.h for details on isleap_sum.
752059035fStedu */
762059035fStedu #define isleap_sum(a, b)	isleap((a) % 400 + (b) % 400)
772059035fStedu #endif /* !defined isleap_sum */
782059035fStedu 
792059035fStedu #define SECSPERDAY	((long) SECSPERHOUR * HOURSPERDAY)
802059035fStedu #define SECSPERNYEAR	(SECSPERDAY * DAYSPERNYEAR)
812059035fStedu #define SECSPERLYEAR	(SECSPERNYEAR + SECSPERDAY)
822059035fStedu 
832059035fStedu #ifndef HAVE_GETTEXT
842059035fStedu #define HAVE_GETTEXT 0
852059035fStedu #endif
862059035fStedu #if HAVE_GETTEXT
872059035fStedu #include "locale.h"	/* for setlocale */
882059035fStedu #include "libintl.h"
892059035fStedu #endif /* HAVE_GETTEXT */
902059035fStedu 
912059035fStedu #ifndef GNUC_or_lint
922059035fStedu #ifdef lint
932059035fStedu #define GNUC_or_lint
942059035fStedu #else /* !defined lint */
952059035fStedu #ifdef __GNUC__
962059035fStedu #define GNUC_or_lint
972059035fStedu #endif /* defined __GNUC__ */
982059035fStedu #endif /* !defined lint */
992059035fStedu #endif /* !defined GNUC_or_lint */
1002059035fStedu 
1012059035fStedu #ifndef INITIALIZE
1022059035fStedu #ifdef GNUC_or_lint
1032059035fStedu #define INITIALIZE(x)	((x) = 0)
1042059035fStedu #else /* !defined GNUC_or_lint */
1052059035fStedu #define INITIALIZE(x)
1062059035fStedu #endif /* !defined GNUC_or_lint */
1072059035fStedu #endif /* !defined INITIALIZE */
1082059035fStedu 
1092059035fStedu /*
1102059035fStedu ** For the benefit of GNU folk...
1112059035fStedu ** `_(MSGID)' uses the current locale's message library string for MSGID.
1122059035fStedu ** The default is to use gettext if available, and use MSGID otherwise.
1132059035fStedu */
1142059035fStedu 
1152059035fStedu #ifndef _
1162059035fStedu #if HAVE_GETTEXT
1172059035fStedu #define _(msgid) gettext(msgid)
1182059035fStedu #else /* !HAVE_GETTEXT */
1192059035fStedu #define _(msgid) msgid
1202059035fStedu #endif /* !HAVE_GETTEXT */
1212059035fStedu #endif /* !defined _ */
1222059035fStedu 
1232059035fStedu #ifndef TZ_DOMAIN
1242059035fStedu #define TZ_DOMAIN "tz"
1252059035fStedu #endif /* !defined TZ_DOMAIN */
1262059035fStedu 
1272059035fStedu extern char **	environ;
1282059035fStedu extern int	getopt(int argc, char * const argv[],
1292059035fStedu 			const char * options);
1302059035fStedu extern char *	optarg;
1312059035fStedu extern int	optind;
1322059035fStedu extern char *	tzname[2];
1332059035fStedu 
1342059035fStedu static time_t	absolute_min_time;
1352059035fStedu static time_t	absolute_max_time;
1362059035fStedu static size_t	longest;
1372059035fStedu static char *	progname;
1382059035fStedu static int	warned;
1392059035fStedu 
1402059035fStedu static char *	abbr(struct tm * tmp);
1412059035fStedu static void	abbrok(const char * abbrp, const char * zone);
1422059035fStedu static long	delta(struct tm * newp, struct tm * oldp);
1432059035fStedu static void	dumptime(const struct tm * tmp);
1442059035fStedu static time_t	hunt(char * name, time_t lot, time_t	hit);
1452059035fStedu static void	setabsolutes(void);
1462059035fStedu static void	show(char * zone, time_t t, int v);
1472059035fStedu static const char *	tformat(void);
1482059035fStedu static time_t	yeartot(long y);
1492059035fStedu 
1502059035fStedu #ifndef TYPECHECK
1512059035fStedu #define my_localtime	localtime
1522059035fStedu #else /* !defined TYPECHECK */
1532059035fStedu static struct tm *
1542059035fStedu my_localtime(tp)
1552059035fStedu time_t *	tp;
1562059035fStedu {
157*66e58127Stedu 	struct tm *	tmp;
1582059035fStedu 
1592059035fStedu 	tmp = localtime(tp);
1602059035fStedu 	if (tp != NULL && tmp != NULL) {
1612059035fStedu 		struct tm	tm;
162*66e58127Stedu 		time_t	t;
1632059035fStedu 
1642059035fStedu 		tm = *tmp;
1652059035fStedu 		t = mktime(&tm);
1662059035fStedu 		if (t - *tp >= 1 || *tp - t >= 1) {
1672059035fStedu 			(void) fflush(stdout);
1682059035fStedu 			(void) fprintf(stderr, "\n%s: ", progname);
1692059035fStedu 			(void) fprintf(stderr, tformat(), *tp);
1702059035fStedu 			(void) fprintf(stderr, " ->");
1712059035fStedu 			(void) fprintf(stderr, " year=%d", tmp->tm_year);
1722059035fStedu 			(void) fprintf(stderr, " mon=%d", tmp->tm_mon);
1732059035fStedu 			(void) fprintf(stderr, " mday=%d", tmp->tm_mday);
1742059035fStedu 			(void) fprintf(stderr, " hour=%d", tmp->tm_hour);
1752059035fStedu 			(void) fprintf(stderr, " min=%d", tmp->tm_min);
1762059035fStedu 			(void) fprintf(stderr, " sec=%d", tmp->tm_sec);
1772059035fStedu 			(void) fprintf(stderr, " isdst=%d", tmp->tm_isdst);
1782059035fStedu 			(void) fprintf(stderr, " -> ");
1792059035fStedu 			(void) fprintf(stderr, tformat(), t);
1802059035fStedu 			(void) fprintf(stderr, "\n");
1812059035fStedu 		}
1822059035fStedu 	}
1832059035fStedu 	return tmp;
1842059035fStedu }
1852059035fStedu #endif /* !defined TYPECHECK */
1862059035fStedu 
1872059035fStedu static void
1882059035fStedu abbrok(abbrp, zone)
1892059035fStedu const char * const	abbrp;
1902059035fStedu const char * const	zone;
1912059035fStedu {
192*66e58127Stedu 	const char *	cp;
193*66e58127Stedu 	char *		wp;
1942059035fStedu 
1952059035fStedu 	if (warned)
1962059035fStedu 		return;
1972059035fStedu 	cp = abbrp;
1982059035fStedu 	wp = NULL;
1992059035fStedu 	while (isascii((unsigned char) *cp) && isalpha((unsigned char) *cp))
2002059035fStedu 		++cp;
2012059035fStedu 	if (cp - abbrp == 0)
2022059035fStedu 		wp = _("lacks alphabetic at start");
2032059035fStedu 	else if (cp - abbrp < 3)
2042059035fStedu 		wp = _("has fewer than 3 alphabetics");
2052059035fStedu 	else if (cp - abbrp > 6)
2062059035fStedu 		wp = _("has more than 6 alphabetics");
2072059035fStedu 	if (wp == NULL && (*cp == '+' || *cp == '-')) {
2082059035fStedu 		++cp;
2092059035fStedu 		if (isascii((unsigned char) *cp) &&
2102059035fStedu 			isdigit((unsigned char) *cp))
2112059035fStedu 				if (*cp++ == '1' && *cp >= '0' && *cp <= '4')
2122059035fStedu 					++cp;
2132059035fStedu 		if (*cp != '\0')
2142059035fStedu 			wp = _("differs from POSIX standard");
2152059035fStedu 	}
2162059035fStedu 	if (wp == NULL)
2172059035fStedu 		return;
2182059035fStedu 	(void) fflush(stdout);
2192059035fStedu 	(void) fprintf(stderr,
2202059035fStedu 		_("%s: warning: zone \"%s\" abbreviation \"%s\" %s\n"),
2212059035fStedu 		progname, zone, abbrp, wp);
2222059035fStedu 	warned = TRUE;
2232059035fStedu }
2242059035fStedu 
2252059035fStedu static void
2262059035fStedu usage(stream, status)
2272059035fStedu FILE * const	stream;
2282059035fStedu const int	status;
2292059035fStedu {
2302059035fStedu 	(void) fprintf(stream,
2312059035fStedu _("usage: %s [-v] [-c [loyear,]hiyear] zonename ...\n"), progname);
2322059035fStedu 	exit(status);
2332059035fStedu }
2342059035fStedu 
2352059035fStedu int
2362059035fStedu main(argc, argv)
2372059035fStedu int	argc;
2382059035fStedu char *	argv[];
2392059035fStedu {
240*66e58127Stedu 	int		i;
241*66e58127Stedu 	int		c;
242*66e58127Stedu 	int		vflag;
243*66e58127Stedu 	char *		cutarg;
244*66e58127Stedu 	long		cutloyear = ZDUMP_LO_YEAR;
245*66e58127Stedu 	long		cuthiyear = ZDUMP_HI_YEAR;
246*66e58127Stedu 	time_t		cutlotime;
247*66e58127Stedu 	time_t		cuthitime;
248*66e58127Stedu 	char **	fakeenv;
2492059035fStedu 	time_t			now;
2502059035fStedu 	time_t			t;
2512059035fStedu 	time_t			newt;
2522059035fStedu 	struct tm		tm;
2532059035fStedu 	struct tm		newtm;
254*66e58127Stedu 	struct tm *	tmp;
255*66e58127Stedu 	struct tm *	newtmp;
2562059035fStedu 
2572059035fStedu 	INITIALIZE(cutlotime);
2582059035fStedu 	INITIALIZE(cuthitime);
2592059035fStedu #if HAVE_GETTEXT
2602059035fStedu 	(void) setlocale(LC_ALL, "");
2612059035fStedu #ifdef TZ_DOMAINDIR
2622059035fStedu 	(void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
2632059035fStedu #endif /* defined TEXTDOMAINDIR */
2642059035fStedu 	(void) textdomain(TZ_DOMAIN);
2652059035fStedu #endif /* HAVE_GETTEXT */
2662059035fStedu 	progname = argv[0];
2672059035fStedu 	vflag = 0;
2682059035fStedu 	cutarg = NULL;
2692059035fStedu 	while ((c = getopt(argc, argv, "c:v")) == 'c' || c == 'v')
2702059035fStedu 		if (c == 'v')
2712059035fStedu 			vflag = 1;
2722059035fStedu 		else	cutarg = optarg;
2732059035fStedu 	if ((c != EOF && c != -1) ||
2742059035fStedu 		(optind == argc - 1 && strcmp(argv[optind], "=") == 0)) {
2752059035fStedu 			usage(stderr, EXIT_FAILURE);
2762059035fStedu 	}
2772059035fStedu 	if (vflag) {
2782059035fStedu 		if (cutarg != NULL) {
2792059035fStedu 			long	lo;
2802059035fStedu 			long	hi;
2812059035fStedu 			char	dummy;
2822059035fStedu 
2832059035fStedu 			if (sscanf(cutarg, "%ld%c", &hi, &dummy) == 1) {
2842059035fStedu 				cuthiyear = hi;
2852059035fStedu 			} else if (sscanf(cutarg, "%ld,%ld%c",
2862059035fStedu 				&lo, &hi, &dummy) == 2) {
2872059035fStedu 					cutloyear = lo;
2882059035fStedu 					cuthiyear = hi;
2892059035fStedu 			} else {
2902059035fStedu (void) fprintf(stderr, _("%s: wild -c argument %s\n"),
2912059035fStedu 					progname, cutarg);
2922059035fStedu 				exit(EXIT_FAILURE);
2932059035fStedu 			}
2942059035fStedu 		}
2952059035fStedu 		setabsolutes();
2962059035fStedu 		cutlotime = yeartot(cutloyear);
2972059035fStedu 		cuthitime = yeartot(cuthiyear);
2982059035fStedu 	}
2992059035fStedu 	(void) time(&now);
3002059035fStedu 	longest = 0;
3012059035fStedu 	for (i = optind; i < argc; ++i)
3022059035fStedu 		if (strlen(argv[i]) > longest)
3032059035fStedu 			longest = strlen(argv[i]);
3042059035fStedu 	{
305*66e58127Stedu 		int	from;
306*66e58127Stedu 		int	to;
3072059035fStedu 
3082059035fStedu 		for (i = 0; environ[i] != NULL; ++i)
3092059035fStedu 			continue;
3102059035fStedu 		fakeenv = (char **) malloc((size_t) ((i + 2) *
3112059035fStedu 			sizeof *fakeenv));
3122059035fStedu 		if (fakeenv == NULL ||
3132059035fStedu 			(fakeenv[0] = (char *) malloc(longest + 4)) == NULL) {
3142059035fStedu 					(void) perror(progname);
3152059035fStedu 					exit(EXIT_FAILURE);
3162059035fStedu 		}
3172059035fStedu 		to = 0;
3182059035fStedu 		strlcpy(fakeenv[to++], "TZ=", longest + 4);
3192059035fStedu 		for (from = 0; environ[from] != NULL; ++from)
3202059035fStedu 			if (strncmp(environ[from], "TZ=", 3) != 0)
3212059035fStedu 				fakeenv[to++] = environ[from];
3222059035fStedu 		fakeenv[to] = NULL;
3232059035fStedu 		environ = fakeenv;
3242059035fStedu 	}
3252059035fStedu 	for (i = optind; i < argc; ++i) {
3262059035fStedu 		static char	buf[MAX_STRING_LENGTH];
3272059035fStedu 
3282059035fStedu 		strlcpy(&fakeenv[0][3], argv[i], longest + 1);
3292059035fStedu 		if (!vflag) {
3302059035fStedu 			show(argv[i], now, FALSE);
3312059035fStedu 			continue;
3322059035fStedu 		}
3332059035fStedu 		warned = FALSE;
3342059035fStedu 		t = absolute_min_time;
3352059035fStedu 		show(argv[i], t, TRUE);
3362059035fStedu 		t += SECSPERHOUR * HOURSPERDAY;
3372059035fStedu 		show(argv[i], t, TRUE);
3382059035fStedu 		if (t < cutlotime)
3392059035fStedu 			t = cutlotime;
3402059035fStedu 		tmp = my_localtime(&t);
3412059035fStedu 		if (tmp != NULL) {
3422059035fStedu 			tm = *tmp;
3432059035fStedu 			strlcpy(buf, abbr(&tm), sizeof buf);
3442059035fStedu 		}
3452059035fStedu 		for ( ; ; ) {
3462059035fStedu 			if (t >= cuthitime || t >= cuthitime - SECSPERHOUR * 12)
3472059035fStedu 				break;
3482059035fStedu 			newt = t + SECSPERHOUR * 12;
3492059035fStedu 			newtmp = localtime(&newt);
3502059035fStedu 			if (newtmp != NULL)
3512059035fStedu 				newtm = *newtmp;
3522059035fStedu 			if ((tmp == NULL || newtmp == NULL) ? (tmp != newtmp) :
3532059035fStedu 				(delta(&newtm, &tm) != (newt - t) ||
3542059035fStedu 				newtm.tm_isdst != tm.tm_isdst ||
3552059035fStedu 				strcmp(abbr(&newtm), buf) != 0)) {
3562059035fStedu 					newt = hunt(argv[i], t, newt);
3572059035fStedu 					newtmp = localtime(&newt);
3582059035fStedu 					if (newtmp != NULL) {
3592059035fStedu 						newtm = *newtmp;
3602059035fStedu 						strlcpy(buf, abbr(&newtm),
3612059035fStedu 							sizeof buf);
3622059035fStedu 					}
3632059035fStedu 			}
3642059035fStedu 			t = newt;
3652059035fStedu 			tm = newtm;
3662059035fStedu 			tmp = newtmp;
3672059035fStedu 		}
3682059035fStedu 		t = absolute_max_time;
3692059035fStedu 		t -= SECSPERHOUR * HOURSPERDAY;
3702059035fStedu 		show(argv[i], t, TRUE);
3712059035fStedu 		t += SECSPERHOUR * HOURSPERDAY;
3722059035fStedu 		show(argv[i], t, TRUE);
3732059035fStedu 	}
3742059035fStedu 	if (fflush(stdout) || ferror(stdout)) {
3752059035fStedu 		(void) fprintf(stderr, "%s: ", progname);
3762059035fStedu 		(void) perror(_("Error writing to standard output"));
3772059035fStedu 		exit(EXIT_FAILURE);
3782059035fStedu 	}
3792059035fStedu 	exit(EXIT_SUCCESS);
3802059035fStedu 	/* If exit fails to exit... */
3812059035fStedu 	return EXIT_FAILURE;
3822059035fStedu }
3832059035fStedu 
3842059035fStedu static void
3852059035fStedu setabsolutes(void)
3862059035fStedu {
3872059035fStedu 	if (0.5 == (time_t) 0.5) {
3882059035fStedu 		/*
3892059035fStedu 		** time_t is floating.
3902059035fStedu 		*/
3912059035fStedu 		if (sizeof (time_t) == sizeof (float)) {
3922059035fStedu 			absolute_min_time = (time_t) -FLT_MAX;
3932059035fStedu 			absolute_max_time = (time_t) FLT_MAX;
3942059035fStedu 		} else if (sizeof (time_t) == sizeof (double)) {
3952059035fStedu 			absolute_min_time = (time_t) -DBL_MAX;
3962059035fStedu 			absolute_max_time = (time_t) DBL_MAX;
3972059035fStedu 		} else {
3982059035fStedu 			(void) fprintf(stderr,
3992059035fStedu _("%s: use of -v on system with floating time_t other than float or double\n"),
4002059035fStedu 				progname);
4012059035fStedu 			exit(EXIT_FAILURE);
4022059035fStedu 		}
4032059035fStedu 	} else if (0 > (time_t) -1) {
4042059035fStedu 		/*
4052059035fStedu 		** time_t is signed.  Assume overflow wraps around.
4062059035fStedu 		*/
4072059035fStedu 		time_t t = 0;
4082059035fStedu 		time_t t1 = 1;
4092059035fStedu 
4102059035fStedu 		while (t < t1) {
4112059035fStedu 			t = t1;
4122059035fStedu 			t1 = 2 * t1 + 1;
4132059035fStedu 		}
4142059035fStedu 
4152059035fStedu 		absolute_max_time = t;
4162059035fStedu 		t = -t;
4172059035fStedu 		absolute_min_time = t - 1;
4182059035fStedu 		if (t < absolute_min_time)
4192059035fStedu 			absolute_min_time = t;
4202059035fStedu 	} else {
4212059035fStedu 		/*
4222059035fStedu 		** time_t is unsigned.
4232059035fStedu 		*/
4242059035fStedu 		absolute_min_time = 0;
4252059035fStedu 		absolute_max_time = absolute_min_time - 1;
4262059035fStedu 	}
4272059035fStedu }
4282059035fStedu 
4292059035fStedu static time_t
4302059035fStedu yeartot(y)
4312059035fStedu const long	y;
4322059035fStedu {
433*66e58127Stedu 	long	myy;
434*66e58127Stedu 	long	seconds;
435*66e58127Stedu 	time_t	t;
4362059035fStedu 
4372059035fStedu 	myy = EPOCH_YEAR;
4382059035fStedu 	t = 0;
4392059035fStedu 	while (myy != y) {
4402059035fStedu 		if (myy < y) {
4412059035fStedu 			seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
4422059035fStedu 			++myy;
4432059035fStedu 			if (t > absolute_max_time - seconds) {
4442059035fStedu 				t = absolute_max_time;
4452059035fStedu 				break;
4462059035fStedu 			}
4472059035fStedu 			t += seconds;
4482059035fStedu 		} else {
4492059035fStedu 			--myy;
4502059035fStedu 			seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
4512059035fStedu 			if (t < absolute_min_time + seconds) {
4522059035fStedu 				t = absolute_min_time;
4532059035fStedu 				break;
4542059035fStedu 			}
4552059035fStedu 			t -= seconds;
4562059035fStedu 		}
4572059035fStedu 	}
4582059035fStedu 	return t;
4592059035fStedu }
4602059035fStedu 
4612059035fStedu static time_t
4622059035fStedu hunt(char *name, time_t lot, time_t hit)
4632059035fStedu {
4642059035fStedu 	time_t			t;
4652059035fStedu 	long			diff;
4662059035fStedu 	struct tm		lotm;
467*66e58127Stedu 	struct tm *	lotmp;
4682059035fStedu 	struct tm		tm;
469*66e58127Stedu 	struct tm *	tmp;
4702059035fStedu 	char			loab[MAX_STRING_LENGTH];
4712059035fStedu 
4722059035fStedu 	lotmp = my_localtime(&lot);
4732059035fStedu 	if (lotmp != NULL) {
4742059035fStedu 		lotm = *lotmp;
4752059035fStedu 		(void) strlcpy(loab, abbr(&lotm), sizeof loab);
4762059035fStedu 	}
4772059035fStedu 	for ( ; ; ) {
4782059035fStedu 		diff = (long) (hit - lot);
4792059035fStedu 		if (diff < 2)
4802059035fStedu 			break;
4812059035fStedu 		t = lot;
4822059035fStedu 		t += diff / 2;
4832059035fStedu 		if (t <= lot)
4842059035fStedu 			++t;
4852059035fStedu 		else if (t >= hit)
4862059035fStedu 			--t;
4872059035fStedu 		tmp = my_localtime(&t);
4882059035fStedu 		if (tmp != NULL)
4892059035fStedu 			tm = *tmp;
4902059035fStedu 		if ((lotmp == NULL || tmp == NULL) ? (lotmp == tmp) :
4912059035fStedu 			(delta(&tm, &lotm) == (t - lot) &&
4922059035fStedu 			tm.tm_isdst == lotm.tm_isdst &&
4932059035fStedu 			strcmp(abbr(&tm), loab) == 0)) {
4942059035fStedu 				lot = t;
4952059035fStedu 				lotm = tm;
4962059035fStedu 				lotmp = tmp;
4972059035fStedu 		} else	hit = t;
4982059035fStedu 	}
4992059035fStedu 	show(name, lot, TRUE);
5002059035fStedu 	show(name, hit, TRUE);
5012059035fStedu 	return hit;
5022059035fStedu }
5032059035fStedu 
5042059035fStedu /*
5052059035fStedu ** Thanks to Paul Eggert for logic used in delta.
5062059035fStedu */
5072059035fStedu 
5082059035fStedu static long
5092059035fStedu delta(newp, oldp)
5102059035fStedu struct tm *	newp;
5112059035fStedu struct tm *	oldp;
5122059035fStedu {
513*66e58127Stedu 	long	result;
514*66e58127Stedu 	int	tmy;
5152059035fStedu 
5162059035fStedu 	if (newp->tm_year < oldp->tm_year)
5172059035fStedu 		return -delta(oldp, newp);
5182059035fStedu 	result = 0;
5192059035fStedu 	for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy)
5202059035fStedu 		result += DAYSPERNYEAR + isleap_sum(tmy, TM_YEAR_BASE);
5212059035fStedu 	result += newp->tm_yday - oldp->tm_yday;
5222059035fStedu 	result *= HOURSPERDAY;
5232059035fStedu 	result += newp->tm_hour - oldp->tm_hour;
5242059035fStedu 	result *= MINSPERHOUR;
5252059035fStedu 	result += newp->tm_min - oldp->tm_min;
5262059035fStedu 	result *= SECSPERMIN;
5272059035fStedu 	result += newp->tm_sec - oldp->tm_sec;
5282059035fStedu 	return result;
5292059035fStedu }
5302059035fStedu 
5312059035fStedu static void
5322059035fStedu show(char *zone, time_t t, int v)
5332059035fStedu {
534*66e58127Stedu 	struct tm *	tmp;
5352059035fStedu 
5362059035fStedu 	(void) printf("%-*s  ", (int) longest, zone);
5372059035fStedu 	if (v) {
5382059035fStedu 		tmp = gmtime(&t);
5392059035fStedu 		if (tmp == NULL) {
5402059035fStedu 			(void) printf(tformat(), t);
5412059035fStedu 		} else {
5422059035fStedu 			dumptime(tmp);
5432059035fStedu 			(void) printf(" UTC");
5442059035fStedu 		}
5452059035fStedu 		(void) printf(" = ");
5462059035fStedu 	}
5472059035fStedu 	tmp = my_localtime(&t);
5482059035fStedu 	dumptime(tmp);
5492059035fStedu 	if (tmp != NULL) {
5502059035fStedu 		if (*abbr(tmp) != '\0')
5512059035fStedu 			(void) printf(" %s", abbr(tmp));
5522059035fStedu 		if (v) {
5532059035fStedu 			(void) printf(" isdst=%d", tmp->tm_isdst);
5542059035fStedu #ifdef TM_GMTOFF
5552059035fStedu 			(void) printf(" gmtoff=%ld", tmp->TM_GMTOFF);
5562059035fStedu #endif /* defined TM_GMTOFF */
5572059035fStedu 		}
5582059035fStedu 	}
5592059035fStedu 	(void) printf("\n");
5602059035fStedu 	if (tmp != NULL && *abbr(tmp) != '\0')
5612059035fStedu 		abbrok(abbr(tmp), zone);
5622059035fStedu }
5632059035fStedu 
5642059035fStedu static char *
5652059035fStedu abbr(tmp)
5662059035fStedu struct tm *	tmp;
5672059035fStedu {
568*66e58127Stedu 	char *	result;
5692059035fStedu 	static char	nada;
5702059035fStedu 
5712059035fStedu 	if (tmp->tm_isdst != 0 && tmp->tm_isdst != 1)
5722059035fStedu 		return &nada;
5732059035fStedu 	result = tzname[tmp->tm_isdst];
5742059035fStedu 	return (result == NULL) ? &nada : result;
5752059035fStedu }
5762059035fStedu 
5772059035fStedu /*
5782059035fStedu ** The code below can fail on certain theoretical systems;
5792059035fStedu ** it works on all known real-world systems as of 2004-12-30.
5802059035fStedu */
5812059035fStedu 
5822059035fStedu static const char *
5832059035fStedu tformat(void)
5842059035fStedu {
5852059035fStedu 	if (0.5 == (time_t) 0.5) {	/* floating */
5862059035fStedu 		if (sizeof (time_t) > sizeof (double))
5872059035fStedu 			return "%Lg";
5882059035fStedu 		return "%g";
5892059035fStedu 	}
5902059035fStedu 	if (0 > (time_t) -1) {		/* signed */
5912059035fStedu 		if (sizeof (time_t) > sizeof (long))
5922059035fStedu 			return "%lld";
5932059035fStedu 		if (sizeof (time_t) > sizeof (int))
5942059035fStedu 			return "%ld";
5952059035fStedu 		return "%d";
5962059035fStedu 	}
5972059035fStedu 	if (sizeof (time_t) > sizeof (unsigned long))
5982059035fStedu 		return "%llu";
5992059035fStedu 	if (sizeof (time_t) > sizeof (unsigned int))
6002059035fStedu 		return "%lu";
6012059035fStedu 	return "%u";
6022059035fStedu }
6032059035fStedu 
6042059035fStedu static void
6052059035fStedu dumptime(timeptr)
606*66e58127Stedu const struct tm *	timeptr;
6072059035fStedu {
6082059035fStedu 	static const char	wday_name[][3] = {
6092059035fStedu 		"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
6102059035fStedu 	};
6112059035fStedu 	static const char	mon_name[][3] = {
6122059035fStedu 		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
6132059035fStedu 		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
6142059035fStedu 	};
615*66e58127Stedu 	const char *	wn;
616*66e58127Stedu 	const char *	mn;
617*66e58127Stedu 	int		lead;
618*66e58127Stedu 	int		trail;
6192059035fStedu 
6202059035fStedu 	if (timeptr == NULL) {
6212059035fStedu 		(void) printf("NULL");
6222059035fStedu 		return;
6232059035fStedu 	}
6242059035fStedu 	/*
6252059035fStedu 	** The packaged versions of localtime and gmtime never put out-of-range
6262059035fStedu 	** values in tm_wday or tm_mon, but since this code might be compiled
6272059035fStedu 	** with other (perhaps experimental) versions, paranoia is in order.
6282059035fStedu 	*/
6292059035fStedu 	if (timeptr->tm_wday < 0 || timeptr->tm_wday >=
6302059035fStedu 		(int) (sizeof wday_name / sizeof wday_name[0]))
6312059035fStedu 			wn = "???";
6322059035fStedu 	else		wn = wday_name[timeptr->tm_wday];
6332059035fStedu 	if (timeptr->tm_mon < 0 || timeptr->tm_mon >=
6342059035fStedu 		(int) (sizeof mon_name / sizeof mon_name[0]))
6352059035fStedu 			mn = "???";
6362059035fStedu 	else		mn = mon_name[timeptr->tm_mon];
6372059035fStedu 	(void) printf("%.3s %.3s%3d %.2d:%.2d:%.2d ",
6382059035fStedu 		wn, mn,
6392059035fStedu 		timeptr->tm_mday, timeptr->tm_hour,
6402059035fStedu 		timeptr->tm_min, timeptr->tm_sec);
6412059035fStedu #define DIVISOR	10
6422059035fStedu 	trail = timeptr->tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR;
6432059035fStedu 	lead = timeptr->tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR +
6442059035fStedu 		trail / DIVISOR;
6452059035fStedu 	trail %= DIVISOR;
6462059035fStedu 	if (trail < 0 && lead > 0) {
6472059035fStedu 		trail += DIVISOR;
6482059035fStedu 		--lead;
6492059035fStedu 	} else if (lead < 0 && trail > 0) {
6502059035fStedu 		trail -= DIVISOR;
6512059035fStedu 		++lead;
6522059035fStedu 	}
6532059035fStedu 	if (lead == 0)
6542059035fStedu 		(void) printf("%d", trail);
6552059035fStedu 	else	(void) printf("%d%d", lead, ((trail < 0) ? -trail : trail));
6562059035fStedu }
657