1:
2:
3:
4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34:
35:
36:
37:
38:
39: #include <time.h>
40: #include <t2ex/datetime.h>
41: #include <stdio.h>
42: #define TM_YEAR_BASE 1900
43: #define DAYSPERNYEAR 365
44: #define DAYSPERLYEAR (DAYSPERNYEAR + 1)
45: #define MONSPERYEAR 12
46: #define DAYSPERWEEK 7
47: #define HOURSPERDAY 24
48: #define MINSPERHOUR 60
49: #define SECSPERMIN 60
50: #define USG_COMPAT 1
51: #define NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU 1
52: #define INT_STRLEN_MAXIMUM(t) (sizeof(t) * 3 + 1)
53: #define isleap_sum(year,base) (((((year) + (base)) % 4) == 0 && (((year) + (base)) % 100) != 0) || (((year) + (base)) % 400) == 0)
54:
55: struct lc_time_T {
56: const char * mon[MONSPERYEAR];
57: const char * month[MONSPERYEAR];
58: const char * wday[DAYSPERWEEK];
59: const char * weekday[DAYSPERWEEK];
60: const char * X_fmt;
61: const char * x_fmt;
62: const char * c_fmt;
63: const char * am;
64: const char * pm;
65: const char * date_fmt;
66: };
67:
68: #ifdef LOCALE_HOME
69: #include "sys/stat.h"
70: static struct lc_time_T localebuf;
71: static struct lc_time_T * _loc(void);
72: #define Locale _loc()
73: #endif
74: #ifndef LOCALE_HOME
75: #define Locale (&C_time_locale)
76: #endif
77:
78: static const struct lc_time_T C_time_locale = {
79: {
80: "Jan", "Feb", "Mar", "Apr", "May", "Jun",
81: "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
82: }, {
83: "January", "February", "March", "April", "May", "June",
84: "July", "August", "September", "October", "November", "December"
85: }, {
86: "Sun", "Mon", "Tue", "Wed",
87: "Thu", "Fri", "Sat"
88: }, {
89: "Sunday", "Monday", "Tuesday", "Wednesday",
90: "Thursday", "Friday", "Saturday"
91: },
92:
93:
94: "%H:%M:%S",
95:
96: 97: 98: 99: 100: 101:
102: "%m/%d/%y",
103:
104: 105: 106: 107: 108: 109: 110: 111:
112: "%a %b %e %T %Y",
113:
114:
115: "AM",
116:
117:
118: "PM",
119:
120:
121: "%a %b %e %H:%M:%S %Z %Y"
122: };
123:
124: static char * _add(const char *, char *, const char *);
125: static char * _conv(int, const char *, char *, const char *);
126: static char * _fmt(const char *, const struct tm *, char *, const char *,
127: int *);
128: static char * _yconv(int, int, int, int, char *, const char *);
129:
130:
131:
132: #ifndef YEAR_2000_NAME
133: #define YEAR_2000_NAME "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS"
134: #endif
135:
136: #define IN_NONE 0
137: #define IN_SOME 1
138: #define IN_THIS 2
139: #define IN_ALL 3
140:
141: size_t
142: strftime(s, maxsize, format, t)
143: char * const s;
144: const size_t maxsize;
145: const char * const format;
146: const struct tm * const t;
147: {
148: char * p;
149: int warn;
150:
151:
152: #ifdef LOCALE_HOME
153: localebuf.mon[0] = 0;
154: #endif
155: warn = IN_NONE;
156: p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize, &warn);
157: #ifndef NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU
158: if (warn != IN_NONE && getenv(YEAR_2000_NAME) != NULL) {
159: (void) fprintf(stderr, "\n");
160: if (format == NULL)
161: (void) fprintf(stderr, "NULL strftime format ");
162: else (void) fprintf(stderr, "strftime format \"%s\" ",
163: format);
164: (void) fprintf(stderr, "yields only two digits of years in ");
165: if (warn == IN_SOME)
166: (void) fprintf(stderr, "some locales");
167: else if (warn == IN_THIS)
168: (void) fprintf(stderr, "the current locale");
169: else (void) fprintf(stderr, "all locales");
170: (void) fprintf(stderr, "\n");
171: }
172: #endif
173: if (p == s + maxsize) {
174: if (maxsize > 0)
175: s[maxsize - 1] = '\0';
176: return 0;
177: }
178: *p = '\0';
179: return p - s;
180: }
181:
182: static char *
183: _fmt(format, t, pt, ptlim, warnp)
184: const char * format;
185: const struct tm * const t;
186: char * pt;
187: const char * const ptlim;
188: int * warnp;
189: {struct tzinfo tz;
190: for ( ; *format; ++format) {
191: if (*format == '%') {
192: label:
193: switch (*++format) {
194: case '\0':
195: --format;
196: break;
197: case 'A':
198: pt = _add((t->tm_wday < 0 ||
199: t->tm_wday >= DAYSPERWEEK) ?
200: "?" : Locale->weekday[t->tm_wday],
201: pt, ptlim);
202: continue;
203: case 'a':
204: pt = _add((t->tm_wday < 0 ||
205: t->tm_wday >= DAYSPERWEEK) ?
206: "?" : Locale->wday[t->tm_wday],
207: pt, ptlim);
208: continue;
209: case 'B':
210: pt = _add((t->tm_mon < 0 ||
211: t->tm_mon >= MONSPERYEAR) ?
212: "?" : Locale->month[t->tm_mon],
213: pt, ptlim);
214: continue;
215: case 'b':
216: case 'h':
217: pt = _add((t->tm_mon < 0 ||
218: t->tm_mon >= MONSPERYEAR) ?
219: "?" : Locale->mon[t->tm_mon],
220: pt, ptlim);
221: continue;
222: case 'C':
223: 224: 225: 226: 227: 228: 229:
230: pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 0,
231: pt, ptlim);
232: continue;
233: case 'c':
234: {
235: int warn2 = IN_SOME;
236:
237: pt = _fmt(Locale->c_fmt, t, pt, ptlim, &warn2);
238: if (warn2 == IN_ALL)
239: warn2 = IN_THIS;
240: if (warn2 > *warnp)
241: *warnp = warn2;
242: }
243: continue;
244: case 'D':
245: pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp);
246: continue;
247: case 'd':
248: pt = _conv(t->tm_mday, "%02d", pt, ptlim);
249: continue;
250: case 'E':
251: case 'O':
252: 253: 254: 255: 256: 257: 258: 259: 260:
261: goto label;
262: case 'e':
263: pt = _conv(t->tm_mday, "%2d", pt, ptlim);
264: continue;
265: case 'F':
266: pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp);
267: continue;
268: case 'H':
269: pt = _conv(t->tm_hour, "%02d", pt, ptlim);
270: continue;
271: case 'I':
272: pt = _conv((t->tm_hour % 12) ?
273: (t->tm_hour % 12) : 12,
274: "%02d", pt, ptlim);
275: continue;
276: case 'j':
277: pt = _conv(t->tm_yday + 1, "%03d", pt, ptlim);
278: continue;
279: case 'k':
280: 281: 282: 283: 284: 285: 286: 287: 288: 289:
290: pt = _conv(t->tm_hour, "%2d", pt, ptlim);
291: continue;
292: #ifdef KITCHEN_SINK
293: case 'K':
294: 295: 296:
297: pt = _add("kitchen sink", pt, ptlim);
298: continue;
299: #endif
300: case 'l':
301: 302: 303: 304: 305: 306: 307: 308: 309:
310: pt = _conv((t->tm_hour % 12) ?
311: (t->tm_hour % 12) : 12,
312: "%2d", pt, ptlim);
313: continue;
314: case 'M':
315: pt = _conv(t->tm_min, "%02d", pt, ptlim);
316: continue;
317: case 'm':
318: pt = _conv(t->tm_mon + 1, "%02d", pt, ptlim);
319: continue;
320: case 'n':
321: pt = _add("\n", pt, ptlim);
322: continue;
323: case 'p':
324: pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ?
325: Locale->pm :
326: Locale->am,
327: pt, ptlim);
328: continue;
329: case 'R':
330: pt = _fmt("%H:%M", t, pt, ptlim, warnp);
331: continue;
332: case 'r':
333: pt = _fmt("%I:%M:%S %p", t, pt, ptlim, warnp);
334: continue;
335: case 'S':
336: pt = _conv(t->tm_sec, "%02d", pt, ptlim);
337: continue;
338: case 's':
339: {
340: struct tm tm;
341: char buf[INT_STRLEN_MAXIMUM(
342: time_t) + 1];
343: time_t mkt;
344:
345: tm = *t;
346: mkt = mktime(&tm);
347: if (1)
348: (void) snprintf(buf, sizeof buf,
349: "%ld", (long) mkt);
350: else (void) snprintf(buf, sizeof buf,
351: "%lu", (unsigned long) mkt);
352: pt = _add(buf, pt, ptlim);
353: }
354: continue;
355: case 'T':
356: pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp);
357: continue;
358: case 't':
359: pt = _add("\t", pt, ptlim);
360: continue;
361: case 'U':
362: pt = _conv((t->tm_yday + DAYSPERWEEK -
363: t->tm_wday) / DAYSPERWEEK,
364: "%02d", pt, ptlim);
365: continue;
366: case 'u':
367: 368: 369: 370: 371: 372:
373: pt = _conv((t->tm_wday == 0) ?
374: DAYSPERWEEK : t->tm_wday,
375: "%d", pt, ptlim);
376: continue;
377: case 'V':
378: case 'G':
379: case 'g':
380: 381: 382: 383: 384: 385: 386: 387: 388: 389: 390: 391: 392: 393: 394: 395: 396: 397:
398: {
399: int year;
400: int base;
401: int yday;
402: int wday;
403: int w;
404:
405: year = t->tm_year;
406: base = TM_YEAR_BASE;
407: yday = t->tm_yday;
408: wday = t->tm_wday;
409: for ( ; ; ) {
410: int len;
411: int bot;
412: int top;
413:
414: len = isleap_sum(year, base) ?
415: DAYSPERLYEAR :
416: DAYSPERNYEAR;
417: 418: 419: 420:
421: bot = ((yday + 11 - wday) %
422: DAYSPERWEEK) - 3;
423: 424: 425: 426:
427: top = bot -
428: (len % DAYSPERWEEK);
429: if (top < -3)
430: top += DAYSPERWEEK;
431: top += len;
432: if (yday >= top) {
433: ++base;
434: w = 1;
435: break;
436: }
437: if (yday >= bot) {
438: w = 1 + ((yday - bot) /
439: DAYSPERWEEK);
440: break;
441: }
442: --base;
443: yday += isleap_sum(year, base) ?
444: DAYSPERLYEAR :
445: DAYSPERNYEAR;
446: }
447: #ifdef XPG4_1994_04_09
448: if ((w == 52 &&
449: t->tm_mon == TM_JANUARY) ||
450: (w == 1 &&
451: t->tm_mon == TM_DECEMBER))
452: w = 53;
453: #endif
454: if (*format == 'V')
455: pt = _conv(w, "%02d",
456: pt, ptlim);
457: else if (*format == 'g') {
458: *warnp = IN_ALL;
459: pt = _yconv(year, base, 0, 1,
460: pt, ptlim);
461: } else pt = _yconv(year, base, 1, 1,
462: pt, ptlim);
463: }
464: continue;
465: case 'v':
466: 467: 468: 469: 470:
471: pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp);
472: continue;
473: case 'W':
474: pt = _conv((t->tm_yday + DAYSPERWEEK -
475: (t->tm_wday ?
476: (t->tm_wday - 1) :
477: (DAYSPERWEEK - 1))) / DAYSPERWEEK,
478: "%02d", pt, ptlim);
479: continue;
480: case 'w':
481: pt = _conv(t->tm_wday, "%d", pt, ptlim);
482: continue;
483: case 'X':
484: pt = _fmt(Locale->X_fmt, t, pt, ptlim, warnp);
485: continue;
486: case 'x':
487: {
488: int warn2 = IN_SOME;
489:
490: pt = _fmt(Locale->x_fmt, t, pt, ptlim, &warn2);
491: if (warn2 == IN_ALL)
492: warn2 = IN_THIS;
493: if (warn2 > *warnp)
494: *warnp = warn2;
495: }
496: continue;
497: case 'y':
498: *warnp = IN_ALL;
499: pt = _yconv(t->tm_year, TM_YEAR_BASE, 0, 1,
500: pt, ptlim);
501: continue;
502: case 'Y':
503: pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 1,
504: pt, ptlim);
505: continue;
506: case 'Z':
507: #ifdef TM_ZONE
508: if (t->TM_ZONE != NULL)
509: pt = _add(t->TM_ZONE, pt, ptlim);
510: else
511: #endif
512: if (t->tm_isdst >= 0)
513: if (dt_getsystz(&tz) >= 0) pt = _add(tz.tzname[t->tm_isdst != 0],
514: pt, ptlim);
515: 516: 517: 518: 519:
520: continue;
521: case 'z':
522: {
523: int diff;
524: char const * sign;
525:
526: if (t->tm_isdst < 0)
527: continue;
528: #ifdef TM_GMTOFF
529: diff = t->TM_GMTOFF;
530: #else
531: 532: 533: 534: 535: 536: 537: 538: 539: 540: 541: 542: 543: 544: 545: 546: 547: 548: 549:
550: if (t->tm_isdst == 0)
551: #ifdef USG_COMPAT
552: if (dt_getsystz(&tz) >= 0) diff = -tz.offset; else continue;
553: #else
554: continue;
555: #endif
556: else
557: #ifdef ALTZONE
558: diff = -altzone;
559: #else
560: continue;
561: #endif
562: #endif
563: if (diff < 0) {
564: sign = "-";
565: diff = -diff;
566: } else sign = "+";
567: pt = _add(sign, pt, ptlim);
568: diff /= SECSPERMIN;
569: diff = (diff / MINSPERHOUR) * 100 +
570: (diff % MINSPERHOUR);
571: pt = _conv(diff, "%04d", pt, ptlim);
572: }
573: continue;
574: case '+':
575: pt = _fmt(Locale->date_fmt, t, pt, ptlim,
576: warnp);
577: continue;
578: case '%':
579: 580: 581: 582: 583:
584: default:
585: break;
586: }
587: }
588: if (pt == ptlim)
589: break;
590: *pt++ = *format;
591: }
592: return pt;
593: }
594:
595: static char *
596: _conv(n, format, pt, ptlim)
597: const int n;
598: const char * const format;
599: char * const pt;
600: const char * const ptlim;
601: {
602: char buf[INT_STRLEN_MAXIMUM(int) + 1];
603:
604: (void) snprintf(buf, sizeof buf, format, n);
605: return _add(buf, pt, ptlim);
606: }
607:
608: static char *
609: _add(str, pt, ptlim)
610: const char * str;
611: char * pt;
612: const char * const ptlim;
613: {
614: while (pt < ptlim && (*pt = *str++) != '\0')
615: ++pt;
616: return pt;
617: }
618:
619: 620: 621: 622: 623: 624: 625:
626:
627: static char *
628: _yconv(a, b, convert_top, convert_yy, pt, ptlim)
629: const int a;
630: const int b;
631: const int convert_top;
632: const int convert_yy;
633: char * pt;
634: const char * const ptlim;
635: {
636: register int lead;
637: register int trail;
638:
639: #define DIVISOR 100
640: trail = a % DIVISOR + b % DIVISOR;
641: lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR;
642: trail %= DIVISOR;
643: if (trail < 0 && lead > 0) {
644: trail += DIVISOR;
645: --lead;
646: } else if (lead < 0 && trail > 0) {
647: trail -= DIVISOR;
648: ++lead;
649: }
650: if (convert_top) {
651: if (lead == 0 && trail < 0)
652: pt = _add("-0", pt, ptlim);
653: else pt = _conv(lead, "%02d", pt, ptlim);
654: }
655: if (convert_yy)
656: pt = _conv(((trail < 0) ? -trail : trail), "%02d", pt, ptlim);
657: return pt;
658: }
659:
660: #ifdef LOCALE_HOME
661: static struct lc_time_T *
662: _loc(void)
663: {
664: static const char locale_home[] = LOCALE_HOME;
665: static const char lc_time[] = "LC_TIME";
666: static char * locale_buf;
667:
668: int fd;
669: int oldsun;
670: int len;
671: char * lbuf;
672: char * nlbuf;
673: char * name;
674: char * p;
675: const char ** ap;
676: const char * plim;
677: char filename[FILENAME_MAX];
678: struct stat st;
679: size_t namesize;
680: size_t bufsize;
681:
682: 683: 684:
685: if (localebuf.mon[0])
686: return &localebuf;
687: name = setlocale(LC_TIME, (char *) NULL);
688: if (name == NULL || *name == '\0')
689: goto no_locale;
690: 691: 692:
693: lbuf = locale_buf;
694: if (lbuf != NULL && strcmp(name, lbuf) == 0) {
695: p = lbuf;
696: for (ap = (const char **) &localebuf;
697: ap < (const char **) (&localebuf + 1);
698: ++ap)
699: *ap = p += strlen(p) + 1;
700: return &localebuf;
701: }
702: 703: 704:
705: namesize = strlen(name) + 1;
706: if (sizeof filename <
707: ((sizeof locale_home) + namesize + (sizeof lc_time)))
708: goto no_locale;
709: oldsun = 0;
710: len = snprintf(filename, sizeof filename, "%s/%s/%s", locale_home,
711: name, lc_time);
712: if (len < 0 || len >= sizeof filename)
713: goto no_locale;
714: fd = open(filename, O_RDONLY);
715: if (fd < 0) {
716: 717: 718:
719: oldsun = 1;
720: len = snprintf(filename, sizeof filename, "%s/%s/%s",
721: locale_home, lc_time, name);
722: if (len < 0 || len >= sizeof filename)
723: goto no_locale;
724: fd = open(filename, O_RDONLY);
725: if (fd < 0)
726: goto no_locale;
727: }
728: if (fstat(fd, &st) != 0)
729: goto bad_locale;
730: if (st.st_size <= 0)
731: goto bad_locale;
732: bufsize = namesize + st.st_size;
733: locale_buf = NULL;
734: nlbuf = (lbuf == NULL) ? malloc(bufsize) : realloc(lbuf, bufsize);
735: if (nlbuf == NULL) {
736: if (lbuf)
737: free(lbuf);
738: lbuf = NULL;
739: goto bad_locale;
740: }
741: lbuf = nlbuf;
742: (void) strlcpy(lbuf, name, bufsize);
743: p = lbuf + namesize;
744: plim = p + st.st_size;
745: if (read(fd, p, (size_t) st.st_size) != st.st_size)
746: goto bad_lbuf;
747: if (close(fd) != 0)
748: goto bad_lbuf;
749: 750: 751:
752: if (plim[-1] != '\n')
753: goto bad_lbuf;
754: for (ap = (const char **) &localebuf;
755: ap < (const char **) (&localebuf + 1);
756: ++ap) {
757: if (p == plim)
758: goto bad_lbuf;
759: *ap = p;
760: while (*p != '\n')
761: ++p;
762: *p++ = '\0';
763: }
764: if (oldsun) {
765: 766: 767: 768: 769: 770: 771: 772:
773: localebuf.date_fmt = localebuf.c_fmt;
774: }
775: 776: 777:
778: locale_buf = lbuf;
779:
780: return &localebuf;
781:
782: bad_lbuf:
783: free(lbuf);
784: bad_locale:
785: (void) close(fd);
786: no_locale:
787: localebuf = C_time_locale;
788: locale_buf = NULL;
789: return &localebuf;
790: }
791: #endif