]>
git.saurik.com Git - apple/icu.git/blob - icuSources/tools/tzcode/zdump.c
c7199acb8725cc2a08d33c11acae1f2826186f85
1 static char elsieid
[] = "@(#)zdump.c 8.3";
4 ** This code has been made independent of the rest of the time
5 ** conversion package to increase confidence in the verification it provides.
6 ** You can use this code to help in verifying other implementations.
10 * ICU note: Mr. Arthur David Olson (olsona@dc37a.nci.nih.gov) stated that
11 * "zdump.c is indeed in the public domain" in e-mail on Feb 22, 2007.
12 * This version of zdump.c is modified by ICU team to change output format
13 * and some additional options.
17 #include "stdio.h" /* for stdout, stderr, perror */
18 #include "string.h" /* for strcpy */
19 #include "sys/types.h" /* for time_t */
20 #include "time.h" /* for struct tm */
21 #include "stdlib.h" /* for exit, malloc, atoi */
22 #include "float.h" /* for FLT_MAX and DBL_MAX */
23 #include "ctype.h" /* for isalpha et al. */
25 /* Enable extensions and modifications for ICU. */
34 #endif /* !defined isascii */
37 #define ZDUMP_LO_YEAR (-500)
38 #endif /* !defined ZDUMP_LO_YEAR */
41 #define ZDUMP_HI_YEAR 2500
42 #endif /* !defined ZDUMP_HI_YEAR */
44 #ifndef MAX_STRING_LENGTH
45 #define MAX_STRING_LENGTH 1024
46 #endif /* !defined MAX_STRING_LENGTH */
50 #endif /* !defined TRUE */
54 #endif /* !defined FALSE */
57 #define EXIT_SUCCESS 0
58 #endif /* !defined EXIT_SUCCESS */
61 #define EXIT_FAILURE 1
62 #endif /* !defined EXIT_FAILURE */
66 #endif /* !defined SECSPERMIN */
69 #define MINSPERHOUR 60
70 #endif /* !defined MINSPERHOUR */
73 #define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
74 #endif /* !defined SECSPERHOUR */
77 #define HOURSPERDAY 24
78 #endif /* !defined HOURSPERDAY */
81 #define EPOCH_YEAR 1970
82 #endif /* !defined EPOCH_YEAR */
85 #define TM_YEAR_BASE 1900
86 #endif /* !defined TM_YEAR_BASE */
89 #define DAYSPERNYEAR 365
90 #endif /* !defined DAYSPERNYEAR */
93 #define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
94 #endif /* !defined isleap */
98 ** See tzfile.h for details on isleap_sum.
100 #define isleap_sum(a, b) isleap((a) % 400 + (b) % 400)
101 #endif /* !defined isleap_sum */
103 #define SECSPERDAY ((long) SECSPERHOUR * HOURSPERDAY)
104 #define SECSPERNYEAR (SECSPERDAY * DAYSPERNYEAR)
105 #define SECSPERLYEAR (SECSPERNYEAR + SECSPERDAY)
108 #include "locale.h" /* for setlocale */
110 #endif /* HAVE_GETTEXT */
115 #else /* !defined lint */
118 #endif /* defined __GNUC__ */
119 #endif /* !defined lint */
120 #endif /* !defined GNUC_or_lint */
124 #define INITIALIZE(x) ((x) = 0)
125 #else /* !defined GNUC_or_lint */
126 #define INITIALIZE(x)
127 #endif /* !defined GNUC_or_lint */
128 #endif /* !defined INITIALIZE */
131 ** For the benefit of GNU folk...
132 ** `_(MSGID)' uses the current locale's message library string for MSGID.
133 ** The default is to use gettext if available, and use MSGID otherwise.
138 #define _(msgid) gettext(msgid)
139 #else /* !HAVE_GETTEXT */
140 #define _(msgid) msgid
141 #endif /* !HAVE_GETTEXT */
142 #endif /* !defined _ */
145 #define TZ_DOMAIN "tz"
146 #endif /* !defined TZ_DOMAIN */
150 #endif /* !defined P */
152 extern char ** environ
;
153 extern int getopt
P((int argc
, char * const argv
[],
154 const char * options
));
155 extern char * optarg
;
157 extern char * tzname
[2];
159 static time_t absolute_min_time
;
160 static time_t absolute_max_time
;
161 static size_t longest
;
162 static char * progname
;
165 static char * abbr
P((struct tm
* tmp
));
166 static void abbrok
P((const char * abbrp
, const char * zone
));
167 static long delta
P((struct tm
* newp
, struct tm
* oldp
));
168 static void dumptime
P((const struct tm
* tmp
));
169 static time_t hunt
P((char * name
, time_t lot
, time_t hit
));
170 static void setabsolutes
P((void));
171 static void show
P((char * zone
, time_t t
, int v
));
172 static const char * tformat
P((void));
173 static time_t yeartot
P((long y
));
175 typedef struct listentry
{
177 struct listentry
* next
;
180 static time_t huntICU
P((char * name
, time_t lot
, time_t hit
, FILE *fp
));
181 static void dumptimeICU
P((FILE * fp
, time_t t
));
182 static void showICU
P((FILE * fp
, char * zone
, time_t t1
, time_t t2
));
183 static int getall
P((struct listentry
** namelist
));
184 static void getzones
P((char * basedir
, char * subdir
, struct listentry
** last
, int * count
));
188 #define my_localtime localtime
189 #else /* !defined TYPECHECK */
194 register struct tm
* tmp
;
197 if (tp
!= NULL
&& tmp
!= NULL
) {
203 if (t
- *tp
>= 1 || *tp
- t
>= 1) {
204 (void) fflush(stdout
);
205 (void) fprintf(stderr
, "\n%s: ", progname
);
206 (void) fprintf(stderr
, tformat(), *tp
);
207 (void) fprintf(stderr
, " ->");
208 (void) fprintf(stderr
, " year=%d", tmp
->tm_year
);
209 (void) fprintf(stderr
, " mon=%d", tmp
->tm_mon
);
210 (void) fprintf(stderr
, " mday=%d", tmp
->tm_mday
);
211 (void) fprintf(stderr
, " hour=%d", tmp
->tm_hour
);
212 (void) fprintf(stderr
, " min=%d", tmp
->tm_min
);
213 (void) fprintf(stderr
, " sec=%d", tmp
->tm_sec
);
214 (void) fprintf(stderr
, " isdst=%d", tmp
->tm_isdst
);
215 (void) fprintf(stderr
, " -> ");
216 (void) fprintf(stderr
, tformat(), t
);
217 (void) fprintf(stderr
, "\n");
222 #endif /* !defined TYPECHECK */
226 const char * const abbrp
;
227 const char * const zone
;
229 register const char * cp
;
236 while (isascii((unsigned char) *cp
) && isalpha((unsigned char) *cp
))
239 wp
= _("lacks alphabetic at start");
240 else if (cp
- abbrp
< 3)
241 wp
= _("has fewer than 3 alphabetics");
242 else if (cp
- abbrp
> 6)
243 wp
= _("has more than 6 alphabetics");
244 if (wp
== NULL
&& (*cp
== '+' || *cp
== '-')) {
246 if (isascii((unsigned char) *cp
) &&
247 isdigit((unsigned char) *cp
))
248 if (*cp
++ == '1' && *cp
>= '0' && *cp
<= '4')
251 wp
= _("differs from POSIX standard");
255 (void) fflush(stdout
);
256 (void) fprintf(stderr
,
257 _("%s: warning: zone \"%s\" abbreviation \"%s\" %s\n"),
258 progname
, zone
, abbrp
, wp
);
270 register char * cutarg
;
271 register long cutloyear
= ZDUMP_LO_YEAR
;
272 register long cuthiyear
= ZDUMP_HI_YEAR
;
273 register time_t cutlotime
;
274 register time_t cuthitime
;
275 register char ** fakeenv
;
281 register struct tm
* tmp
;
282 register struct tm
* newtmp
;
288 listentry
* namelist
= NULL
;
292 INITIALIZE(cutlotime
);
293 INITIALIZE(cuthitime
);
295 (void) setlocale(LC_ALL
, "");
297 (void) bindtextdomain(TZ_DOMAIN
, TZ_DOMAINDIR
);
298 #endif /* defined TEXTDOMAINDIR */
299 (void) textdomain(TZ_DOMAIN
);
300 #endif /* HAVE_GETTEXT */
302 for (i
= 1; i
< argc
; ++i
)
303 if (strcmp(argv
[i
], "--version") == 0) {
304 (void) printf("%s\n", elsieid
);
315 c
= getopt(argc
, argv
, "ac:d:iv");
337 if ((c
!= EOF
&& c
!= -1) ||
338 (optind
== argc
- 1 && strcmp(argv
[optind
], "=") == 0)) {
339 (void) fprintf(stderr
,
340 _("%s: usage is %s [ --version ] [ -a ] [ -v ] [ -i ] [ -c [loyear,]hiyear ] [ -d dir ] [ zonename ... ]\n"),
345 if (dirarg
!= NULL
) {
347 /* create the output directory */
349 if ((dp
= opendir(dirarg
)) == NULL
) {
350 fprintf(stderr
, "cannot create the target directory");
356 while ((c
= getopt(argc
, argv
, "c:v")) == 'c' || c
== 'v')
359 else cutarg
= optarg
;
360 if ((c
!= EOF
&& c
!= -1) ||
361 (optind
== argc
- 1 && strcmp(argv
[optind
], "=") == 0)) {
362 (void) fprintf(stderr
,
363 _("%s: usage is %s [ --version ] [ -v ] [ -c [loyear,]hiyear ] zonename ...\n"),
369 if (cutarg
!= NULL
) {
374 if (sscanf(cutarg
, "%ld%c", &hi
, &dummy
) == 1) {
376 } else if (sscanf(cutarg
, "%ld,%ld%c",
377 &lo
, &hi
, &dummy
) == 2) {
381 (void) fprintf(stderr
, _("%s: wild -c argument %s\n"),
387 cutlotime
= yeartot(cutloyear
);
388 cuthitime
= yeartot(cuthiyear
);
393 /* get all available zones */
398 count
= getall(&namelist
);
400 fakeargv
= (char **) malloc((size_t) (argc
+ count
) * sizeof *argv
);
402 if ((fakeargv = (char **) malloc((size_t) (argc + count) * sizeof *argv)) == NULL) {
406 for (i
= 0; i
< argc
; i
++) {
407 fakeargv
[i
] = argv
[i
];
409 for (i
= 0; i
< count
; i
++) {
410 fakeargv
[i
+ argc
] = namelist
->name
;
411 namelist
= namelist
->next
;
419 for (i
= optind
; i
< argc
; ++i
)
420 if (strlen(argv
[i
]) > longest
)
421 longest
= strlen(argv
[i
]);
426 for (i
= 0; environ
[i
] != NULL
; ++i
)
428 fakeenv
= (char **) malloc((size_t) ((i
+ 2) *
430 if (fakeenv
== NULL
||
431 (fakeenv
[0] = (char *) malloc(longest
+ 4)) == NULL
) {
432 (void) perror(progname
);
436 (void) strcpy(fakeenv
[to
++], "TZ=");
437 for (from
= 0; environ
[from
] != NULL
; ++from
)
438 if (strncmp(environ
[from
], "TZ=", 3) != 0)
439 fakeenv
[to
++] = environ
[from
];
443 for (i
= optind
; i
< argc
; ++i
) {
444 static char buf
[MAX_STRING_LENGTH
];
446 (void) strcpy(&fakeenv
[0][3], argv
[i
]);
448 show(argv
[i
], now
, FALSE
);
454 if (dirarg
== NULL
) {
455 /* we want to display a zone name here */
459 printf("ZONE: %s\n", argv
[i
]);
462 char path
[FILENAME_MAX
+ 1];
463 strcpy(path
, dirarg
);
465 zstart
= strlen(path
);
466 strcat(path
, argv
[i
]);
467 /* replace '/' with '-' */
468 while(path
[++zstart
] != 0) {
469 if (path
[zstart
] == '/') {
473 if ((fp
= fopen(path
, "w")) == NULL
) {
474 fprintf(stderr
, "cannot create output file %s\n", path
);
481 t
= absolute_min_time
;
483 /* skip displaying info for the lowest time, which is actually not
484 * a transition when -i option is set */
487 show(argv
[i
], t
, TRUE
);
488 t
+= SECSPERHOUR
* HOURSPERDAY
;
489 show(argv
[i
], t
, TRUE
);
495 tmp
= my_localtime(&t
);
498 (void) strncpy(buf
, abbr(&tm
), (sizeof buf
) - 1);
503 newt
= t
+ SECSPERHOUR
* 12;
504 if (newt
>= cuthitime
)
508 newtmp
= localtime(&newt
);
513 /* We do not want to capture transitions just for
514 * abbreviated zone name changes */
515 if ((tmp
== NULL
|| newtmp
== NULL
) ? (tmp
!= newtmp
) :
516 (delta(&newtm
, &tm
) != (newt
- t
) ||
517 newtm
.tm_isdst
!= tm
.tm_isdst
)) {
518 newt
= huntICU(argv
[i
], t
, newt
, fp
);
519 newtmp
= localtime(&newt
);
520 if (newtmp
!= NULL
) {
529 if ((tmp
== NULL
|| newtmp
== NULL
) ? (tmp
!= newtmp
) :
530 (delta(&newtm
, &tm
) != (newt
- t
) ||
531 newtm
.tm_isdst
!= tm
.tm_isdst
||
532 strcmp(abbr(&newtm
), buf
) != 0)) {
533 newt
= hunt(argv
[i
], t
, newt
);
534 newtmp
= localtime(&newt
);
535 if (newtmp
!= NULL
) {
551 /* skip displaying info for the highest time, which is actually not
552 * a transition when -i option is used*/
554 t
= absolute_max_time
;
555 t
-= SECSPERHOUR
* HOURSPERDAY
;
556 show(argv
[i
], t
, TRUE
);
557 t
+= SECSPERHOUR
* HOURSPERDAY
;
558 show(argv
[i
], t
, TRUE
);
568 if (fflush(stdout
) || ferror(stdout
)) {
569 (void) fprintf(stderr
, "%s: ", progname
);
570 (void) perror(_("Error writing to standard output"));
575 struct listentry
* entry
= namelist
;
576 struct listentry
* next
;
577 while (entry
!= NULL
) {
586 /* If exit fails to exit... */
593 if (0.5 == (time_t) 0.5) {
595 ** time_t is floating.
597 if (sizeof (time_t) == sizeof (float)) {
598 absolute_min_time
= (time_t) -FLT_MAX
;
599 absolute_max_time
= (time_t) FLT_MAX
;
600 } else if (sizeof (time_t) == sizeof (double)) {
601 absolute_min_time
= (time_t) -DBL_MAX
;
602 absolute_max_time
= (time_t) DBL_MAX
;
604 (void) fprintf(stderr
,
605 _("%s: use of -v on system with floating time_t other than float or double\n"),
609 } else if (0 > (time_t) -1) {
611 ** time_t is signed. Assume overflow wraps around.
621 absolute_max_time
= t
;
623 absolute_min_time
= t
- 1;
624 if (t
< absolute_min_time
)
625 absolute_min_time
= t
;
628 ** time_t is unsigned.
630 absolute_min_time
= 0;
631 absolute_max_time
= absolute_min_time
- 1;
640 register long seconds
;
647 seconds
= isleap(myy
) ? SECSPERLYEAR
: SECSPERNYEAR
;
649 if (t
> absolute_max_time
- seconds
) {
650 t
= absolute_max_time
;
656 seconds
= isleap(myy
) ? SECSPERLYEAR
: SECSPERNYEAR
;
657 if (t
< absolute_min_time
+ seconds
) {
658 t
= absolute_min_time
;
668 hunt(char *name
, time_t lot
, time_t hit
)
673 register struct tm
* lotmp
;
675 register struct tm
* tmp
;
676 char loab
[MAX_STRING_LENGTH
];
678 lotmp
= my_localtime(&lot
);
681 (void) strncpy(loab
, abbr(&lotm
), (sizeof loab
) - 1);
684 diff
= (long) (hit
- lot
);
693 tmp
= my_localtime(&t
);
696 if ((lotmp
== NULL
|| tmp
== NULL
) ? (lotmp
== tmp
) :
697 (delta(&tm
, &lotm
) == (t
- lot
) &&
698 tm
.tm_isdst
== lotm
.tm_isdst
&&
699 strcmp(abbr(&tm
), loab
) == 0)) {
705 show(name
, lot
, TRUE
);
706 show(name
, hit
, TRUE
);
711 ** Thanks to Paul Eggert for logic used in delta.
719 register long result
;
722 if (newp
->tm_year
< oldp
->tm_year
)
723 return -delta(oldp
, newp
);
725 for (tmy
= oldp
->tm_year
; tmy
< newp
->tm_year
; ++tmy
)
726 result
+= DAYSPERNYEAR
+ isleap_sum(tmy
, TM_YEAR_BASE
);
727 result
+= newp
->tm_yday
- oldp
->tm_yday
;
728 result
*= HOURSPERDAY
;
729 result
+= newp
->tm_hour
- oldp
->tm_hour
;
730 result
*= MINSPERHOUR
;
731 result
+= newp
->tm_min
- oldp
->tm_min
;
732 result
*= SECSPERMIN
;
733 result
+= newp
->tm_sec
- oldp
->tm_sec
;
738 show(char *zone
, time_t t
, int v
)
740 register struct tm
* tmp
;
742 (void) printf("%-*s ", (int) longest
, zone
);
746 (void) printf(tformat(), t
);
749 (void) printf(" UTC");
751 (void) printf(" = ");
753 tmp
= my_localtime(&t
);
756 if (*abbr(tmp
) != '\0')
757 (void) printf(" %s", abbr(tmp
));
759 (void) printf(" isdst=%d", tmp
->tm_isdst
);
761 (void) printf(" gmtoff=%ld", tmp
->TM_GMTOFF
);
762 #endif /* defined TM_GMTOFF */
766 if (tmp
!= NULL
&& *abbr(tmp
) != '\0')
767 abbrok(abbr(tmp
), zone
);
774 register char * result
;
777 if (tmp
->tm_isdst
!= 0 && tmp
->tm_isdst
!= 1)
779 result
= tzname
[tmp
->tm_isdst
];
780 return (result
== NULL
) ? &nada
: result
;
784 ** The code below can fail on certain theoretical systems;
785 ** it works on all known real-world systems as of 2004-12-30.
791 if (0.5 == (time_t) 0.5) { /* floating */
792 if (sizeof (time_t) > sizeof (double))
796 if (0 > (time_t) -1) { /* signed */
797 if (sizeof (time_t) > sizeof (long))
799 if (sizeof (time_t) > sizeof (int))
803 if (sizeof (time_t) > sizeof (unsigned long))
805 if (sizeof (time_t) > sizeof (unsigned int))
812 register const struct tm
* timeptr
;
814 static const char wday_name
[][3] = {
815 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
817 static const char mon_name
[][3] = {
818 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
819 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
821 register const char * wn
;
822 register const char * mn
;
826 if (timeptr
== NULL
) {
827 (void) printf("NULL");
831 ** The packaged versions of localtime and gmtime never put out-of-range
832 ** values in tm_wday or tm_mon, but since this code might be compiled
833 ** with other (perhaps experimental) versions, paranoia is in order.
835 if (timeptr
->tm_wday
< 0 || timeptr
->tm_wday
>=
836 (int) (sizeof wday_name
/ sizeof wday_name
[0]))
838 else wn
= wday_name
[timeptr
->tm_wday
];
839 if (timeptr
->tm_mon
< 0 || timeptr
->tm_mon
>=
840 (int) (sizeof mon_name
/ sizeof mon_name
[0]))
842 else mn
= mon_name
[timeptr
->tm_mon
];
843 (void) printf("%.3s %.3s%3d %.2d:%.2d:%.2d ",
845 timeptr
->tm_mday
, timeptr
->tm_hour
,
846 timeptr
->tm_min
, timeptr
->tm_sec
);
848 trail
= timeptr
->tm_year
% DIVISOR
+ TM_YEAR_BASE
% DIVISOR
;
849 lead
= timeptr
->tm_year
/ DIVISOR
+ TM_YEAR_BASE
/ DIVISOR
+
852 if (trail
< 0 && lead
> 0) {
855 } else if (lead
< 0 && trail
> 0) {
860 (void) printf("%d", trail
);
861 else (void) printf("%d%d", lead
, ((trail
< 0) ? -trail
: trail
));
866 huntICU(char *name
, time_t lot
, time_t hit
, FILE * fp
)
871 register struct tm
* lotmp
;
873 register struct tm
* tmp
;
874 char loab
[MAX_STRING_LENGTH
];
876 lotmp
= my_localtime(&lot
);
879 (void) strncpy(loab
, abbr(&lotm
), (sizeof loab
) - 1);
882 diff
= (long) (hit
- lot
);
891 tmp
= my_localtime(&t
);
894 /* We do not want to capture transitions just for
895 * abbreviated zone name changes */
896 if ((lotmp
== NULL
|| tmp
== NULL
) ? (lotmp
== tmp
) :
897 (delta(&tm
, &lotm
) == (t
- lot
) &&
898 tm
.tm_isdst
== lotm
.tm_isdst
)) {
904 showICU(fp
, name
, lot
, hit
);
908 static void showICU(FILE * fp
, char *zone
, time_t t1
, time_t t2
)
919 static void dumptimeICU(FILE * fp
, time_t t
)
921 static const char wday_name
[][3] = {
922 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
931 loc
= *my_localtime(&t
);
933 trail
= loc
.tm_year
% DIVISOR
+ TM_YEAR_BASE
% DIVISOR
;
934 lead
= loc
.tm_year
/ DIVISOR
+ TM_YEAR_BASE
/ DIVISOR
+ trail
/ DIVISOR
;
936 if (trail
< 0 && lead
> 0) {
939 } else if (lead
< 0 && trail
> 0) {
944 fprintf(fp
, "%04d-%02d-%02d", lead
* DIVISOR
+ trail
, loc
.tm_mon
+ 1, loc
.tm_mday
);
945 fprintf(fp
, " %.3s ", wday_name
[loc
.tm_wday
]);
946 fprintf(fp
, "%02d:%02d:%02d", loc
.tm_hour
, loc
.tm_min
, loc
.tm_sec
);
949 offset
= delta(&loc
, &gmt
);
958 offset
= (offset
- sec
) / 60;
962 fprintf(fp
, "%02d", hour
);
963 fprintf(fp
, "%02d", min
);
964 fprintf(fp
, "%02d", sec
);
965 fprintf(fp
, "[DST=%d]", loc
.tm_isdst
);
968 static int getall(struct listentry
** namelist
) {
970 struct listentry dummyentry
;
971 struct listentry
* last
= &dummyentry
;
973 getzones(TZDIR
, NULL
, &last
, &count
);
975 *namelist
= dummyentry
.next
;
981 static void getzones(char * basedir
, char * relpath
, struct listentry
** last
, int * count
) {
982 char path
[FILENAME_MAX
+ 1];
986 strcpy(path
, basedir
);
987 if (relpath
!= NULL
) {
989 strcat(path
, relpath
);
992 if ((dp
= opendir(path
)) == NULL
) {
994 if (strstr(relpath
, ".tab") == NULL
) {
998 if ((pzonename
= malloc(strlen(relpath
) + 1)) == NULL
) {
1001 strcpy(pzonename
, relpath
);
1003 if ((pentry
= malloc(sizeof(listentry
))) == NULL
) {
1007 pentry
->name
= pzonename
;
1008 pentry
->next
= NULL
;
1009 (*last
)->next
= pentry
;
1015 while ((dir
= readdir(dp
)) != NULL
) {
1016 char subpath
[FILENAME_MAX
+ 1];
1018 if (strcmp(dir
->d_name
, ".") == 0
1019 || strcmp(dir
->d_name
, "..") == 0) {
1022 if (relpath
!= NULL
) {
1023 strcpy(subpath
, relpath
);
1024 strcat(subpath
, "/");
1025 strcat(subpath
, dir
->d_name
);
1027 strcpy(subpath
, dir
->d_name
);
1029 getzones(basedir
, subpath
, last
, count
);