1 /* Dump time zone data in a textual format. */
4 ** This file is in the public domain, so clarified as of
5 ** 2009-05-17 by Arthur David Olson.
10 #ifndef NETBSD_INSPIRED
11 # define NETBSD_INSPIRED 1
18 # define HAVE_SNPRINTF (199901 <= __STDC_VERSION__)
21 #ifndef HAVE_LOCALTIME_R
22 # define HAVE_LOCALTIME_R 1
25 #ifndef HAVE_LOCALTIME_RZ
27 # define HAVE_LOCALTIME_RZ (NETBSD_INSPIRED && USE_LTZ)
29 # define HAVE_LOCALTIME_RZ 0
38 #define ZDUMP_LO_YEAR (-500)
39 #endif /* !defined ZDUMP_LO_YEAR */
42 #define ZDUMP_HI_YEAR 2500
43 #endif /* !defined ZDUMP_HI_YEAR */
45 #ifndef MAX_STRING_LENGTH
46 #define MAX_STRING_LENGTH 1024
47 #endif /* !defined MAX_STRING_LENGTH */
49 #define SECSPERNYEAR (SECSPERDAY * DAYSPERNYEAR)
50 #define SECSPERLYEAR (SECSPERNYEAR + SECSPERDAY)
51 #define SECSPER400YEARS (SECSPERNYEAR * (intmax_t) (300 + 3) \
52 + SECSPERLYEAR * (intmax_t) (100 - 3))
55 ** True if SECSPER400YEARS is known to be representable as an
56 ** intmax_t. It's OK that SECSPER400YEARS_FITS can in theory be false
57 ** even if SECSPER400YEARS is representable, because when that happens
58 ** the code merely runs a bit more slowly, and this slowness doesn't
59 ** occur on any practical platform.
61 enum { SECSPER400YEARS_FITS
= SECSPERLYEAR
<= INTMAX_MAX
/ 400 };
64 #include <locale.h> /* for setlocale */
65 #endif /* HAVE_GETTEXT */
67 #if ! HAVE_LOCALTIME_RZ
69 # define timezone_t char **
73 extern int getopt(int argc
, char * const argv
[],
74 const char * options
);
79 /* The minimum and maximum finite time values. */
80 enum { atime_shift
= CHAR_BIT
* sizeof (time_t) - 2 };
81 static time_t const absolute_min_time
=
83 ? (- ((time_t) ~ (time_t) 0 < 0)
84 - (((time_t) 1 << atime_shift
) - 1 + ((time_t) 1 << atime_shift
)))
86 static time_t const absolute_max_time
=
88 ? (((time_t) 1 << atime_shift
) - 1 + ((time_t) 1 << atime_shift
))
91 static char * progname
;
95 static char const *abbr(struct tm
const *);
96 static intmax_t delta(struct tm
*, struct tm
*) ATTRIBUTE_PURE
;
97 static void dumptime(struct tm
const *);
98 static time_t hunt(timezone_t
, char *, time_t, time_t);
99 static void show(timezone_t
, char *, time_t, bool);
100 static void showtrans(char const *, struct tm
const *, time_t, char const *,
102 static const char *tformat(void);
103 static time_t yeartot(intmax_t) ATTRIBUTE_PURE
;
105 /* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */
106 #define is_digit(c) ((unsigned)(c) - '0' <= 9)
108 /* Is A an alphabetic character in the C locale? */
115 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
116 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
117 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
118 case 'V': case 'W': case 'X': case 'Y': case 'Z':
119 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
120 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
121 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
122 case 'v': case 'w': case 'x': case 'y': case 'z':
127 /* Return A + B, exiting if the result would overflow. */
129 sumsize(size_t a
, size_t b
)
133 fprintf(stderr
, "%s: size overflow\n", progname
);
139 /* Return a pointer to a newly allocated buffer of size SIZE, exiting
140 on failure. SIZE should be nonzero. */
141 static void * ATTRIBUTE_MALLOC
144 void *p
= malloc(size
);
154 # define tzset zdump_tzset
155 static void tzset(void) { }
158 /* Assume gmtime_r works if localtime_r does.
159 A replacement localtime_r is defined below if needed. */
160 #if ! HAVE_LOCALTIME_R
163 # define gmtime_r zdump_gmtime_r
166 gmtime_r(time_t *tp
, struct tm
*tmp
)
168 struct tm
*r
= gmtime(tp
);
178 /* Platforms with TM_ZONE don't need tzname, so they can use the
179 faster localtime_rz or localtime_r if available. */
181 #if defined TM_ZONE && HAVE_LOCALTIME_RZ
182 # define USE_LOCALTIME_RZ true
184 # define USE_LOCALTIME_RZ false
187 #if ! USE_LOCALTIME_RZ
189 # if !defined TM_ZONE || ! HAVE_LOCALTIME_R || ! HAVE_TZSET
191 # define localtime_r zdump_localtime_r
193 localtime_r(time_t *tp
, struct tm
*tmp
)
195 struct tm
*r
= localtime(tp
);
205 # define localtime_rz zdump_localtime_rz
207 localtime_rz(timezone_t rz
, time_t *tp
, struct tm
*tmp
)
209 return localtime_r(tp
, tmp
);
214 # define mktime_z zdump_mktime_z
216 mktime_z(timezone_t tz
, struct tm
*tmp
)
224 # define tzalloc zdump_tzalloc
225 # define tzfree zdump_tzfree
228 tzalloc(char const *val
)
230 static char **fakeenv
;
231 char **env
= fakeenv
;
239 env
= xmalloc(sumsize(sizeof *environ
,
240 (e
- environ
) * sizeof *environ
));
242 for (e
= environ
; (env
[to
] = *e
); e
++)
243 to
+= strncmp(*e
, "TZ=", 3) != 0;
245 env0
= xmalloc(sumsize(sizeof "TZ=", strlen(val
)));
246 env
[0] = strcat(strcpy(env0
, "TZ="), val
);
247 environ
= fakeenv
= env
;
253 tzfree(timezone_t env
)
258 #endif /* ! USE_LOCALTIME_RZ */
260 /* A UT time zone, and its initializer. */
261 static timezone_t gmtz
;
265 if (USE_LOCALTIME_RZ
) {
266 static char const utc
[] = "UTC0";
275 /* Convert *TP to UT, storing the broken-down time into *TMP.
276 Return TMP if successful, NULL otherwise. This is like gmtime_r(TP, TMP),
277 except typically faster if USE_LOCALTIME_RZ. */
279 my_gmtime_r(time_t *tp
, struct tm
*tmp
)
281 return USE_LOCALTIME_RZ
? localtime_rz(gmtz
, tp
, tmp
) : gmtime_r(tp
, tmp
);
285 # define my_localtime_rz localtime_rz
286 #else /* !defined TYPECHECK */
289 my_localtime_rz(timezone_t tz
, time_t *tp
, struct tm
*tmp
)
291 tmp
= localtime_rz(tz
, tp
, tmp
);
297 t
= mktime_z(tz
, &tm
);
300 fprintf(stderr
, "\n%s: ", progname
);
301 fprintf(stderr
, tformat(), *tp
);
302 fprintf(stderr
, " ->");
303 fprintf(stderr
, " year=%d", tmp
->tm_year
);
304 fprintf(stderr
, " mon=%d", tmp
->tm_mon
);
305 fprintf(stderr
, " mday=%d", tmp
->tm_mday
);
306 fprintf(stderr
, " hour=%d", tmp
->tm_hour
);
307 fprintf(stderr
, " min=%d", tmp
->tm_min
);
308 fprintf(stderr
, " sec=%d", tmp
->tm_sec
);
309 fprintf(stderr
, " isdst=%d", tmp
->tm_isdst
);
310 fprintf(stderr
, " -> ");
311 fprintf(stderr
, tformat(), t
);
312 fprintf(stderr
, "\n");
318 #endif /* !defined TYPECHECK */
321 abbrok(const char *const abbrp
, const char *const zone
)
323 register const char * cp
;
324 register const char * wp
;
329 while (is_alpha(*cp
) || is_digit(*cp
) || *cp
== '-' || *cp
== '+')
332 wp
= _("has characters other than ASCII alphanumerics, '-' or '+'");
333 else if (cp
- abbrp
< 3)
334 wp
= _("has fewer than 3 characters");
335 else if (cp
- abbrp
> 6)
336 wp
= _("has more than 6 characters");
341 _("%s: warning: zone \"%s\" abbreviation \"%s\" %s\n"),
342 progname
, zone
, abbrp
, wp
);
343 warned
= errout
= true;
346 /* Return a time zone abbreviation. If the abbreviation needs to be
347 saved, use *BUF (of size *BUFALLOC) to save it, and return the
348 abbreviation in the possibly-reallocated *BUF. Otherwise, just
349 return the abbreviation. Get the abbreviation from TMP.
350 Exit on memory allocation failure. */
352 saveabbr(char **buf
, size_t *bufalloc
, struct tm
const *tmp
)
354 char const *ab
= abbr(tmp
);
355 if (HAVE_LOCALTIME_RZ
)
358 size_t ablen
= strlen(ab
);
359 if (*bufalloc
<= ablen
) {
362 /* Make the new buffer at least twice as long as the old,
363 to avoid O(N**2) behavior on repeated calls. */
364 *bufalloc
= sumsize(*bufalloc
, ablen
+ 1);
366 *buf
= xmalloc(*bufalloc
);
368 return strcpy(*buf
, ab
);
373 close_file(FILE *stream
)
375 char const *e
= (ferror(stream
) ? _("I/O error")
376 : fclose(stream
) != 0 ? strerror(errno
) : NULL
);
378 fprintf(stderr
, "%s: %s\n", progname
, e
);
384 usage(FILE * const stream
, const int status
)
387 _("%s: usage: %s OPTIONS TIMEZONE ...\n"
389 " -c [L,]U Start at year L (default -500), end before year U (default 2500)\n"
390 " -t [L,]U Start at time L, end before time U (in seconds since 1970)\n"
391 " -i List transitions briefly (format is experimental)\n" \
392 " -v List transitions verbosely\n"
393 " -V List transitions a bit less verbosely\n"
394 " --help Output this help\n"
395 " --version Output version info\n"
397 "Report bugs to %s.\n"),
398 progname
, progname
, REPORT_BUGS_TO
);
399 if (status
== EXIT_SUCCESS
)
405 main(int argc
, char *argv
[])
407 /* These are static so that they're initially zero. */
408 static char * abbrev
;
409 static size_t abbrevsize
;
414 register char * cutarg
;
415 register char * cuttimes
;
416 register time_t cutlotime
;
417 register time_t cuthitime
;
421 cutlotime
= absolute_min_time
;
422 cuthitime
= absolute_max_time
;
424 setlocale(LC_ALL
, "");
426 bindtextdomain(TZ_DOMAIN
, TZ_DOMAINDIR
);
427 #endif /* defined TEXTDOMAINDIR */
428 textdomain(TZ_DOMAIN
);
429 #endif /* HAVE_GETTEXT */
431 for (i
= 1; i
< argc
; ++i
)
432 if (strcmp(argv
[i
], "--version") == 0) {
433 printf("zdump %s%s\n", PKGVERSION
, TZVERSION
);
435 } else if (strcmp(argv
[i
], "--help") == 0) {
436 usage(stdout
, EXIT_SUCCESS
);
438 vflag
= Vflag
= false;
439 cutarg
= cuttimes
= NULL
;
441 switch (getopt(argc
, argv
, "c:it:vV")) {
442 case 'c': cutarg
= optarg
; break;
443 case 't': cuttimes
= optarg
; break;
444 case 'i': iflag
= true; break;
445 case 'v': vflag
= true; break;
446 case 'V': Vflag
= true; break;
448 if (! (optind
== argc
- 1 && strcmp(argv
[optind
], "=") == 0))
449 goto arg_processing_done
;
452 usage(stderr
, EXIT_FAILURE
);
454 arg_processing_done
:;
456 if (iflag
| vflag
| Vflag
) {
460 register intmax_t cutloyear
= ZDUMP_LO_YEAR
;
461 register intmax_t cuthiyear
= ZDUMP_HI_YEAR
;
462 if (cutarg
!= NULL
) {
463 lo
= strtoimax(cutarg
, &loend
, 10);
464 if (cutarg
!= loend
&& !*loend
) {
467 } else if (cutarg
!= loend
&& *loend
== ','
468 && (hi
= strtoimax(loend
+ 1, &hiend
, 10),
469 loend
+ 1 != hiend
&& !*hiend
)) {
473 fprintf(stderr
, _("%s: wild -c argument %s\n"),
478 if (cutarg
!= NULL
|| cuttimes
== NULL
) {
479 cutlotime
= yeartot(cutloyear
);
480 cuthitime
= yeartot(cuthiyear
);
482 if (cuttimes
!= NULL
) {
483 lo
= strtoimax(cuttimes
, &loend
, 10);
484 if (cuttimes
!= loend
&& !*loend
) {
486 if (hi
< cuthitime
) {
487 if (hi
< absolute_min_time
)
488 hi
= absolute_min_time
;
491 } else if (cuttimes
!= loend
&& *loend
== ','
492 && (hi
= strtoimax(loend
+ 1, &hiend
, 10),
493 loend
+ 1 != hiend
&& !*hiend
)) {
494 if (cutlotime
< lo
) {
495 if (absolute_max_time
< lo
)
496 lo
= absolute_max_time
;
499 if (hi
< cuthitime
) {
500 if (hi
< absolute_min_time
)
501 hi
= absolute_min_time
;
506 _("%s: wild -t argument %s\n"),
514 if (! (iflag
| vflag
| Vflag
))
517 for (i
= optind
; i
< argc
; i
++) {
518 size_t arglen
= strlen(argv
[i
]);
519 if (longest
< arglen
)
520 longest
= arglen
< INT_MAX
? arglen
: INT_MAX
;
523 for (i
= optind
; i
< argc
; ++i
) {
524 timezone_t tz
= tzalloc(argv
[i
]);
533 if (! (iflag
| vflag
| Vflag
)) {
534 show(tz
, argv
[i
], now
, false);
539 t
= absolute_min_time
;
540 if (! (iflag
| Vflag
)) {
541 show(tz
, argv
[i
], t
, true);
543 show(tz
, argv
[i
], t
, true);
548 tm_ok
= my_localtime_rz(tz
, &t
, &tm
) != NULL
;
550 ab
= saveabbr(&abbrev
, &abbrevsize
, &tm
);
552 showtrans("\nTZ=%f", &tm
, t
, ab
, argv
[i
]);
553 showtrans("-\t-\t%Q", &tm
, t
, ab
, argv
[i
]);
556 while (t
< cuthitime
) {
557 time_t newt
= ((t
< absolute_max_time
- SECSPERDAY
/ 2
558 && t
+ SECSPERDAY
/ 2 < cuthitime
)
561 struct tm
*newtmp
= localtime_rz(tz
, &newt
, &newtm
);
562 bool newtm_ok
= newtmp
!= NULL
;
563 if (tm_ok
!= newtm_ok
564 || (tm_ok
&& (delta(&newtm
, &tm
) != newt
- t
565 || newtm
.tm_isdst
!= tm
.tm_isdst
566 || strcmp(abbr(&newtm
), ab
) != 0))) {
567 newt
= hunt(tz
, argv
[i
], t
, newt
);
568 newtmp
= localtime_rz(tz
, &newt
, &newtm
);
569 newtm_ok
= newtmp
!= NULL
;
571 showtrans("%Y-%m-%d\t%L\t%Q", newtmp
, newt
,
572 newtm_ok
? abbr(&newtm
) : NULL
, argv
[i
]);
574 show(tz
, argv
[i
], newt
- 1, true);
575 show(tz
, argv
[i
], newt
, true);
581 ab
= saveabbr(&abbrev
, &abbrevsize
, &newtm
);
585 if (! (iflag
| Vflag
)) {
586 t
= absolute_max_time
;
588 show(tz
, argv
[i
], t
, true);
590 show(tz
, argv
[i
], t
, true);
595 if (errout
&& (ferror(stderr
) || fclose(stderr
) != 0))
603 register intmax_t myy
, seconds
, years
;
609 if (SECSPER400YEARS_FITS
&& 400 <= y
- myy
) {
610 intmax_t diff400
= (y
- myy
) / 400;
611 if (INTMAX_MAX
/ SECSPER400YEARS
< diff400
)
612 return absolute_max_time
;
613 seconds
= diff400
* SECSPER400YEARS
;
614 years
= diff400
* 400;
616 seconds
= isleap(myy
) ? SECSPERLYEAR
: SECSPERNYEAR
;
620 if (t
> absolute_max_time
- seconds
)
621 return absolute_max_time
;
625 if (SECSPER400YEARS_FITS
&& y
+ 400 <= myy
&& myy
< 0) {
626 intmax_t diff400
= (myy
- y
) / 400;
627 if (INTMAX_MAX
/ SECSPER400YEARS
< diff400
)
628 return absolute_min_time
;
629 seconds
= diff400
* SECSPER400YEARS
;
630 years
= diff400
* 400;
632 seconds
= isleap(myy
- 1) ? SECSPERLYEAR
: SECSPERNYEAR
;
636 if (t
< absolute_min_time
+ seconds
)
637 return absolute_min_time
;
644 hunt(timezone_t tz
, char *name
, time_t lot
, time_t hit
)
647 static size_t loabsize
;
652 bool lotm_ok
= my_localtime_rz(tz
, &lot
, &lotm
) != NULL
;
656 ab
= saveabbr(&loab
, &loabsize
, &lotm
);
658 time_t diff
= hit
- lot
;
667 tm_ok
= my_localtime_rz(tz
, &t
, &tm
) != NULL
;
669 ? (delta(&tm
, &lotm
) == t
- lot
670 && tm
.tm_isdst
== lotm
.tm_isdst
671 && strcmp(abbr(&tm
), ab
) == 0)
672 : lotm_ok
== tm_ok
) {
682 ** Thanks to Paul Eggert for logic used in delta_nonneg.
686 delta_nonneg(struct tm
*newp
, struct tm
*oldp
)
688 register intmax_t result
;
692 for (tmy
= oldp
->tm_year
; tmy
< newp
->tm_year
; ++tmy
)
693 result
+= DAYSPERNYEAR
+ isleap_sum(tmy
, TM_YEAR_BASE
);
694 result
+= newp
->tm_yday
- oldp
->tm_yday
;
695 result
*= HOURSPERDAY
;
696 result
+= newp
->tm_hour
- oldp
->tm_hour
;
697 result
*= MINSPERHOUR
;
698 result
+= newp
->tm_min
- oldp
->tm_min
;
699 result
*= SECSPERMIN
;
700 result
+= newp
->tm_sec
- oldp
->tm_sec
;
705 delta(struct tm
*newp
, struct tm
*oldp
)
707 return (newp
->tm_year
< oldp
->tm_year
708 ? -delta_nonneg(oldp
, newp
)
709 : delta_nonneg(newp
, oldp
));
713 /* Return A->tm_yday, adjusted to compare it fairly to B->tm_yday.
714 Assume A and B differ by at most one year. */
716 adjusted_yday(struct tm
const *a
, struct tm
const *b
)
718 int yday
= a
->tm_yday
;
719 if (b
->tm_year
< a
->tm_year
)
720 yday
+= 365 + isleap_sum(b
->tm_year
, TM_YEAR_BASE
);
725 /* If A is the broken-down local time and B the broken-down UT for
726 the same instant, return A's UT offset in seconds, where positive
727 offsets are east of Greenwich. On failure, return LONG_MIN.
729 If T is nonnull, *T is the timestamp that corresponds to A; call
730 my_gmtime_r and use its result instead of B. Otherwise, B is the
731 possibly nonnull result of an earlier call to my_gmtime_r. */
733 gmtoff(struct tm
const *a
, time_t *t
, struct tm
const *b
)
740 b
= my_gmtime_r(t
, &tm
);
744 int ayday
= adjusted_yday(a
, b
);
745 int byday
= adjusted_yday(b
, a
);
746 int days
= ayday
- byday
;
747 long hours
= a
->tm_hour
- b
->tm_hour
+ 24 * days
;
748 long minutes
= a
->tm_min
- b
->tm_min
+ 60 * hours
;
749 long seconds
= a
->tm_sec
- b
->tm_sec
+ 60 * minutes
;
756 show(timezone_t tz
, char *zone
, time_t t
, bool v
)
758 register struct tm
* tmp
;
759 register struct tm
* gmtmp
;
762 printf("%-*s ", longest
, zone
);
764 gmtmp
= my_gmtime_r(&t
, &gmtm
);
766 printf(tformat(), t
);
773 tmp
= my_localtime_rz(tz
, &t
, &tm
);
776 if (*abbr(tmp
) != '\0')
777 printf(" %s", abbr(tmp
));
779 long off
= gmtoff(tmp
, NULL
, gmtmp
);
780 printf(" isdst=%d", tmp
->tm_isdst
);
782 printf(" gmtoff=%ld", off
);
786 if (tmp
!= NULL
&& *abbr(tmp
) != '\0')
787 abbrok(abbr(tmp
), zone
);
791 # define my_snprintf snprintf
795 /* A substitute for snprintf that is good enough for zdump. */
796 static int ATTRIBUTE_FORMAT((printf
, 3, 4))
797 my_snprintf(char *s
, size_t size
, char const *format
, ...)
804 va_start(args
, format
);
805 if (strcmp(format
, "%s") == 0) {
806 arg
= va_arg(args
, char const *);
807 arglen
= strlen(arg
);
809 n
= vsprintf(buf
, format
, args
);
817 slen
= arglen
< size
? arglen
: size
- 1;
818 memcpy(s
, arg
, slen
);
820 n
= arglen
<= INT_MAX
? arglen
: -1;
826 /* Store into BUF, of size SIZE, a formatted local time taken from *TM.
827 Use ISO 8601 format +HH:MM:SS. Omit :SS if SS is zero, and omit
828 :MM too if MM is also zero.
830 Return the length of the resulting string. If the string does not
831 fit, return the length that the string would have been if it had
832 fit; do not overrun the output buffer. */
834 format_local_time(char *buf
, size_t size
, struct tm
const *tm
)
836 int ss
= tm
->tm_sec
, mm
= tm
->tm_min
, hh
= tm
->tm_hour
;
838 ? my_snprintf(buf
, size
, "%02d:%02d:%02d", hh
, mm
, ss
)
840 ? my_snprintf(buf
, size
, "%02d:%02d", hh
, mm
)
841 : my_snprintf(buf
, size
, "%02d", hh
));
844 /* Store into BUF, of size SIZE, a formatted UT offset for the
845 localtime *TM corresponding to time T. Use ISO 8601 format
846 +HHMMSS, or -HHMMSS for timestamps west of Greenwich; use the
847 format -00 for unknown UT offsets. If the hour needs more than
848 two digits to represent, extend the length of HH as needed.
849 Otherwise, omit SS if SS is zero, and omit MM too if MM is also
852 Return the length of the resulting string, or -1 if the result is
853 not representable as a string. If the string does not fit, return
854 the length that the string would have been if it had fit; do not
855 overrun the output buffer. */
857 format_utc_offset(char *buf
, size_t size
, struct tm
const *tm
, time_t t
)
859 long off
= gmtoff(tm
, &t
, NULL
);
860 char sign
= ((off
< 0
862 && (*abbr(tm
) == '-' || strcmp(abbr(tm
), "zzz") == 0)))
875 return (ss
|| 100 <= hh
876 ? my_snprintf(buf
, size
, "%c%02ld%02d%02d", sign
, hh
, mm
, ss
)
878 ? my_snprintf(buf
, size
, "%c%02ld%02d", sign
, hh
, mm
)
879 : my_snprintf(buf
, size
, "%c%02ld", sign
, hh
));
882 /* Store into BUF (of size SIZE) a quoted string representation of P.
883 If the representation's length is less than SIZE, return the
884 length; the representation is not null terminated. Otherwise
885 return SIZE, to indicate that BUF is too small. */
887 format_quoted_string(char *buf
, size_t size
, char const *p
)
899 default: *b
++ = c
, s
--; continue;
900 case '\0': *b
++ = '"', s
--; return size
- s
;
901 case '"': case '\\': break;
902 case ' ': c
= 's'; break;
903 case '\f': c
= 'f'; break;
904 case '\n': c
= 'n'; break;
905 case '\r': c
= 'r'; break;
906 case '\t': c
= 't'; break;
907 case '\v': c
= 'v'; break;
909 *b
++ = '\\', *b
++ = c
, s
-= 2;
913 /* Store into BUF (of size SIZE) a timestamp formatted by TIME_FMT.
914 TM is the broken-down time, T the seconds count, AB the time zone
915 abbreviation, and ZONE_NAME the zone name. Return true if
916 successful, false if the output would require more than SIZE bytes.
917 TIME_FMT uses the same format that strftime uses, with these
921 %L local time as per format_local_time
922 %Q like "U\t%Z\tD" where U is the UT offset as for format_utc_offset
923 and D is the isdst flag; except omit D if it is zero, omit %Z if
924 it equals U, quote and escape %Z if it contains nonalphabetics,
925 and omit any trailing tabs. */
928 istrftime(char *buf
, size_t size
, char const *time_fmt
,
929 struct tm
const *tm
, time_t t
, char const *ab
, char const *zone_name
)
933 char const *f
= time_fmt
, *p
;
936 if (*p
== '%' && p
[1] == '%')
940 && (p
[1] == 'f' || p
[1] == 'L' || p
[1] == 'Q'))) {
941 size_t formatted_len
;
942 size_t f_prefix_len
= p
- f
;
943 size_t f_prefix_copy_size
= p
- f
+ 2;
945 bool oversized
= sizeof fbuf
<= f_prefix_copy_size
;
946 char *f_prefix_copy
= oversized
? xmalloc(f_prefix_copy_size
) : fbuf
;
947 memcpy(f_prefix_copy
, f
, f_prefix_len
);
948 strcpy(f_prefix_copy
+ f_prefix_len
, "X");
949 formatted_len
= strftime(b
, s
, f_prefix_copy
, tm
);
952 if (formatted_len
== 0)
955 b
+= formatted_len
, s
-= formatted_len
;
960 formatted_len
= format_quoted_string(b
, s
, zone_name
);
963 formatted_len
= format_local_time(b
, s
, tm
);
968 int offlen
= format_utc_offset(b
, s
, tm
, t
);
969 if (! (0 <= offlen
&& offlen
< s
))
971 show_abbr
= strcmp(b
, ab
) != 0;
972 b
+= offlen
, s
-= offlen
;
979 for (abp
= ab
; is_alpha(*abp
); abp
++)
982 ? my_snprintf(b
, s
, "%s", ab
)
983 : format_quoted_string(b
, s
, ab
));
990 ? my_snprintf(b
, s
, &"\t\t%d"[show_abbr
], tm
->tm_isdst
)
995 if (s
<= formatted_len
)
997 b
+= formatted_len
, s
-= formatted_len
;
1004 /* Show a time transition. */
1006 showtrans(char const *time_fmt
, struct tm
const *tm
, time_t t
, char const *ab
,
1007 char const *zone_name
)
1010 printf(tformat(), t
);
1013 char stackbuf
[1000];
1014 size_t size
= sizeof stackbuf
;
1015 char *buf
= stackbuf
;
1016 char *bufalloc
= NULL
;
1017 while (! istrftime(buf
, size
, time_fmt
, tm
, t
, ab
, zone_name
)) {
1018 size
= sumsize(size
, size
);
1020 buf
= bufalloc
= xmalloc(size
);
1028 abbr(struct tm
const *tmp
)
1031 return tmp
->TM_ZONE
;
1034 if (0 <= tmp
->tm_isdst
&& tzname
[0 < tmp
->tm_isdst
])
1035 return tzname
[0 < tmp
->tm_isdst
];
1042 ** The code below can fail on certain theoretical systems;
1043 ** it works on all known real-world systems as of 2004-12-30.
1049 if (0 > (time_t) -1) { /* signed */
1050 if (sizeof (time_t) == sizeof (intmax_t))
1052 if (sizeof (time_t) > sizeof (long))
1054 if (sizeof (time_t) > sizeof (int))
1059 if (sizeof (time_t) == sizeof (uintmax_t))
1062 if (sizeof (time_t) > sizeof (unsigned long))
1064 if (sizeof (time_t) > sizeof (unsigned int))
1070 dumptime(register const struct tm
*timeptr
)
1072 static const char wday_name
[][4] = {
1073 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
1075 static const char mon_name
[][4] = {
1076 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
1077 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1079 register const char * wn
;
1080 register const char * mn
;
1084 if (timeptr
== NULL
) {
1089 ** The packaged localtime_rz and gmtime_r never put out-of-range
1090 ** values in tm_wday or tm_mon, but since this code might be compiled
1091 ** with other (perhaps experimental) versions, paranoia is in order.
1093 if (timeptr
->tm_wday
< 0 || timeptr
->tm_wday
>=
1094 (int) (sizeof wday_name
/ sizeof wday_name
[0]))
1096 else wn
= wday_name
[timeptr
->tm_wday
];
1097 if (timeptr
->tm_mon
< 0 || timeptr
->tm_mon
>=
1098 (int) (sizeof mon_name
/ sizeof mon_name
[0]))
1100 else mn
= mon_name
[timeptr
->tm_mon
];
1101 printf("%s %s%3d %.2d:%.2d:%.2d ",
1103 timeptr
->tm_mday
, timeptr
->tm_hour
,
1104 timeptr
->tm_min
, timeptr
->tm_sec
);
1106 trail
= timeptr
->tm_year
% DIVISOR
+ TM_YEAR_BASE
% DIVISOR
;
1107 lead
= timeptr
->tm_year
/ DIVISOR
+ TM_YEAR_BASE
/ DIVISOR
+
1110 if (trail
< 0 && lead
> 0) {
1113 } else if (lead
< 0 && trail
> 0) {
1118 printf("%d", trail
);
1119 else printf("%d%d", lead
, ((trail
< 0) ? -trail
: trail
));