2 * $FreeBSD: src/usr.sbin/zic/zdump.c,v 1.7 1999/08/28 01:21:19 peter Exp $
5 ** This code has been made independent of the rest of the time
6 ** conversion package to increase confidence in the verification it provides.
7 ** You can use this code to help in verifying other implementations.
10 #include <ctype.h> /* for isalpha et al. */
13 #include <limits.h> /* for CHAR_BIT, LLONG_MAX */
15 #include <stdio.h> /* for stdout, stderr */
16 #include <stdlib.h> /* for exit, malloc, atoi */
17 #include <string.h> /* for strcpy */
18 #include <sys/types.h> /* for time_t */
19 #include <time.h> /* for struct tm */
23 #define ZDUMP_LO_YEAR (-500)
24 #endif /* !defined ZDUMP_LO_YEAR */
27 #define ZDUMP_HI_YEAR 2500
28 #endif /* !defined ZDUMP_HI_YEAR */
30 #ifndef MAX_STRING_LENGTH
31 #define MAX_STRING_LENGTH 1024
32 #endif /* !defined MAX_STRING_LENGTH */
36 #endif /* !defined TRUE */
40 #endif /* !defined FALSE */
43 #define EXIT_SUCCESS 0
44 #endif /* !defined EXIT_SUCCESS */
47 #define EXIT_FAILURE 1
48 #endif /* !defined EXIT_FAILURE */
52 #endif /* !defined SECSPERMIN */
55 #define MINSPERHOUR 60
56 #endif /* !defined MINSPERHOUR */
59 #define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
60 #endif /* !defined SECSPERHOUR */
63 #define HOURSPERDAY 24
64 #endif /* !defined HOURSPERDAY */
67 #define EPOCH_YEAR 1970
68 #endif /* !defined EPOCH_YEAR */
71 #define TM_YEAR_BASE 1900
72 #endif /* !defined TM_YEAR_BASE */
75 #define DAYSPERNYEAR 365
76 #endif /* !defined DAYSPERNYEAR */
79 #define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
80 #endif /* !defined isleap */
84 ** See tzfile.h for details on isleap_sum.
86 #define isleap_sum(a, b) isleap((a) % 400 + (b) % 400)
87 #endif /* !defined isleap_sum */
89 #define SECSPERDAY ((int_fast32_t) SECSPERHOUR * HOURSPERDAY)
90 #define SECSPERNYEAR (SECSPERDAY * DAYSPERNYEAR)
91 #define SECSPERLYEAR (SECSPERNYEAR + SECSPERDAY)
92 #define SECSPER400YEARS (SECSPERNYEAR * (intmax_t) (300 + 3) \
93 + SECSPERLYEAR * (intmax_t) (100 - 3))
96 ** True if SECSPER400YEARS is known to be representable as an
97 ** intmax_t. It's OK that SECSPER400YEARS_FITS can in theory be false
98 ** even if SECSPER400YEARS is representable, because when that happens
99 ** the code merely runs a bit more slowly, and this slowness doesn't
100 ** occur on any practical platform.
102 enum { SECSPER400YEARS_FITS
= SECSPERLYEAR
<= INTMAX_MAX
/ 400 };
105 ** For the benefit of GNU folk...
106 ** `_(MSGID)' uses the current locale's message library string for MSGID.
107 ** The default is to use gettext if available, and use MSGID otherwise.
111 #define _(msgid) msgid
112 #endif /* !defined _ */
115 #define TZ_DOMAIN "tz"
116 #endif /* !defined TZ_DOMAIN */
118 extern char ** environ
;
119 extern char * tzname
[2];
121 /* The minimum and maximum finite time values. */
122 static time_t const absolute_min_time
=
124 ? (time_t) -1 << (CHAR_BIT
* sizeof (time_t) - 1)
126 static time_t const absolute_max_time
=
128 ? - (~ 0 < 0) - ((time_t) -1 << (CHAR_BIT
* sizeof (time_t) - 1))
130 static size_t longest
;
133 static char *abbr(struct tm
*tmp
);
134 static void abbrok(const char *abbrp
, const char *zone
);
135 static intmax_t delta(struct tm
* newp
, struct tm
* oldp
) __pure
;
136 static void dumptime(const struct tm
*tmp
);
137 static time_t hunt(char *name
, time_t lot
, time_t hit
);
138 static void show(char *zone
, time_t t
, int v
);
139 static const char *tformat(void);
140 static time_t yeartot(intmax_t y
) __pure
;
141 static void usage(void);
144 #define my_localtime localtime
145 #else /* !defined TYPECHECK */
147 my_localtime(time_t *tp
)
152 if (tp
!= NULL
&& tmp
!= NULL
) {
160 fprintf(stderr
, "\n%s: ", progname
);
161 fprintf(stderr
, tformat(), *tp
);
162 fprintf(stderr
, " ->");
163 fprintf(stderr
, " year=%d", tmp
->tm_year
);
164 fprintf(stderr
, " mon=%d", tmp
->tm_mon
);
165 fprintf(stderr
, " mday=%d", tmp
->tm_mday
);
166 fprintf(stderr
, " hour=%d", tmp
->tm_hour
);
167 fprintf(stderr
, " min=%d", tmp
->tm_min
);
168 fprintf(stderr
, " sec=%d", tmp
->tm_sec
);
169 fprintf(stderr
, " isdst=%d", tmp
->tm_isdst
);
170 fprintf(stderr
, " -> ");
171 fprintf(stderr
, tformat(), t
);
172 fprintf(stderr
, "\n");
177 #endif /* !defined TYPECHECK */
180 abbrok(const char *const abbrp
, const char *const zone
)
188 while (isalpha(*cp
) || isdigit(*cp
) || *cp
== '-' || *cp
== '+')
191 wp
= _("has fewer than 3 characters");
192 else if (cp
- abbrp
> 6)
193 wp
= _("has more than 6 characters");
195 wp
= _("has characters other than ASCII alphanumerics, '-' or '+'");
199 warnx(_("warning: zone \"%s\" abbreviation \"%s\" %s\n"),
205 main(int argc
, char *argv
[])
223 cutlotime
= absolute_min_time
;
224 cuthitime
= absolute_max_time
;
226 cutarg
= cuttimes
= NULL
;
228 switch (getopt(argc
, argv
, "c:t:vV")) {
229 case 'c': cutarg
= optarg
; break;
230 case 't': cuttimes
= optarg
; break;
231 case 'v': vflag
= 1; break;
232 case 'V': Vflag
= 1; break;
234 if (! (optind
== argc
- 1 && strcmp(argv
[optind
], "=") == 0))
235 goto arg_processing_done
;
240 arg_processing_done
:;
246 intmax_t cutloyear
= ZDUMP_LO_YEAR
;
247 intmax_t cuthiyear
= ZDUMP_HI_YEAR
;
248 if (cutarg
!= NULL
) {
249 lo
= strtoimax(cutarg
, &loend
, 10);
250 if (cutarg
!= loend
&& !*loend
) {
253 } else if (cutarg
!= loend
&& *loend
== ','
254 && (hi
= strtoimax(loend
+ 1, &hiend
, 10),
255 loend
+ 1 != hiend
&& !*hiend
)) {
260 _("wild -c argument %s\n"),
264 if (cutarg
!= NULL
|| cuttimes
== NULL
) {
265 cutlotime
= yeartot(cutloyear
);
266 cuthitime
= yeartot(cuthiyear
);
268 if (cuttimes
!= NULL
) {
269 lo
= strtoimax(cuttimes
, &loend
, 10);
270 if (cuttimes
!= loend
&& !*loend
) {
272 if (hi
< cuthitime
) {
273 if (hi
< absolute_min_time
)
274 hi
= absolute_min_time
;
277 } else if (cuttimes
!= loend
&& *loend
== ','
278 && (hi
= strtoimax(loend
+ 1, &hiend
, 10),
279 loend
+ 1 != hiend
&& !*hiend
)) {
280 if (cutlotime
< lo
) {
281 if (absolute_max_time
< lo
)
282 lo
= absolute_max_time
;
285 if (hi
< cuthitime
) {
286 if (hi
< absolute_min_time
)
287 hi
= absolute_min_time
;
292 _("wild -t argument %s\n"),
299 for (i
= optind
; i
< argc
; ++i
)
300 if (strlen(argv
[i
]) > longest
)
301 longest
= strlen(argv
[i
]);
306 for (i
= 0; environ
[i
] != NULL
; ++i
)
308 fakeenv
= malloc((i
+ 2) * sizeof *fakeenv
);
310 || (fakeenv
[0] = malloc(longest
+ 4)) == NULL
) {
312 _("malloc() failed"));
315 strcpy(fakeenv
[to
++], "TZ=");
316 for (from
= 0; environ
[from
] != NULL
; ++from
)
317 if (strncmp(environ
[from
], "TZ=", 3) != 0)
318 fakeenv
[to
++] = environ
[from
];
322 for (i
= optind
; i
< argc
; ++i
) {
323 static char buf
[MAX_STRING_LENGTH
];
325 strcpy(&fakeenv
[0][3], argv
[i
]);
326 if (! (vflag
| Vflag
)) {
327 show(argv
[i
], now
, FALSE
);
331 t
= absolute_min_time
;
333 show(argv
[i
], t
, TRUE
);
335 show(argv
[i
], t
, TRUE
);
339 tmp
= my_localtime(&t
);
342 strncpy(buf
, abbr(&tm
), (sizeof buf
) - 1);
345 newt
= (t
< absolute_max_time
- SECSPERDAY
/ 2
347 : absolute_max_time
);
348 if (cuthitime
<= newt
)
350 newtmp
= localtime(&newt
);
353 if ((tmp
== NULL
|| newtmp
== NULL
) ? (tmp
!= newtmp
) :
354 (delta(&newtm
, &tm
) != (newt
- t
) ||
355 newtm
.tm_isdst
!= tm
.tm_isdst
||
356 strcmp(abbr(&newtm
), buf
) != 0)) {
357 newt
= hunt(argv
[i
], t
, newt
);
358 newtmp
= localtime(&newt
);
359 if (newtmp
!= NULL
) {
371 t
= absolute_max_time
;
373 show(argv
[i
], t
, TRUE
);
375 show(argv
[i
], t
, TRUE
);
378 if (fflush(stdout
) || ferror(stdout
))
379 errx(EXIT_FAILURE
, _("error writing standard output"));
381 /* If exit fails to exit... */
388 fprintf(stderr
, _("usage: zdump [-vV] [-c [loyear,]hiyear] [-t [lotime,]hitime] zonename ...\n"));
393 yeartot(const intmax_t y
)
395 intmax_t myy
, seconds
, years
;
401 if (SECSPER400YEARS_FITS
&& 400 <= y
- myy
) {
402 intmax_t diff400
= (y
- myy
) / 400;
403 if (INTMAX_MAX
/ SECSPER400YEARS
< diff400
)
404 return absolute_max_time
;
405 seconds
= diff400
* SECSPER400YEARS
;
406 years
= diff400
* 400;
408 seconds
= isleap(myy
) ? SECSPERLYEAR
: SECSPERNYEAR
;
412 if (t
> absolute_max_time
- seconds
)
413 return absolute_max_time
;
417 if (SECSPER400YEARS_FITS
&& y
+ 400 <= myy
&& myy
< 0) {
418 intmax_t diff400
= (myy
- y
) / 400;
419 if (INTMAX_MAX
/ SECSPER400YEARS
< diff400
)
420 return absolute_min_time
;
421 seconds
= diff400
* SECSPER400YEARS
;
422 years
= diff400
* 400;
424 seconds
= isleap(myy
- 1) ? SECSPERLYEAR
: SECSPERNYEAR
;
428 if (t
< absolute_min_time
+ seconds
)
429 return absolute_min_time
;
436 hunt(char *name
, time_t lot
, time_t hit
)
443 char loab
[MAX_STRING_LENGTH
];
445 lotmp
= my_localtime(&lot
);
448 strncpy(loab
, abbr(&lotm
), (sizeof loab
) - 1);
451 time_t diff
= hit
- lot
;
460 tmp
= my_localtime(&t
);
463 if ((lotmp
== NULL
|| tmp
== NULL
) ? (lotmp
== tmp
) :
464 (delta(&tm
, &lotm
) == (t
- lot
) &&
465 tm
.tm_isdst
== lotm
.tm_isdst
&&
466 strcmp(abbr(&tm
), loab
) == 0)) {
472 show(name
, lot
, TRUE
);
473 show(name
, hit
, TRUE
);
478 ** Thanks to Paul Eggert for logic used in delta.
482 delta(struct tm
*newp
, struct tm
*oldp
)
487 if (newp
->tm_year
< oldp
->tm_year
)
488 return -delta(oldp
, newp
);
490 for (tmy
= oldp
->tm_year
; tmy
< newp
->tm_year
; ++tmy
)
491 result
+= DAYSPERNYEAR
+ isleap_sum(tmy
, TM_YEAR_BASE
);
492 result
+= newp
->tm_yday
- oldp
->tm_yday
;
493 result
*= HOURSPERDAY
;
494 result
+= newp
->tm_hour
- oldp
->tm_hour
;
495 result
*= MINSPERHOUR
;
496 result
+= newp
->tm_min
- oldp
->tm_min
;
497 result
*= SECSPERMIN
;
498 result
+= newp
->tm_sec
- oldp
->tm_sec
;
503 show(char *zone
, time_t t
, int v
)
507 printf("%-*s ", (int) longest
, zone
);
511 printf(tformat(), t
);
518 tmp
= my_localtime(&t
);
521 if (*abbr(tmp
) != '\0')
522 printf(" %s", abbr(tmp
));
524 printf(" isdst=%d", tmp
->tm_isdst
);
525 printf(" gmtoff=%ld", tmp
->TM_GMTOFF
);
529 if (tmp
!= NULL
&& *abbr(tmp
) != '\0')
530 abbrok(abbr(tmp
), zone
);
539 if (tmp
->tm_isdst
!= 0 && tmp
->tm_isdst
!= 1)
541 result
= tzname
[tmp
->tm_isdst
];
542 return (result
== NULL
) ? &nada
: result
;
546 ** The code below can fail on certain theoretical systems;
547 ** it works on all known real-world systems as of 2004-12-30.
553 if (0 > (time_t) -1) { /* signed */
554 if (sizeof (time_t) == sizeof (intmax_t))
556 if (sizeof (time_t) > sizeof (long))
558 if (sizeof (time_t) > sizeof (int))
562 if (sizeof (time_t) == sizeof (uintmax_t))
564 if (sizeof (time_t) > sizeof (unsigned long))
566 if (sizeof (time_t) > sizeof (unsigned int))
572 dumptime(const struct tm
*timeptr
)
574 static const char wday_name
[][3] = {
575 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
577 static const char mon_name
[][3] = {
578 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
579 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
586 if (timeptr
== NULL
) {
591 ** The packaged versions of localtime and gmtime never put out-of-range
592 ** values in tm_wday or tm_mon, but since this code might be compiled
593 ** with other (perhaps experimental) versions, paranoia is in order.
595 if (timeptr
->tm_wday
< 0 || timeptr
->tm_wday
>=
596 (int) (sizeof wday_name
/ sizeof wday_name
[0]))
598 else wn
= wday_name
[timeptr
->tm_wday
];
599 if (timeptr
->tm_mon
< 0 || timeptr
->tm_mon
>=
600 (int) (sizeof mon_name
/ sizeof mon_name
[0]))
602 else mn
= mon_name
[timeptr
->tm_mon
];
603 printf("%.3s %.3s%3d %.2d:%.2d:%.2d ",
605 timeptr
->tm_mday
, timeptr
->tm_hour
,
606 timeptr
->tm_min
, timeptr
->tm_sec
);
608 trail
= timeptr
->tm_year
% DIVISOR
+ TM_YEAR_BASE
% DIVISOR
;
609 lead
= timeptr
->tm_year
/ DIVISOR
+ TM_YEAR_BASE
/ DIVISOR
+
612 if (trail
< 0 && lead
> 0) {
615 } else if (lead
< 0 && trail
> 0) {
621 else printf("%d%d", lead
, ((trail
< 0) ? -trail
: trail
));