1*fcefcf04Smillert /* $OpenBSD: zdump.c,v 1.14 2016/03/15 19:50:48 millert 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
13f0dc9fbaSderaadt #include <ctype.h>
14f0dc9fbaSderaadt #include <stdio.h>
15f0dc9fbaSderaadt #include <string.h>
16f0dc9fbaSderaadt #include <stdlib.h>
17f0dc9fbaSderaadt #include <unistd.h>
18f0dc9fbaSderaadt #include <time.h>
192059035fStedu
202059035fStedu #define ZDUMP_LO_YEAR (-500)
212059035fStedu #define ZDUMP_HI_YEAR 2500
222059035fStedu
232059035fStedu #define MAX_STRING_LENGTH 1024
242059035fStedu
252059035fStedu #define TRUE 1
262059035fStedu #define FALSE 0
272059035fStedu
282059035fStedu #define SECSPERMIN 60
292059035fStedu #define MINSPERHOUR 60
302059035fStedu #define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
312059035fStedu #define HOURSPERDAY 24
322059035fStedu #define EPOCH_YEAR 1970
332059035fStedu #define TM_YEAR_BASE 1900
342059035fStedu #define DAYSPERNYEAR 365
352059035fStedu
36f0dc9fbaSderaadt #define SECSPERDAY ((long) SECSPERHOUR * HOURSPERDAY)
37f0dc9fbaSderaadt #define SECSPERNYEAR (SECSPERDAY * DAYSPERNYEAR)
38f0dc9fbaSderaadt #define SECSPERLYEAR (SECSPERNYEAR + SECSPERDAY)
39f0dc9fbaSderaadt
402059035fStedu #define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
412059035fStedu
422059035fStedu #ifndef isleap_sum
432059035fStedu /*
442059035fStedu ** See tzfile.h for details on isleap_sum.
452059035fStedu */
462059035fStedu #define isleap_sum(a, b) isleap((a) % 400 + (b) % 400)
472059035fStedu #endif /* !defined isleap_sum */
482059035fStedu
492059035fStedu extern char **environ;
502059035fStedu extern char *tzname[2];
51f0dc9fbaSderaadt extern char *__progname;
522059035fStedu
53f0dc9fbaSderaadt time_t absolute_min_time;
54f0dc9fbaSderaadt time_t absolute_max_time;
55f0dc9fbaSderaadt size_t longest;
56f0dc9fbaSderaadt int warned;
572059035fStedu
582059035fStedu static char *abbr(struct tm *tmp);
592059035fStedu static void abbrok(const char *abbrp, const char *zone);
602059035fStedu static long delta(struct tm *newp, struct tm *oldp);
612059035fStedu static void dumptime(const struct tm *tmp);
622059035fStedu static time_t hunt(char *name, time_t lot, time_t hit);
632059035fStedu static void setabsolutes(void);
642059035fStedu static void show(char *zone, time_t t, int v);
652059035fStedu static const char *tformat(void);
662059035fStedu static time_t yeartot(long y);
67f0dc9fbaSderaadt static void usage(void);
682059035fStedu
692059035fStedu static void
abbrok(const char * const abbrp,const char * const zone)70f0dc9fbaSderaadt abbrok(const char * const abbrp, const char * const zone)
712059035fStedu {
7266e58127Stedu const char *cp;
7366e58127Stedu char *wp;
742059035fStedu
752059035fStedu if (warned)
762059035fStedu return;
772059035fStedu cp = abbrp;
782059035fStedu wp = NULL;
79*fcefcf04Smillert while (isascii((unsigned char)*cp) &&
80*fcefcf04Smillert (isalnum((unsigned char)*cp) || *cp == '-' || *cp == '+'))
812059035fStedu ++cp;
82*fcefcf04Smillert if (cp - abbrp < 3)
83*fcefcf04Smillert wp = "has fewer than 3 characters";
842059035fStedu else if (cp - abbrp > 6)
85*fcefcf04Smillert wp = "has more than 6 characters";
86*fcefcf04Smillert else if (*cp)
87*fcefcf04Smillert wp = "has characters other than ASCII alphanumerics, '-' or '+'";
88*fcefcf04Smillert else
892059035fStedu return;
90e3b8c046Stedu fflush(stdout);
91f0dc9fbaSderaadt fprintf(stderr, "%s: warning: zone \"%s\" abbreviation \"%s\" %s\n",
92f0dc9fbaSderaadt __progname, zone, abbrp, wp);
932059035fStedu warned = TRUE;
942059035fStedu }
952059035fStedu
962059035fStedu static void
usage(void)97b3cc38c2Stedu usage(void)
982059035fStedu {
99f0dc9fbaSderaadt fprintf(stderr, "usage: %s [-v] [-c [loyear,]hiyear] zonename ...\n",
100f0dc9fbaSderaadt __progname);
101b3cc38c2Stedu exit(EXIT_FAILURE);
1022059035fStedu }
1032059035fStedu
1042059035fStedu int
main(int argc,char * argv[])105f0dc9fbaSderaadt main(int argc, char *argv[])
1062059035fStedu {
107f0dc9fbaSderaadt int i, c, vflag = 0;
108f0dc9fbaSderaadt char *cutarg = NULL;
10966e58127Stedu long cutloyear = ZDUMP_LO_YEAR;
11066e58127Stedu long cuthiyear = ZDUMP_HI_YEAR;
111f0dc9fbaSderaadt time_t cutlotime = 0, cuthitime = 0;
112f0dc9fbaSderaadt time_t now, t, newt;
113f0dc9fbaSderaadt struct tm tm, newtm, *tmp, *newtmp;
11466e58127Stedu char **fakeenv;
1152059035fStedu
116244b8f99Smestre if (pledge("stdio rpath", NULL) == -1) {
117efe28ca9Sderaadt perror("pledge");
1189b32031cSderaadt exit(1);
1199b32031cSderaadt }
120efe28ca9Sderaadt
121f0dc9fbaSderaadt while ((c = getopt(argc, argv, "c:v")) == 'c' || c == 'v') {
122f0dc9fbaSderaadt switch (c) {
123f0dc9fbaSderaadt case 'v':
1242059035fStedu vflag = 1;
125f0dc9fbaSderaadt break;
126f0dc9fbaSderaadt case 'c':
127f0dc9fbaSderaadt cutarg = optarg;
128f0dc9fbaSderaadt break;
129f0dc9fbaSderaadt default:
130f0dc9fbaSderaadt usage();
131f0dc9fbaSderaadt break;
132f0dc9fbaSderaadt }
133f0dc9fbaSderaadt }
134f0dc9fbaSderaadt if (c != -1 ||
1352059035fStedu (optind == argc - 1 && strcmp(argv[optind], "=") == 0)) {
136b3cc38c2Stedu usage();
1372059035fStedu }
1382059035fStedu if (vflag) {
1392059035fStedu if (cutarg != NULL) {
140f0dc9fbaSderaadt long lo, hi;
1412059035fStedu char dummy;
1422059035fStedu
1432059035fStedu if (sscanf(cutarg, "%ld%c", &hi, &dummy) == 1) {
1442059035fStedu cuthiyear = hi;
1452059035fStedu } else if (sscanf(cutarg, "%ld,%ld%c",
1462059035fStedu &lo, &hi, &dummy) == 2) {
1472059035fStedu cutloyear = lo;
1482059035fStedu cuthiyear = hi;
1492059035fStedu } else {
150f0dc9fbaSderaadt fprintf(stderr, "%s: wild -c argument %s\n",
151f0dc9fbaSderaadt __progname, cutarg);
1522059035fStedu exit(EXIT_FAILURE);
1532059035fStedu }
1542059035fStedu }
1552059035fStedu setabsolutes();
1562059035fStedu cutlotime = yeartot(cutloyear);
1572059035fStedu cuthitime = yeartot(cuthiyear);
1582059035fStedu }
159e3b8c046Stedu time(&now);
1602059035fStedu longest = 0;
1612059035fStedu for (i = optind; i < argc; ++i)
1622059035fStedu if (strlen(argv[i]) > longest)
1632059035fStedu longest = strlen(argv[i]);
164f0dc9fbaSderaadt
1652059035fStedu {
166f0dc9fbaSderaadt int from, to;
1672059035fStedu
1682059035fStedu for (i = 0; environ[i] != NULL; ++i)
1692059035fStedu continue;
170753cfe0cSderaadt fakeenv = reallocarray(NULL, i + 2, sizeof *fakeenv);
1712059035fStedu if (fakeenv == NULL ||
172e3b8c046Stedu (fakeenv[0] = malloc(longest + 4)) == NULL) {
173f0dc9fbaSderaadt perror(__progname);
1742059035fStedu exit(EXIT_FAILURE);
1752059035fStedu }
1762059035fStedu to = 0;
1772059035fStedu strlcpy(fakeenv[to++], "TZ=", longest + 4);
1782059035fStedu for (from = 0; environ[from] != NULL; ++from)
1792059035fStedu if (strncmp(environ[from], "TZ=", 3) != 0)
1802059035fStedu fakeenv[to++] = environ[from];
1812059035fStedu fakeenv[to] = NULL;
1822059035fStedu environ = fakeenv;
1832059035fStedu }
1842059035fStedu for (i = optind; i < argc; ++i) {
185f0dc9fbaSderaadt char buf[MAX_STRING_LENGTH];
1862059035fStedu
1872059035fStedu strlcpy(&fakeenv[0][3], argv[i], longest + 1);
1882059035fStedu if (!vflag) {
1892059035fStedu show(argv[i], now, FALSE);
1902059035fStedu continue;
1912059035fStedu }
1922059035fStedu warned = FALSE;
1932059035fStedu t = absolute_min_time;
1942059035fStedu show(argv[i], t, TRUE);
1952059035fStedu t += SECSPERHOUR * HOURSPERDAY;
1962059035fStedu show(argv[i], t, TRUE);
1972059035fStedu if (t < cutlotime)
1982059035fStedu t = cutlotime;
199f0dc9fbaSderaadt tmp = localtime(&t);
2002059035fStedu if (tmp != NULL) {
2012059035fStedu tm = *tmp;
2022059035fStedu strlcpy(buf, abbr(&tm), sizeof buf);
2032059035fStedu }
2042059035fStedu for ( ; ; ) {
2052059035fStedu if (t >= cuthitime || t >= cuthitime - SECSPERHOUR * 12)
2062059035fStedu break;
2072059035fStedu newt = t + SECSPERHOUR * 12;
2082059035fStedu newtmp = localtime(&newt);
2092059035fStedu if (newtmp != NULL)
2102059035fStedu newtm = *newtmp;
2112059035fStedu if ((tmp == NULL || newtmp == NULL) ? (tmp != newtmp) :
2122059035fStedu (delta(&newtm, &tm) != (newt - t) ||
2132059035fStedu newtm.tm_isdst != tm.tm_isdst ||
2142059035fStedu strcmp(abbr(&newtm), buf) != 0)) {
2152059035fStedu newt = hunt(argv[i], t, newt);
2162059035fStedu newtmp = localtime(&newt);
2172059035fStedu if (newtmp != NULL) {
2182059035fStedu newtm = *newtmp;
219f0dc9fbaSderaadt strlcpy(buf, abbr(&newtm), sizeof buf);
2202059035fStedu }
2212059035fStedu }
2222059035fStedu t = newt;
2232059035fStedu tm = newtm;
2242059035fStedu tmp = newtmp;
2252059035fStedu }
2262059035fStedu t = absolute_max_time;
2272059035fStedu t -= SECSPERHOUR * HOURSPERDAY;
2282059035fStedu show(argv[i], t, TRUE);
2292059035fStedu t += SECSPERHOUR * HOURSPERDAY;
2302059035fStedu show(argv[i], t, TRUE);
2312059035fStedu }
2322059035fStedu if (fflush(stdout) || ferror(stdout)) {
233f0dc9fbaSderaadt fprintf(stderr, "%s: ", __progname);
234f0dc9fbaSderaadt perror("Error writing to standard output");
2352059035fStedu exit(EXIT_FAILURE);
2362059035fStedu }
237b3cc38c2Stedu return 0;
2382059035fStedu }
2392059035fStedu
2402059035fStedu static void
setabsolutes(void)2412059035fStedu setabsolutes(void)
2422059035fStedu {
243f0dc9fbaSderaadt time_t t = 0, t1 = 1;
2442059035fStedu
2452059035fStedu while (t < t1) {
2462059035fStedu t = t1;
2472059035fStedu t1 = 2 * t1 + 1;
2482059035fStedu }
2492059035fStedu
2502059035fStedu absolute_max_time = t;
2512059035fStedu t = -t;
2522059035fStedu absolute_min_time = t - 1;
2532059035fStedu if (t < absolute_min_time)
2542059035fStedu absolute_min_time = t;
2552059035fStedu }
2562059035fStedu
2572059035fStedu static time_t
yeartot(const long y)258f0dc9fbaSderaadt yeartot(const long y)
2592059035fStedu {
260f0dc9fbaSderaadt long myy = EPOCH_YEAR, seconds;
261f0dc9fbaSderaadt time_t t = 0;
2622059035fStedu
2632059035fStedu while (myy != y) {
2642059035fStedu if (myy < y) {
2652059035fStedu seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
2662059035fStedu ++myy;
2672059035fStedu if (t > absolute_max_time - seconds) {
2682059035fStedu t = absolute_max_time;
2692059035fStedu break;
2702059035fStedu }
2712059035fStedu t += seconds;
2722059035fStedu } else {
2732059035fStedu --myy;
2742059035fStedu seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
2752059035fStedu if (t < absolute_min_time + seconds) {
2762059035fStedu t = absolute_min_time;
2772059035fStedu break;
2782059035fStedu }
2792059035fStedu t -= seconds;
2802059035fStedu }
2812059035fStedu }
2822059035fStedu return t;
2832059035fStedu }
2842059035fStedu
2852059035fStedu static time_t
hunt(char * name,time_t lot,time_t hit)2862059035fStedu hunt(char *name, time_t lot, time_t hit)
2872059035fStedu {
2882059035fStedu time_t t;
2892059035fStedu long diff;
290f0dc9fbaSderaadt struct tm lotm, *lotmp;
291f0dc9fbaSderaadt struct tm tm, *tmp;
2922059035fStedu char loab[MAX_STRING_LENGTH];
2932059035fStedu
294f0dc9fbaSderaadt lotmp = localtime(&lot);
2952059035fStedu if (lotmp != NULL) {
2962059035fStedu lotm = *lotmp;
297e3b8c046Stedu strlcpy(loab, abbr(&lotm), sizeof loab);
2982059035fStedu }
2992059035fStedu for ( ; ; ) {
3002059035fStedu diff = (long) (hit - lot);
3012059035fStedu if (diff < 2)
3022059035fStedu break;
3032059035fStedu t = lot;
3042059035fStedu t += diff / 2;
3052059035fStedu if (t <= lot)
3062059035fStedu ++t;
3072059035fStedu else if (t >= hit)
3082059035fStedu --t;
309f0dc9fbaSderaadt tmp = localtime(&t);
3102059035fStedu if (tmp != NULL)
3112059035fStedu tm = *tmp;
3122059035fStedu if ((lotmp == NULL || tmp == NULL) ? (lotmp == tmp) :
3132059035fStedu (delta(&tm, &lotm) == (t - lot) &&
3142059035fStedu tm.tm_isdst == lotm.tm_isdst &&
3152059035fStedu strcmp(abbr(&tm), loab) == 0)) {
3162059035fStedu lot = t;
3172059035fStedu lotm = tm;
3182059035fStedu lotmp = tmp;
319f0dc9fbaSderaadt } else
320f0dc9fbaSderaadt hit = t;
3212059035fStedu }
3222059035fStedu show(name, lot, TRUE);
3232059035fStedu show(name, hit, TRUE);
3242059035fStedu return hit;
3252059035fStedu }
3262059035fStedu
3272059035fStedu /*
3282059035fStedu ** Thanks to Paul Eggert for logic used in delta.
3292059035fStedu */
3302059035fStedu
3312059035fStedu static long
delta(struct tm * newp,struct tm * oldp)332f0dc9fbaSderaadt delta(struct tm *newp, struct tm *oldp)
3332059035fStedu {
33466e58127Stedu long result;
33566e58127Stedu int tmy;
3362059035fStedu
3372059035fStedu if (newp->tm_year < oldp->tm_year)
3382059035fStedu return -delta(oldp, newp);
3392059035fStedu result = 0;
3402059035fStedu for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy)
3412059035fStedu result += DAYSPERNYEAR + isleap_sum(tmy, TM_YEAR_BASE);
3422059035fStedu result += newp->tm_yday - oldp->tm_yday;
3432059035fStedu result *= HOURSPERDAY;
3442059035fStedu result += newp->tm_hour - oldp->tm_hour;
3452059035fStedu result *= MINSPERHOUR;
3462059035fStedu result += newp->tm_min - oldp->tm_min;
3472059035fStedu result *= SECSPERMIN;
3482059035fStedu result += newp->tm_sec - oldp->tm_sec;
3492059035fStedu return result;
3502059035fStedu }
3512059035fStedu
3522059035fStedu static void
show(char * zone,time_t t,int v)3532059035fStedu show(char *zone, time_t t, int v)
3542059035fStedu {
35566e58127Stedu struct tm *tmp;
3562059035fStedu
357e3b8c046Stedu printf("%-*s ", (int) longest, zone);
3582059035fStedu if (v) {
3592059035fStedu tmp = gmtime(&t);
3602059035fStedu if (tmp == NULL) {
361e3b8c046Stedu printf(tformat(), t);
3622059035fStedu } else {
3632059035fStedu dumptime(tmp);
364e3b8c046Stedu printf(" UTC");
3652059035fStedu }
366e3b8c046Stedu printf(" = ");
3672059035fStedu }
368f0dc9fbaSderaadt tmp = localtime(&t);
3692059035fStedu dumptime(tmp);
3702059035fStedu if (tmp != NULL) {
3712059035fStedu if (*abbr(tmp) != '\0')
372e3b8c046Stedu printf(" %s", abbr(tmp));
3732059035fStedu if (v) {
374e3b8c046Stedu printf(" isdst=%d", tmp->tm_isdst);
3752059035fStedu #ifdef TM_GMTOFF
376e3b8c046Stedu printf(" gmtoff=%ld", tmp->TM_GMTOFF);
3772059035fStedu #endif /* defined TM_GMTOFF */
3782059035fStedu }
3792059035fStedu }
380e3b8c046Stedu printf("\n");
3812059035fStedu if (tmp != NULL && *abbr(tmp) != '\0')
3822059035fStedu abbrok(abbr(tmp), zone);
3832059035fStedu }
3842059035fStedu
3852059035fStedu static char *
abbr(struct tm * tmp)386f0dc9fbaSderaadt abbr(struct tm *tmp)
3872059035fStedu {
38866e58127Stedu char *result;
3892059035fStedu static char nada;
3902059035fStedu
3912059035fStedu if (tmp->tm_isdst != 0 && tmp->tm_isdst != 1)
3922059035fStedu return &nada;
3932059035fStedu result = tzname[tmp->tm_isdst];
3942059035fStedu return (result == NULL) ? &nada : result;
3952059035fStedu }
3962059035fStedu
3972059035fStedu /*
3982059035fStedu ** The code below can fail on certain theoretical systems;
3992059035fStedu ** it works on all known real-world systems as of 2004-12-30.
4002059035fStedu */
4012059035fStedu
4022059035fStedu static const char *
tformat(void)4032059035fStedu tformat(void)
4042059035fStedu {
4052059035fStedu return "%lld";
4062059035fStedu }
4072059035fStedu
4082059035fStedu static void
dumptime(const struct tm * timeptr)409f0dc9fbaSderaadt dumptime(const struct tm *timeptr)
4102059035fStedu {
4112059035fStedu static const char wday_name[][3] = {
4122059035fStedu "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
4132059035fStedu };
4142059035fStedu static const char mon_name[][3] = {
4152059035fStedu "Jan", "Feb", "Mar", "Apr", "May", "Jun",
4162059035fStedu "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
4172059035fStedu };
418f0dc9fbaSderaadt const char *wn, *mn;
419f0dc9fbaSderaadt int lead, trail;
4202059035fStedu
4212059035fStedu if (timeptr == NULL) {
422e3b8c046Stedu printf("NULL");
4232059035fStedu return;
4242059035fStedu }
4252059035fStedu /*
4262059035fStedu ** The packaged versions of localtime and gmtime never put out-of-range
4272059035fStedu ** values in tm_wday or tm_mon, but since this code might be compiled
4282059035fStedu ** with other (perhaps experimental) versions, paranoia is in order.
4292059035fStedu */
4302059035fStedu if (timeptr->tm_wday < 0 || timeptr->tm_wday >=
4312059035fStedu (int) (sizeof wday_name / sizeof wday_name[0]))
4322059035fStedu wn = "???";
433f0dc9fbaSderaadt else
434f0dc9fbaSderaadt wn = wday_name[timeptr->tm_wday];
4352059035fStedu if (timeptr->tm_mon < 0 || timeptr->tm_mon >=
4362059035fStedu (int) (sizeof mon_name / sizeof mon_name[0]))
4372059035fStedu mn = "???";
438f0dc9fbaSderaadt else
439f0dc9fbaSderaadt mn = mon_name[timeptr->tm_mon];
440e3b8c046Stedu printf("%.3s %.3s%3d %.2d:%.2d:%.2d ",
4412059035fStedu wn, mn,
4422059035fStedu timeptr->tm_mday, timeptr->tm_hour,
4432059035fStedu timeptr->tm_min, timeptr->tm_sec);
4442059035fStedu #define DIVISOR 10
4452059035fStedu trail = timeptr->tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR;
4462059035fStedu lead = timeptr->tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR +
4472059035fStedu trail / DIVISOR;
4482059035fStedu trail %= DIVISOR;
4492059035fStedu if (trail < 0 && lead > 0) {
4502059035fStedu trail += DIVISOR;
4512059035fStedu --lead;
4522059035fStedu } else if (lead < 0 && trail > 0) {
4532059035fStedu trail -= DIVISOR;
4542059035fStedu ++lead;
4552059035fStedu }
4562059035fStedu if (lead == 0)
457e3b8c046Stedu printf("%d", trail);
458e3b8c046Stedu else
459e3b8c046Stedu printf("%d%d", lead, ((trail < 0) ? -trail : trail));
4602059035fStedu }
461