1 /* $OpenBSD: zdump.c,v 1.14 2016/03/15 19:50:48 millert Exp $ */ 2 /* 3 ** This file is in the public domain, so clarified as of 4 ** 2009-05-17 by Arthur David Olson. 5 */ 6 7 /* 8 ** This code has been made independent of the rest of the time 9 ** conversion package to increase confidence in the verification it provides. 10 ** You can use this code to help in verifying other implementations. 11 */ 12 13 #include <ctype.h> 14 #include <stdio.h> 15 #include <string.h> 16 #include <stdlib.h> 17 #include <unistd.h> 18 #include <time.h> 19 20 #define ZDUMP_LO_YEAR (-500) 21 #define ZDUMP_HI_YEAR 2500 22 23 #define MAX_STRING_LENGTH 1024 24 25 #define TRUE 1 26 #define FALSE 0 27 28 #define SECSPERMIN 60 29 #define MINSPERHOUR 60 30 #define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) 31 #define HOURSPERDAY 24 32 #define EPOCH_YEAR 1970 33 #define TM_YEAR_BASE 1900 34 #define DAYSPERNYEAR 365 35 36 #define SECSPERDAY ((long) SECSPERHOUR * HOURSPERDAY) 37 #define SECSPERNYEAR (SECSPERDAY * DAYSPERNYEAR) 38 #define SECSPERLYEAR (SECSPERNYEAR + SECSPERDAY) 39 40 #define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) 41 42 #ifndef isleap_sum 43 /* 44 ** See tzfile.h for details on isleap_sum. 45 */ 46 #define isleap_sum(a, b) isleap((a) % 400 + (b) % 400) 47 #endif /* !defined isleap_sum */ 48 49 extern char **environ; 50 extern char *tzname[2]; 51 extern char *__progname; 52 53 time_t absolute_min_time; 54 time_t absolute_max_time; 55 size_t longest; 56 int warned; 57 58 static char *abbr(struct tm *tmp); 59 static void abbrok(const char *abbrp, const char *zone); 60 static long delta(struct tm *newp, struct tm *oldp); 61 static void dumptime(const struct tm *tmp); 62 static time_t hunt(char *name, time_t lot, time_t hit); 63 static void setabsolutes(void); 64 static void show(char *zone, time_t t, int v); 65 static const char *tformat(void); 66 static time_t yeartot(long y); 67 static void usage(void); 68 69 static void 70 abbrok(const char * const abbrp, const char * const zone) 71 { 72 const char *cp; 73 char *wp; 74 75 if (warned) 76 return; 77 cp = abbrp; 78 wp = NULL; 79 while (isascii((unsigned char)*cp) && 80 (isalnum((unsigned char)*cp) || *cp == '-' || *cp == '+')) 81 ++cp; 82 if (cp - abbrp < 3) 83 wp = "has fewer than 3 characters"; 84 else if (cp - abbrp > 6) 85 wp = "has more than 6 characters"; 86 else if (*cp) 87 wp = "has characters other than ASCII alphanumerics, '-' or '+'"; 88 else 89 return; 90 fflush(stdout); 91 fprintf(stderr, "%s: warning: zone \"%s\" abbreviation \"%s\" %s\n", 92 __progname, zone, abbrp, wp); 93 warned = TRUE; 94 } 95 96 static void 97 usage(void) 98 { 99 fprintf(stderr, "usage: %s [-v] [-c [loyear,]hiyear] zonename ...\n", 100 __progname); 101 exit(EXIT_FAILURE); 102 } 103 104 int 105 main(int argc, char *argv[]) 106 { 107 int i, c, vflag = 0; 108 char *cutarg = NULL; 109 long cutloyear = ZDUMP_LO_YEAR; 110 long cuthiyear = ZDUMP_HI_YEAR; 111 time_t cutlotime = 0, cuthitime = 0; 112 time_t now, t, newt; 113 struct tm tm, newtm, *tmp, *newtmp; 114 char **fakeenv; 115 116 if (pledge("stdio rpath", NULL) == -1) { 117 perror("pledge"); 118 exit(1); 119 } 120 121 while ((c = getopt(argc, argv, "c:v")) == 'c' || c == 'v') { 122 switch (c) { 123 case 'v': 124 vflag = 1; 125 break; 126 case 'c': 127 cutarg = optarg; 128 break; 129 default: 130 usage(); 131 break; 132 } 133 } 134 if (c != -1 || 135 (optind == argc - 1 && strcmp(argv[optind], "=") == 0)) { 136 usage(); 137 } 138 if (vflag) { 139 if (cutarg != NULL) { 140 long lo, hi; 141 char dummy; 142 143 if (sscanf(cutarg, "%ld%c", &hi, &dummy) == 1) { 144 cuthiyear = hi; 145 } else if (sscanf(cutarg, "%ld,%ld%c", 146 &lo, &hi, &dummy) == 2) { 147 cutloyear = lo; 148 cuthiyear = hi; 149 } else { 150 fprintf(stderr, "%s: wild -c argument %s\n", 151 __progname, cutarg); 152 exit(EXIT_FAILURE); 153 } 154 } 155 setabsolutes(); 156 cutlotime = yeartot(cutloyear); 157 cuthitime = yeartot(cuthiyear); 158 } 159 time(&now); 160 longest = 0; 161 for (i = optind; i < argc; ++i) 162 if (strlen(argv[i]) > longest) 163 longest = strlen(argv[i]); 164 165 { 166 int from, to; 167 168 for (i = 0; environ[i] != NULL; ++i) 169 continue; 170 fakeenv = reallocarray(NULL, i + 2, sizeof *fakeenv); 171 if (fakeenv == NULL || 172 (fakeenv[0] = malloc(longest + 4)) == NULL) { 173 perror(__progname); 174 exit(EXIT_FAILURE); 175 } 176 to = 0; 177 strlcpy(fakeenv[to++], "TZ=", longest + 4); 178 for (from = 0; environ[from] != NULL; ++from) 179 if (strncmp(environ[from], "TZ=", 3) != 0) 180 fakeenv[to++] = environ[from]; 181 fakeenv[to] = NULL; 182 environ = fakeenv; 183 } 184 for (i = optind; i < argc; ++i) { 185 char buf[MAX_STRING_LENGTH]; 186 187 strlcpy(&fakeenv[0][3], argv[i], longest + 1); 188 if (!vflag) { 189 show(argv[i], now, FALSE); 190 continue; 191 } 192 warned = FALSE; 193 t = absolute_min_time; 194 show(argv[i], t, TRUE); 195 t += SECSPERHOUR * HOURSPERDAY; 196 show(argv[i], t, TRUE); 197 if (t < cutlotime) 198 t = cutlotime; 199 tmp = localtime(&t); 200 if (tmp != NULL) { 201 tm = *tmp; 202 strlcpy(buf, abbr(&tm), sizeof buf); 203 } 204 for ( ; ; ) { 205 if (t >= cuthitime || t >= cuthitime - SECSPERHOUR * 12) 206 break; 207 newt = t + SECSPERHOUR * 12; 208 newtmp = localtime(&newt); 209 if (newtmp != NULL) 210 newtm = *newtmp; 211 if ((tmp == NULL || newtmp == NULL) ? (tmp != newtmp) : 212 (delta(&newtm, &tm) != (newt - t) || 213 newtm.tm_isdst != tm.tm_isdst || 214 strcmp(abbr(&newtm), buf) != 0)) { 215 newt = hunt(argv[i], t, newt); 216 newtmp = localtime(&newt); 217 if (newtmp != NULL) { 218 newtm = *newtmp; 219 strlcpy(buf, abbr(&newtm), sizeof buf); 220 } 221 } 222 t = newt; 223 tm = newtm; 224 tmp = newtmp; 225 } 226 t = absolute_max_time; 227 t -= SECSPERHOUR * HOURSPERDAY; 228 show(argv[i], t, TRUE); 229 t += SECSPERHOUR * HOURSPERDAY; 230 show(argv[i], t, TRUE); 231 } 232 if (fflush(stdout) || ferror(stdout)) { 233 fprintf(stderr, "%s: ", __progname); 234 perror("Error writing to standard output"); 235 exit(EXIT_FAILURE); 236 } 237 return 0; 238 } 239 240 static void 241 setabsolutes(void) 242 { 243 time_t t = 0, t1 = 1; 244 245 while (t < t1) { 246 t = t1; 247 t1 = 2 * t1 + 1; 248 } 249 250 absolute_max_time = t; 251 t = -t; 252 absolute_min_time = t - 1; 253 if (t < absolute_min_time) 254 absolute_min_time = t; 255 } 256 257 static time_t 258 yeartot(const long y) 259 { 260 long myy = EPOCH_YEAR, seconds; 261 time_t t = 0; 262 263 while (myy != y) { 264 if (myy < y) { 265 seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR; 266 ++myy; 267 if (t > absolute_max_time - seconds) { 268 t = absolute_max_time; 269 break; 270 } 271 t += seconds; 272 } else { 273 --myy; 274 seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR; 275 if (t < absolute_min_time + seconds) { 276 t = absolute_min_time; 277 break; 278 } 279 t -= seconds; 280 } 281 } 282 return t; 283 } 284 285 static time_t 286 hunt(char *name, time_t lot, time_t hit) 287 { 288 time_t t; 289 long diff; 290 struct tm lotm, *lotmp; 291 struct tm tm, *tmp; 292 char loab[MAX_STRING_LENGTH]; 293 294 lotmp = localtime(&lot); 295 if (lotmp != NULL) { 296 lotm = *lotmp; 297 strlcpy(loab, abbr(&lotm), sizeof loab); 298 } 299 for ( ; ; ) { 300 diff = (long) (hit - lot); 301 if (diff < 2) 302 break; 303 t = lot; 304 t += diff / 2; 305 if (t <= lot) 306 ++t; 307 else if (t >= hit) 308 --t; 309 tmp = localtime(&t); 310 if (tmp != NULL) 311 tm = *tmp; 312 if ((lotmp == NULL || tmp == NULL) ? (lotmp == tmp) : 313 (delta(&tm, &lotm) == (t - lot) && 314 tm.tm_isdst == lotm.tm_isdst && 315 strcmp(abbr(&tm), loab) == 0)) { 316 lot = t; 317 lotm = tm; 318 lotmp = tmp; 319 } else 320 hit = t; 321 } 322 show(name, lot, TRUE); 323 show(name, hit, TRUE); 324 return hit; 325 } 326 327 /* 328 ** Thanks to Paul Eggert for logic used in delta. 329 */ 330 331 static long 332 delta(struct tm *newp, struct tm *oldp) 333 { 334 long result; 335 int tmy; 336 337 if (newp->tm_year < oldp->tm_year) 338 return -delta(oldp, newp); 339 result = 0; 340 for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy) 341 result += DAYSPERNYEAR + isleap_sum(tmy, TM_YEAR_BASE); 342 result += newp->tm_yday - oldp->tm_yday; 343 result *= HOURSPERDAY; 344 result += newp->tm_hour - oldp->tm_hour; 345 result *= MINSPERHOUR; 346 result += newp->tm_min - oldp->tm_min; 347 result *= SECSPERMIN; 348 result += newp->tm_sec - oldp->tm_sec; 349 return result; 350 } 351 352 static void 353 show(char *zone, time_t t, int v) 354 { 355 struct tm *tmp; 356 357 printf("%-*s ", (int) longest, zone); 358 if (v) { 359 tmp = gmtime(&t); 360 if (tmp == NULL) { 361 printf(tformat(), t); 362 } else { 363 dumptime(tmp); 364 printf(" UTC"); 365 } 366 printf(" = "); 367 } 368 tmp = localtime(&t); 369 dumptime(tmp); 370 if (tmp != NULL) { 371 if (*abbr(tmp) != '\0') 372 printf(" %s", abbr(tmp)); 373 if (v) { 374 printf(" isdst=%d", tmp->tm_isdst); 375 #ifdef TM_GMTOFF 376 printf(" gmtoff=%ld", tmp->TM_GMTOFF); 377 #endif /* defined TM_GMTOFF */ 378 } 379 } 380 printf("\n"); 381 if (tmp != NULL && *abbr(tmp) != '\0') 382 abbrok(abbr(tmp), zone); 383 } 384 385 static char * 386 abbr(struct tm *tmp) 387 { 388 char *result; 389 static char nada; 390 391 if (tmp->tm_isdst != 0 && tmp->tm_isdst != 1) 392 return &nada; 393 result = tzname[tmp->tm_isdst]; 394 return (result == NULL) ? &nada : result; 395 } 396 397 /* 398 ** The code below can fail on certain theoretical systems; 399 ** it works on all known real-world systems as of 2004-12-30. 400 */ 401 402 static const char * 403 tformat(void) 404 { 405 return "%lld"; 406 } 407 408 static void 409 dumptime(const struct tm *timeptr) 410 { 411 static const char wday_name[][3] = { 412 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" 413 }; 414 static const char mon_name[][3] = { 415 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 416 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 417 }; 418 const char *wn, *mn; 419 int lead, trail; 420 421 if (timeptr == NULL) { 422 printf("NULL"); 423 return; 424 } 425 /* 426 ** The packaged versions of localtime and gmtime never put out-of-range 427 ** values in tm_wday or tm_mon, but since this code might be compiled 428 ** with other (perhaps experimental) versions, paranoia is in order. 429 */ 430 if (timeptr->tm_wday < 0 || timeptr->tm_wday >= 431 (int) (sizeof wday_name / sizeof wday_name[0])) 432 wn = "???"; 433 else 434 wn = wday_name[timeptr->tm_wday]; 435 if (timeptr->tm_mon < 0 || timeptr->tm_mon >= 436 (int) (sizeof mon_name / sizeof mon_name[0])) 437 mn = "???"; 438 else 439 mn = mon_name[timeptr->tm_mon]; 440 printf("%.3s %.3s%3d %.2d:%.2d:%.2d ", 441 wn, mn, 442 timeptr->tm_mday, timeptr->tm_hour, 443 timeptr->tm_min, timeptr->tm_sec); 444 #define DIVISOR 10 445 trail = timeptr->tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR; 446 lead = timeptr->tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR + 447 trail / DIVISOR; 448 trail %= DIVISOR; 449 if (trail < 0 && lead > 0) { 450 trail += DIVISOR; 451 --lead; 452 } else if (lead < 0 && trail > 0) { 453 trail -= DIVISOR; 454 ++lead; 455 } 456 if (lead == 0) 457 printf("%d", trail); 458 else 459 printf("%d%d", lead, ((trail < 0) ? -trail : trail)); 460 } 461