zdump.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680
  1. /*
  2. ** This file is in the public domain, so clarified as of
  3. ** 2009-05-17 by Arthur David Olson.
  4. */
  5. static char elsieid[] = "%W%";
  6. /*
  7. ** This code has been made independent of the rest of the time
  8. ** conversion package to increase confidence in the verification it provides.
  9. ** You can use this code to help in verifying other implementations.
  10. */
  11. #include "stdio.h" /* for stdout, stderr, perror */
  12. #include "string.h" /* for strcpy */
  13. #include "sys/types.h" /* for time_t */
  14. #include "time.h" /* for struct tm */
  15. #include "stdlib.h" /* for exit, malloc, atoi */
  16. #include "float.h" /* for FLT_MAX and DBL_MAX */
  17. #include "ctype.h" /* for isalpha et al. */
  18. #ifndef isascii
  19. #define isascii(x) 1
  20. #endif /* !defined isascii */
  21. #ifndef ZDUMP_LO_YEAR
  22. #define ZDUMP_LO_YEAR (-500)
  23. #endif /* !defined ZDUMP_LO_YEAR */
  24. #ifndef ZDUMP_HI_YEAR
  25. #define ZDUMP_HI_YEAR 2500
  26. #endif /* !defined ZDUMP_HI_YEAR */
  27. #ifndef MAX_STRING_LENGTH
  28. #define MAX_STRING_LENGTH 1024
  29. #endif /* !defined MAX_STRING_LENGTH */
  30. #ifndef TRUE
  31. #define TRUE 1
  32. #endif /* !defined TRUE */
  33. #ifndef FALSE
  34. #define FALSE 0
  35. #endif /* !defined FALSE */
  36. #ifndef EXIT_SUCCESS
  37. #define EXIT_SUCCESS 0
  38. #endif /* !defined EXIT_SUCCESS */
  39. #ifndef EXIT_FAILURE
  40. #define EXIT_FAILURE 1
  41. #endif /* !defined EXIT_FAILURE */
  42. #ifndef SECSPERMIN
  43. #define SECSPERMIN 60
  44. #endif /* !defined SECSPERMIN */
  45. #ifndef MINSPERHOUR
  46. #define MINSPERHOUR 60
  47. #endif /* !defined MINSPERHOUR */
  48. #ifndef SECSPERHOUR
  49. #define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
  50. #endif /* !defined SECSPERHOUR */
  51. #ifndef HOURSPERDAY
  52. #define HOURSPERDAY 24
  53. #endif /* !defined HOURSPERDAY */
  54. #ifndef EPOCH_YEAR
  55. #define EPOCH_YEAR 1970
  56. #endif /* !defined EPOCH_YEAR */
  57. #ifndef TM_YEAR_BASE
  58. #define TM_YEAR_BASE 1900
  59. #endif /* !defined TM_YEAR_BASE */
  60. #ifndef DAYSPERNYEAR
  61. #define DAYSPERNYEAR 365
  62. #endif /* !defined DAYSPERNYEAR */
  63. #ifndef isleap
  64. #define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
  65. #endif /* !defined isleap */
  66. #ifndef isleap_sum
  67. /*
  68. ** See tzfile.h for details on isleap_sum.
  69. */
  70. #define isleap_sum(a, b) isleap((a) % 400 + (b) % 400)
  71. #endif /* !defined isleap_sum */
  72. #define SECSPERDAY ((long) SECSPERHOUR * HOURSPERDAY)
  73. #define SECSPERNYEAR (SECSPERDAY * DAYSPERNYEAR)
  74. #define SECSPERLYEAR (SECSPERNYEAR + SECSPERDAY)
  75. #ifndef HAVE_GETTEXT
  76. #define HAVE_GETTEXT 0
  77. #endif
  78. #if HAVE_GETTEXT
  79. #include "locale.h" /* for setlocale */
  80. #include "libintl.h"
  81. #endif /* HAVE_GETTEXT */
  82. #ifndef GNUC_or_lint
  83. #ifdef lint
  84. #define GNUC_or_lint
  85. #else /* !defined lint */
  86. #ifdef __GNUC__
  87. #define GNUC_or_lint
  88. #endif /* defined __GNUC__ */
  89. #endif /* !defined lint */
  90. #endif /* !defined GNUC_or_lint */
  91. #ifndef INITIALIZE
  92. #ifdef GNUC_or_lint
  93. #define INITIALIZE(x) ((x) = 0)
  94. #else /* !defined GNUC_or_lint */
  95. #define INITIALIZE(x)
  96. #endif /* !defined GNUC_or_lint */
  97. #endif /* !defined INITIALIZE */
  98. /*
  99. ** For the benefit of GNU folk...
  100. ** `_(MSGID)' uses the current locale's message library string for MSGID.
  101. ** The default is to use gettext if available, and use MSGID otherwise.
  102. */
  103. #ifndef _
  104. #if HAVE_GETTEXT
  105. #define _(msgid) gettext(msgid)
  106. #else /* !HAVE_GETTEXT */
  107. #define _(msgid) msgid
  108. #endif /* !HAVE_GETTEXT */
  109. #endif /* !defined _ */
  110. #ifndef TZ_DOMAIN
  111. #define TZ_DOMAIN "tz"
  112. #endif /* !defined TZ_DOMAIN */
  113. extern char ** environ;
  114. extern int getopt(int argc, char * const argv[],
  115. const char * options);
  116. extern char * optarg;
  117. extern int optind;
  118. extern char * tzname[2];
  119. static time_t absolute_min_time;
  120. static time_t absolute_max_time;
  121. static size_t longest;
  122. static char * progname;
  123. static int warned;
  124. static char * abbr(struct tm * tmp);
  125. static void abbrok(const char * abbrp, const char * zone);
  126. static long delta(struct tm * newp, struct tm * oldp);
  127. static void dumptime(const struct tm * tmp);
  128. static time_t hunt(char * name, time_t lot, time_t hit);
  129. static void setabsolutes(void);
  130. static void show(char * zone, time_t t, int v);
  131. static const char * tformat(void);
  132. static time_t yeartot(long y);
  133. #ifndef TYPECHECK
  134. #define my_localtime localtime
  135. #else /* !defined TYPECHECK */
  136. static struct tm *
  137. my_localtime(tp)
  138. time_t * tp;
  139. {
  140. register struct tm * tmp;
  141. tmp = localtime(tp);
  142. if (tp != NULL && tmp != NULL) {
  143. struct tm tm;
  144. register time_t t;
  145. tm = *tmp;
  146. t = mktime(&tm);
  147. if (t - *tp >= 1 || *tp - t >= 1) {
  148. (void) fflush(stdout);
  149. (void) fprintf(stderr, "\n%s: ", progname);
  150. (void) fprintf(stderr, tformat(), *tp);
  151. (void) fprintf(stderr, " ->");
  152. (void) fprintf(stderr, " year=%d", tmp->tm_year);
  153. (void) fprintf(stderr, " mon=%d", tmp->tm_mon);
  154. (void) fprintf(stderr, " mday=%d", tmp->tm_mday);
  155. (void) fprintf(stderr, " hour=%d", tmp->tm_hour);
  156. (void) fprintf(stderr, " min=%d", tmp->tm_min);
  157. (void) fprintf(stderr, " sec=%d", tmp->tm_sec);
  158. (void) fprintf(stderr, " isdst=%d", tmp->tm_isdst);
  159. (void) fprintf(stderr, " -> ");
  160. (void) fprintf(stderr, tformat(), t);
  161. (void) fprintf(stderr, "\n");
  162. }
  163. }
  164. return tmp;
  165. }
  166. #endif /* !defined TYPECHECK */
  167. static void
  168. abbrok(abbrp, zone)
  169. const char * const abbrp;
  170. const char * const zone;
  171. {
  172. register const char * cp;
  173. register char * wp;
  174. if (warned)
  175. return;
  176. cp = abbrp;
  177. wp = NULL;
  178. while (isascii((unsigned char) *cp) && isalpha((unsigned char) *cp))
  179. ++cp;
  180. if (cp - abbrp == 0)
  181. wp = _("lacks alphabetic at start");
  182. else if (cp - abbrp < 3)
  183. wp = _("has fewer than 3 alphabetics");
  184. else if (cp - abbrp > 6)
  185. wp = _("has more than 6 alphabetics");
  186. if (wp == NULL && (*cp == '+' || *cp == '-')) {
  187. ++cp;
  188. if (isascii((unsigned char) *cp) &&
  189. isdigit((unsigned char) *cp))
  190. if (*cp++ == '1' && *cp >= '0' && *cp <= '4')
  191. ++cp;
  192. if (*cp != '\0')
  193. wp = _("differs from POSIX standard");
  194. }
  195. if (wp == NULL)
  196. return;
  197. (void) fflush(stdout);
  198. (void) fprintf(stderr,
  199. _("%s: warning: zone \"%s\" abbreviation \"%s\" %s\n"),
  200. progname, zone, abbrp, wp);
  201. warned = TRUE;
  202. }
  203. static void
  204. usage(stream, status)
  205. FILE * const stream;
  206. const int status;
  207. {
  208. (void) fprintf(stream,
  209. _("%s: usage is %s [ --version ] [ --help ] [ -v ] [ -c [loyear,]hiyear ] zonename ...\n\
  210. \n\
  211. Report bugs to tz@elsie.nci.nih.gov.\n"),
  212. progname, progname);
  213. exit(status);
  214. }
  215. int
  216. main(argc, argv)
  217. int argc;
  218. char * argv[];
  219. {
  220. register int i;
  221. register int c;
  222. register int vflag;
  223. register char * cutarg;
  224. register long cutloyear = ZDUMP_LO_YEAR;
  225. register long cuthiyear = ZDUMP_HI_YEAR;
  226. register time_t cutlotime;
  227. register time_t cuthitime;
  228. register char ** fakeenv;
  229. time_t now;
  230. time_t t;
  231. time_t newt;
  232. struct tm tm;
  233. struct tm newtm;
  234. register struct tm * tmp;
  235. register struct tm * newtmp;
  236. INITIALIZE(cutlotime);
  237. INITIALIZE(cuthitime);
  238. #if HAVE_GETTEXT
  239. (void) setlocale(LC_ALL, "");
  240. #ifdef TZ_DOMAINDIR
  241. (void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
  242. #endif /* defined TEXTDOMAINDIR */
  243. (void) textdomain(TZ_DOMAIN);
  244. #endif /* HAVE_GETTEXT */
  245. progname = argv[0];
  246. for (i = 1; i < argc; ++i)
  247. if (strcmp(argv[i], "--version") == 0) {
  248. (void) printf("%s\n", elsieid);
  249. exit(EXIT_SUCCESS);
  250. } else if (strcmp(argv[i], "--help") == 0) {
  251. usage(stdout, EXIT_SUCCESS);
  252. }
  253. vflag = 0;
  254. cutarg = NULL;
  255. while ((c = getopt(argc, argv, "c:v")) == 'c' || c == 'v')
  256. if (c == 'v')
  257. vflag = 1;
  258. else cutarg = optarg;
  259. if ((c != EOF && c != -1) ||
  260. (optind == argc - 1 && strcmp(argv[optind], "=") == 0)) {
  261. usage(stderr, EXIT_FAILURE);
  262. }
  263. if (vflag) {
  264. if (cutarg != NULL) {
  265. long lo;
  266. long hi;
  267. char dummy;
  268. if (sscanf(cutarg, "%ld%c", &hi, &dummy) == 1) {
  269. cuthiyear = hi;
  270. } else if (sscanf(cutarg, "%ld,%ld%c",
  271. &lo, &hi, &dummy) == 2) {
  272. cutloyear = lo;
  273. cuthiyear = hi;
  274. } else {
  275. (void) fprintf(stderr, _("%s: wild -c argument %s\n"),
  276. progname, cutarg);
  277. exit(EXIT_FAILURE);
  278. }
  279. }
  280. setabsolutes();
  281. cutlotime = yeartot(cutloyear);
  282. cuthitime = yeartot(cuthiyear);
  283. }
  284. (void) time(&now);
  285. longest = 0;
  286. for (i = optind; i < argc; ++i)
  287. if (strlen(argv[i]) > longest)
  288. longest = strlen(argv[i]);
  289. {
  290. register int from;
  291. register int to;
  292. for (i = 0; environ[i] != NULL; ++i)
  293. continue;
  294. fakeenv = (char **) malloc((size_t) ((i + 2) *
  295. sizeof *fakeenv));
  296. if (fakeenv == NULL ||
  297. (fakeenv[0] = (char *) malloc(longest + 4)) == NULL) {
  298. (void) perror(progname);
  299. exit(EXIT_FAILURE);
  300. }
  301. to = 0;
  302. (void) strcpy(fakeenv[to++], "TZ=");
  303. for (from = 0; environ[from] != NULL; ++from)
  304. if (strncmp(environ[from], "TZ=", 3) != 0)
  305. fakeenv[to++] = environ[from];
  306. fakeenv[to] = NULL;
  307. environ = fakeenv;
  308. }
  309. for (i = optind; i < argc; ++i) {
  310. static char buf[MAX_STRING_LENGTH];
  311. (void) strcpy(&fakeenv[0][3], argv[i]);
  312. if (!vflag) {
  313. show(argv[i], now, FALSE);
  314. continue;
  315. }
  316. warned = FALSE;
  317. t = absolute_min_time;
  318. show(argv[i], t, TRUE);
  319. t += SECSPERHOUR * HOURSPERDAY;
  320. show(argv[i], t, TRUE);
  321. if (t < cutlotime)
  322. t = cutlotime;
  323. tmp = my_localtime(&t);
  324. if (tmp != NULL) {
  325. tm = *tmp;
  326. (void) strncpy(buf, abbr(&tm), (sizeof buf) - 1);
  327. }
  328. for ( ; ; ) {
  329. if (t >= cuthitime || t >= cuthitime - SECSPERHOUR * 12)
  330. break;
  331. newt = t + SECSPERHOUR * 12;
  332. newtmp = localtime(&newt);
  333. if (newtmp != NULL)
  334. newtm = *newtmp;
  335. if ((tmp == NULL || newtmp == NULL) ? (tmp != newtmp) :
  336. (delta(&newtm, &tm) != (newt - t) ||
  337. newtm.tm_isdst != tm.tm_isdst ||
  338. strcmp(abbr(&newtm), buf) != 0)) {
  339. newt = hunt(argv[i], t, newt);
  340. newtmp = localtime(&newt);
  341. if (newtmp != NULL) {
  342. newtm = *newtmp;
  343. (void) strncpy(buf,
  344. abbr(&newtm),
  345. (sizeof buf) - 1);
  346. }
  347. }
  348. t = newt;
  349. tm = newtm;
  350. tmp = newtmp;
  351. }
  352. t = absolute_max_time;
  353. t -= SECSPERHOUR * HOURSPERDAY;
  354. show(argv[i], t, TRUE);
  355. t += SECSPERHOUR * HOURSPERDAY;
  356. show(argv[i], t, TRUE);
  357. }
  358. if (fflush(stdout) || ferror(stdout)) {
  359. (void) fprintf(stderr, "%s: ", progname);
  360. (void) perror(_("Error writing to standard output"));
  361. exit(EXIT_FAILURE);
  362. }
  363. exit(EXIT_SUCCESS);
  364. /* If exit fails to exit... */
  365. return EXIT_FAILURE;
  366. }
  367. static void
  368. setabsolutes(void)
  369. {
  370. if (0.5 == (time_t) 0.5) {
  371. /*
  372. ** time_t is floating.
  373. */
  374. if (sizeof (time_t) == sizeof (float)) {
  375. absolute_min_time = (time_t) -FLT_MAX;
  376. absolute_max_time = (time_t) FLT_MAX;
  377. } else if (sizeof (time_t) == sizeof (double)) {
  378. absolute_min_time = (time_t) -DBL_MAX;
  379. absolute_max_time = (time_t) DBL_MAX;
  380. } else {
  381. (void) fprintf(stderr,
  382. _("%s: use of -v on system with floating time_t other than float or double\n"),
  383. progname);
  384. exit(EXIT_FAILURE);
  385. }
  386. } else if (0 > (time_t) -1) {
  387. /*
  388. ** time_t is signed. Assume overflow wraps around.
  389. */
  390. time_t t = 0;
  391. time_t t1 = 1;
  392. while (t < t1) {
  393. t = t1;
  394. t1 = 2 * t1 + 1;
  395. }
  396. absolute_max_time = t;
  397. t = -t;
  398. absolute_min_time = t - 1;
  399. if (t < absolute_min_time)
  400. absolute_min_time = t;
  401. } else {
  402. /*
  403. ** time_t is unsigned.
  404. */
  405. absolute_min_time = 0;
  406. absolute_max_time = absolute_min_time - 1;
  407. }
  408. }
  409. static time_t
  410. yeartot(y)
  411. const long y;
  412. {
  413. register long myy;
  414. register long seconds;
  415. register time_t t;
  416. myy = EPOCH_YEAR;
  417. t = 0;
  418. while (myy != y) {
  419. if (myy < y) {
  420. seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
  421. ++myy;
  422. if (t > absolute_max_time - seconds) {
  423. t = absolute_max_time;
  424. break;
  425. }
  426. t += seconds;
  427. } else {
  428. --myy;
  429. seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
  430. if (t < absolute_min_time + seconds) {
  431. t = absolute_min_time;
  432. break;
  433. }
  434. t -= seconds;
  435. }
  436. }
  437. return t;
  438. }
  439. static time_t
  440. hunt(char *name, time_t lot, time_t hit)
  441. {
  442. time_t t;
  443. long diff;
  444. struct tm lotm;
  445. register struct tm * lotmp;
  446. struct tm tm;
  447. register struct tm * tmp;
  448. char loab[MAX_STRING_LENGTH];
  449. lotmp = my_localtime(&lot);
  450. if (lotmp != NULL) {
  451. lotm = *lotmp;
  452. (void) strncpy(loab, abbr(&lotm), (sizeof loab) - 1);
  453. }
  454. for ( ; ; ) {
  455. diff = (long) (hit - lot);
  456. if (diff < 2)
  457. break;
  458. t = lot;
  459. t += diff / 2;
  460. if (t <= lot)
  461. ++t;
  462. else if (t >= hit)
  463. --t;
  464. tmp = my_localtime(&t);
  465. if (tmp != NULL)
  466. tm = *tmp;
  467. if ((lotmp == NULL || tmp == NULL) ? (lotmp == tmp) :
  468. (delta(&tm, &lotm) == (t - lot) &&
  469. tm.tm_isdst == lotm.tm_isdst &&
  470. strcmp(abbr(&tm), loab) == 0)) {
  471. lot = t;
  472. lotm = tm;
  473. lotmp = tmp;
  474. } else hit = t;
  475. }
  476. show(name, lot, TRUE);
  477. show(name, hit, TRUE);
  478. return hit;
  479. }
  480. /*
  481. ** Thanks to Paul Eggert for logic used in delta.
  482. */
  483. static long
  484. delta(newp, oldp)
  485. struct tm * newp;
  486. struct tm * oldp;
  487. {
  488. register long result;
  489. register int tmy;
  490. if (newp->tm_year < oldp->tm_year)
  491. return -delta(oldp, newp);
  492. result = 0;
  493. for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy)
  494. result += DAYSPERNYEAR + isleap_sum(tmy, TM_YEAR_BASE);
  495. result += newp->tm_yday - oldp->tm_yday;
  496. result *= HOURSPERDAY;
  497. result += newp->tm_hour - oldp->tm_hour;
  498. result *= MINSPERHOUR;
  499. result += newp->tm_min - oldp->tm_min;
  500. result *= SECSPERMIN;
  501. result += newp->tm_sec - oldp->tm_sec;
  502. return result;
  503. }
  504. static void
  505. show(char *zone, time_t t, int v)
  506. {
  507. register struct tm * tmp;
  508. (void) printf("%-*s ", (int) longest, zone);
  509. if (v) {
  510. tmp = gmtime(&t);
  511. if (tmp == NULL) {
  512. (void) printf(tformat(), t);
  513. } else {
  514. dumptime(tmp);
  515. (void) printf(" UTC");
  516. }
  517. (void) printf(" = ");
  518. }
  519. tmp = my_localtime(&t);
  520. dumptime(tmp);
  521. if (tmp != NULL) {
  522. if (*abbr(tmp) != '\0')
  523. (void) printf(" %s", abbr(tmp));
  524. if (v) {
  525. (void) printf(" isdst=%d", tmp->tm_isdst);
  526. #ifdef TM_GMTOFF
  527. (void) printf(" gmtoff=%ld", tmp->TM_GMTOFF);
  528. #endif /* defined TM_GMTOFF */
  529. }
  530. }
  531. (void) printf("\n");
  532. if (tmp != NULL && *abbr(tmp) != '\0')
  533. abbrok(abbr(tmp), zone);
  534. }
  535. static char *
  536. abbr(tmp)
  537. struct tm * tmp;
  538. {
  539. register char * result;
  540. static char nada;
  541. if (tmp->tm_isdst != 0 && tmp->tm_isdst != 1)
  542. return &nada;
  543. result = tzname[tmp->tm_isdst];
  544. return (result == NULL) ? &nada : result;
  545. }
  546. /*
  547. ** The code below can fail on certain theoretical systems;
  548. ** it works on all known real-world systems as of 2004-12-30.
  549. */
  550. static const char *
  551. tformat(void)
  552. {
  553. if (0.5 == (time_t) 0.5) { /* floating */
  554. if (sizeof (time_t) > sizeof (double))
  555. return "%Lg";
  556. return "%g";
  557. }
  558. if (0 > (time_t) -1) { /* signed */
  559. if (sizeof (time_t) > sizeof (long))
  560. return "%lld";
  561. if (sizeof (time_t) > sizeof (int))
  562. return "%ld";
  563. return "%d";
  564. }
  565. if (sizeof (time_t) > sizeof (unsigned long))
  566. return "%llu";
  567. if (sizeof (time_t) > sizeof (unsigned int))
  568. return "%lu";
  569. return "%u";
  570. }
  571. static void
  572. dumptime(timeptr)
  573. register const struct tm * timeptr;
  574. {
  575. static const char wday_name[][3] = {
  576. "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
  577. };
  578. static const char mon_name[][3] = {
  579. "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  580. "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
  581. };
  582. register const char * wn;
  583. register const char * mn;
  584. register int lead;
  585. register int trail;
  586. if (timeptr == NULL) {
  587. (void) printf("NULL");
  588. return;
  589. }
  590. /*
  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.
  594. */
  595. if (timeptr->tm_wday < 0 || timeptr->tm_wday >=
  596. (int) (sizeof wday_name / sizeof wday_name[0]))
  597. wn = "???";
  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]))
  601. mn = "???";
  602. else mn = mon_name[timeptr->tm_mon];
  603. (void) printf("%.3s %.3s%3d %.2d:%.2d:%.2d ",
  604. wn, mn,
  605. timeptr->tm_mday, timeptr->tm_hour,
  606. timeptr->tm_min, timeptr->tm_sec);
  607. #define DIVISOR 10
  608. trail = timeptr->tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR;
  609. lead = timeptr->tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR +
  610. trail / DIVISOR;
  611. trail %= DIVISOR;
  612. if (trail < 0 && lead > 0) {
  613. trail += DIVISOR;
  614. --lead;
  615. } else if (lead < 0 && trail > 0) {
  616. trail -= DIVISOR;
  617. ++lead;
  618. }
  619. if (lead == 0)
  620. (void) printf("%d", trail);
  621. else (void) printf("%d%d", lead, ((trail < 0) ? -trail : trail));
  622. }