Annotation of src/lib/libc/time/strftime.c, Revision 1.17.4.1
1.17.4.1! liamjfoy 1: /* $NetBSD$ */
1.1 mrg 2:
1.3 christos 3: #include <sys/cdefs.h>
1.1 mrg 4: #if defined(LIBC_SCCS) && !defined(lint)
1.3 christos 5: #if 0
1.13 kleink 6: static char elsieid[] = "@(#)strftime.c 7.64";
1.3 christos 7: #else
1.17.4.1! liamjfoy 8: __RCSID("$NetBSD$");
1.3 christos 9: #endif
1.1 mrg 10: #endif /* LIBC_SCCS and not lint */
11:
1.4 jtc 12: #include "namespace.h"
1.12 kleink 13:
14: /*
15: ** Based on the UCB version with the ID appearing below.
16: ** This is ANSIish only when "multibyte character == plain character".
17: */
18:
1.11 taca 19: #include "private.h"
1.12 kleink 20:
1.16 kleink 21: /*
22: ** We don't use these extensions in strftime operation even when
23: ** supported by the local tzcode configuration. A strictly
24: ** conforming C application may leave them in undefined state.
25: */
26:
1.15 kleink 27: #ifdef _LIBC
28: #undef TM_ZONE
1.16 kleink 29: #undef TM_GMTOFF
1.15 kleink 30: #endif
31:
1.12 kleink 32: /*
1.14 itojun 33: ** Copyright (c) 1989, 1993
34: ** The Regents of the University of California. All rights reserved.
1.12 kleink 35: **
1.14 itojun 36: ** Redistribution and use in source and binary forms, with or without
37: ** modification, are permitted provided that the following conditions
38: ** are met:
39: ** 1. Redistributions of source code must retain the above copyright
40: ** notice, this list of conditions and the following disclaimer.
41: ** 2. Redistributions in binary form must reproduce the above copyright
42: ** notice, this list of conditions and the following disclaimer in the
43: ** documentation and/or other materials provided with the distribution.
44: ** 3. All advertising materials mentioning features or use of this software
45: ** must display the following acknowledgement:
46: ** This product includes software developed by the University of
47: ** California, Berkeley and its contributors.
48: ** 4. Neither the name of the University nor the names of its contributors
49: ** may be used to endorse or promote products derived from this software
50: ** without specific prior written permission.
51: **
52: ** THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
53: ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54: ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55: ** ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56: ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57: ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58: ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59: ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60: ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61: ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
62: ** SUCH DAMAGE.
1.12 kleink 63: */
64:
65: #ifndef LIBC_SCCS
66: #ifndef lint
67: static const char sccsid[] = "@(#)strftime.c 5.4 (Berkeley) 3/14/89";
68: #endif /* !defined lint */
69: #endif /* !defined LIBC_SCCS */
70:
71: #include "tzfile.h"
72: #include "fcntl.h"
73: #include "locale.h"
74:
75: #include "sys/localedef.h"
76: #define Locale _CurrentTimeLocale
77:
78: static char * _add P((const char *, char *, const char *));
79: static char * _conv P((int, const char *, char *, const char *));
80: static char * _fmt P((const char *, const struct tm *, char *, const char *, int *));
81:
82: #define NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU
83:
84: #ifndef YEAR_2000_NAME
85: #define YEAR_2000_NAME "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS"
86: #endif /* !defined YEAR_2000_NAME */
87:
88:
89: #define IN_NONE 0
90: #define IN_SOME 1
91: #define IN_THIS 2
92: #define IN_ALL 3
1.1 mrg 93:
94: size_t
95: strftime(s, maxsize, format, t)
1.12 kleink 96: char * const s;
97: const size_t maxsize;
98: const char * const format;
99: const struct tm * const t;
1.1 mrg 100: {
1.12 kleink 101: char * p;
102: int warn;
1.5 kleink 103:
1.1 mrg 104: tzset();
1.12 kleink 105: warn = IN_NONE;
106: p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize, &warn);
107: #ifndef NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU
108: if (warn != IN_NONE && getenv(YEAR_2000_NAME) != NULL) {
109: (void) fprintf(stderr, "\n");
110: if (format == NULL)
111: (void) fprintf(stderr, "NULL strftime format ");
112: else (void) fprintf(stderr, "strftime format \"%s\" ",
113: format);
114: (void) fprintf(stderr, "yields only two digits of years in ");
115: if (warn == IN_SOME)
116: (void) fprintf(stderr, "some locales");
117: else if (warn == IN_THIS)
118: (void) fprintf(stderr, "the current locale");
119: else (void) fprintf(stderr, "all locales");
120: (void) fprintf(stderr, "\n");
121: }
122: #endif /* !defined NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU */
123: if (p == s + maxsize)
124: return 0;
125: *p = '\0';
126: return p - s;
1.1 mrg 127: }
128:
1.12 kleink 129: static char *
130: _fmt(format, t, pt, ptlim, warnp)
131: const char * format;
132: const struct tm * const t;
133: char * pt;
134: const char * const ptlim;
135: int * warnp;
1.1 mrg 136: {
1.12 kleink 137: for ( ; *format; ++format) {
1.1 mrg 138: if (*format == '%') {
1.12 kleink 139: label:
140: switch (*++format) {
1.1 mrg 141: case '\0':
142: --format;
143: break;
144: case 'A':
1.12 kleink 145: pt = _add((t->tm_wday < 0 ||
146: t->tm_wday >= DAYSPERWEEK) ?
147: "?" : Locale->day[t->tm_wday],
148: pt, ptlim);
1.1 mrg 149: continue;
150: case 'a':
1.12 kleink 151: pt = _add((t->tm_wday < 0 ||
152: t->tm_wday >= DAYSPERWEEK) ?
153: "?" : Locale->abday[t->tm_wday],
154: pt, ptlim);
1.1 mrg 155: continue;
156: case 'B':
1.12 kleink 157: pt = _add((t->tm_mon < 0 ||
158: t->tm_mon >= MONSPERYEAR) ?
159: "?" : Locale->mon[t->tm_mon],
160: pt, ptlim);
1.1 mrg 161: continue;
162: case 'b':
163: case 'h':
1.12 kleink 164: pt = _add((t->tm_mon < 0 ||
165: t->tm_mon >= MONSPERYEAR) ?
166: "?" : Locale->abmon[t->tm_mon],
167: pt, ptlim);
1.1 mrg 168: continue;
169: case 'C':
1.12 kleink 170: /*
171: ** %C used to do a...
172: ** _fmt("%a %b %e %X %Y", t);
173: ** ...whereas now POSIX 1003.2 calls for
174: ** something completely different.
175: ** (ado, 1993-05-24)
176: */
177: pt = _conv((t->tm_year + TM_YEAR_BASE) / 100,
178: "%02d", pt, ptlim);
1.1 mrg 179: continue;
180: case 'c':
1.12 kleink 181: {
182: int warn2 = IN_SOME;
183:
1.17 christos 184: pt = _fmt(Locale->d_t_fmt, t, pt, ptlim, &warn2);
1.12 kleink 185: if (warn2 == IN_ALL)
186: warn2 = IN_THIS;
187: if (warn2 > *warnp)
188: *warnp = warn2;
189: }
1.1 mrg 190: continue;
191: case 'D':
1.12 kleink 192: pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp);
1.1 mrg 193: continue;
194: case 'd':
1.12 kleink 195: pt = _conv(t->tm_mday, "%02d", pt, ptlim);
1.1 mrg 196: continue;
1.12 kleink 197: case 'E':
198: case 'O':
199: /*
200: ** C99 locale modifiers.
201: ** The sequences
202: ** %Ec %EC %Ex %EX %Ey %EY
203: ** %Od %oe %OH %OI %Om %OM
204: ** %OS %Ou %OU %OV %Ow %OW %Oy
205: ** are supposed to provide alternate
206: ** representations.
207: */
208: goto label;
1.1 mrg 209: case 'e':
1.12 kleink 210: pt = _conv(t->tm_mday, "%2d", pt, ptlim);
1.10 kleink 211: continue;
212: case 'F':
1.12 kleink 213: pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp);
1.1 mrg 214: continue;
215: case 'H':
1.12 kleink 216: pt = _conv(t->tm_hour, "%02d", pt, ptlim);
1.1 mrg 217: continue;
218: case 'I':
1.12 kleink 219: pt = _conv((t->tm_hour % 12) ?
220: (t->tm_hour % 12) : 12,
221: "%02d", pt, ptlim);
1.1 mrg 222: continue;
223: case 'j':
1.12 kleink 224: pt = _conv(t->tm_yday + 1, "%03d", pt, ptlim);
1.1 mrg 225: continue;
226: case 'k':
1.12 kleink 227: /*
228: ** This used to be...
229: ** _conv(t->tm_hour % 12 ?
230: ** t->tm_hour % 12 : 12, 2, ' ');
231: ** ...and has been changed to the below to
232: ** match SunOS 4.1.1 and Arnold Robbins'
233: ** strftime version 3.0. That is, "%k" and
234: ** "%l" have been swapped.
235: ** (ado, 1993-05-24)
236: */
237: pt = _conv(t->tm_hour, "%2d", pt, ptlim);
238: continue;
239: #ifdef KITCHEN_SINK
240: case 'K':
241: /*
242: ** After all this time, still unclaimed!
243: */
244: pt = _add("kitchen sink", pt, ptlim);
1.1 mrg 245: continue;
1.12 kleink 246: #endif /* defined KITCHEN_SINK */
1.1 mrg 247: case 'l':
1.12 kleink 248: /*
249: ** This used to be...
250: ** _conv(t->tm_hour, 2, ' ');
251: ** ...and has been changed to the below to
252: ** match SunOS 4.1.1 and Arnold Robbin's
253: ** strftime version 3.0. That is, "%k" and
254: ** "%l" have been swapped.
255: ** (ado, 1993-05-24)
256: */
257: pt = _conv((t->tm_hour % 12) ?
258: (t->tm_hour % 12) : 12,
259: "%2d", pt, ptlim);
1.1 mrg 260: continue;
261: case 'M':
1.12 kleink 262: pt = _conv(t->tm_min, "%02d", pt, ptlim);
1.1 mrg 263: continue;
264: case 'm':
1.12 kleink 265: pt = _conv(t->tm_mon + 1, "%02d", pt, ptlim);
1.1 mrg 266: continue;
267: case 'n':
1.12 kleink 268: pt = _add("\n", pt, ptlim);
1.1 mrg 269: continue;
270: case 'p':
1.12 kleink 271: pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ?
272: Locale->am_pm[1] :
273: Locale->am_pm[0],
274: pt, ptlim);
1.1 mrg 275: continue;
276: case 'R':
1.12 kleink 277: pt = _fmt("%H:%M", t, pt, ptlim, warnp);
1.1 mrg 278: continue;
279: case 'r':
1.17.4.1! liamjfoy 280: pt = _fmt(Locale->t_fmt_ampm, t, pt, ptlim,
! 281: warnp);
1.1 mrg 282: continue;
283: case 'S':
1.12 kleink 284: pt = _conv(t->tm_sec, "%02d", pt, ptlim);
1.1 mrg 285: continue;
286: case 's':
1.12 kleink 287: {
288: struct tm tm;
289: char buf[INT_STRLEN_MAXIMUM(
290: time_t) + 1];
291: time_t mkt;
292:
293: tm = *t;
294: mkt = mktime(&tm);
295: /* CONSTCOND */
296: if (TYPE_SIGNED(time_t))
297: (void) sprintf(buf, "%ld",
298: (long) mkt);
299: else (void) sprintf(buf, "%lu",
300: (unsigned long) mkt);
301: pt = _add(buf, pt, ptlim);
302: }
1.1 mrg 303: continue;
304: case 'T':
1.12 kleink 305: pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp);
1.1 mrg 306: continue;
307: case 't':
1.12 kleink 308: pt = _add("\t", pt, ptlim);
1.1 mrg 309: continue;
310: case 'U':
1.12 kleink 311: pt = _conv((t->tm_yday + DAYSPERWEEK -
312: t->tm_wday) / DAYSPERWEEK,
313: "%02d", pt, ptlim);
1.1 mrg 314: continue;
315: case 'u':
1.12 kleink 316: /*
317: ** From Arnold Robbins' strftime version 3.0:
318: ** "ISO 8601: Weekday as a decimal number
319: ** [1 (Monday) - 7]"
320: ** (ado, 1993-05-24)
321: */
322: pt = _conv((t->tm_wday == 0) ?
323: DAYSPERWEEK : t->tm_wday,
324: "%d", pt, ptlim);
1.1 mrg 325: continue;
1.8 augustss 326: case 'V': /* ISO 8601 week number */
327: case 'G': /* ISO 8601 year (four digits) */
328: case 'g': /* ISO 8601 year (two digits) */
329: /*
330: ** From Arnold Robbins' strftime version 3.0: "the week number of the
331: ** year (the first Monday as the first day of week 1) as a decimal number
332: ** (01-53)."
333: ** (ado, 1993-05-24)
334: **
335: ** From "http://www.ft.uni-erlangen.de/~mskuhn/iso-time.html" by Markus Kuhn:
336: ** "Week 01 of a year is per definition the first week which has the
337: ** Thursday in this year, which is equivalent to the week which contains
338: ** the fourth day of January. In other words, the first week of a new year
339: ** is the week which has the majority of its days in the new year. Week 01
340: ** might also contain days from the previous year and the week before week
341: ** 01 of a year is the last week (52 or 53) of the previous year even if
342: ** it contains days from the new year. A week starts with Monday (day 1)
343: ** and ends with Sunday (day 7). For example, the first week of the year
344: ** 1997 lasts from 1996-12-30 to 1997-01-05..."
345: ** (ado, 1996-01-02)
346: */
1.1 mrg 347: {
1.8 augustss 348: int year;
349: int yday;
350: int wday;
351: int w;
352:
353: year = t->tm_year + TM_YEAR_BASE;
354: yday = t->tm_yday;
355: wday = t->tm_wday;
356: for ( ; ; ) {
357: int len;
358: int bot;
359: int top;
360:
361: len = isleap(year) ?
362: DAYSPERLYEAR :
363: DAYSPERNYEAR;
364: /*
365: ** What yday (-3 ... 3) does
366: ** the ISO year begin on?
367: */
368: bot = ((yday + 11 - wday) %
369: DAYSPERWEEK) - 3;
370: /*
371: ** What yday does the NEXT
372: ** ISO year begin on?
373: */
374: top = bot -
375: (len % DAYSPERWEEK);
376: if (top < -3)
377: top += DAYSPERWEEK;
378: top += len;
379: if (yday >= top) {
380: ++year;
381: w = 1;
382: break;
383: }
384: if (yday >= bot) {
385: w = 1 + ((yday - bot) /
386: DAYSPERWEEK);
387: break;
388: }
389: --year;
390: yday += isleap(year) ?
391: DAYSPERLYEAR :
392: DAYSPERNYEAR;
393: }
394: #ifdef XPG4_1994_04_09
395: if ((w == 52
396: && t->tm_mon == TM_JANUARY)
397: || (w == 1
398: && t->tm_mon == TM_DECEMBER))
399: w = 53;
400: #endif /* defined XPG4_1994_04_09 */
1.12 kleink 401: if (*format == 'V')
402: pt = _conv(w, "%02d",
403: pt, ptlim);
404: else if (*format == 'g') {
405: *warnp = IN_ALL;
406: pt = _conv(year % 100, "%02d",
407: pt, ptlim);
408: } else pt = _conv(year, "%04d",
409: pt, ptlim);
1.1 mrg 410: }
411: continue;
1.12 kleink 412: case 'v':
413: /*
414: ** From Arnold Robbins' strftime version 3.0:
415: ** "date as dd-bbb-YYYY"
416: ** (ado, 1993-05-24)
417: */
418: pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp);
419: continue;
1.1 mrg 420: case 'W':
1.12 kleink 421: pt = _conv((t->tm_yday + DAYSPERWEEK -
422: (t->tm_wday ?
423: (t->tm_wday - 1) :
424: (DAYSPERWEEK - 1))) / DAYSPERWEEK,
425: "%02d", pt, ptlim);
1.1 mrg 426: continue;
427: case 'w':
1.12 kleink 428: pt = _conv(t->tm_wday, "%d", pt, ptlim);
429: continue;
430: case 'X':
431: pt = _fmt(Locale->t_fmt, t, pt, ptlim, warnp);
1.1 mrg 432: continue;
433: case 'x':
1.12 kleink 434: {
435: int warn2 = IN_SOME;
436:
437: pt = _fmt(Locale->d_fmt, t, pt, ptlim, &warn2);
438: if (warn2 == IN_ALL)
439: warn2 = IN_THIS;
440: if (warn2 > *warnp)
441: *warnp = warn2;
442: }
1.1 mrg 443: continue;
444: case 'y':
1.12 kleink 445: *warnp = IN_ALL;
446: pt = _conv((t->tm_year + TM_YEAR_BASE) % 100,
447: "%02d", pt, ptlim);
1.1 mrg 448: continue;
449: case 'Y':
1.12 kleink 450: pt = _conv(t->tm_year + TM_YEAR_BASE, "%04d",
451: pt, ptlim);
1.1 mrg 452: continue;
453: case 'Z':
1.11 taca 454: #ifdef TM_ZONE
1.12 kleink 455: if (t->TM_ZONE != NULL)
456: pt = _add(t->TM_ZONE, pt, ptlim);
457: else
458: #endif /* defined TM_ZONE */
459: if (t->tm_isdst >= 0)
460: pt = _add(tzname[t->tm_isdst != 0],
461: pt, ptlim);
462: /*
463: ** C99 says that %Z must be replaced by the
464: ** empty string if the time zone is not
465: ** determinable.
466: */
467: continue;
468: case 'z':
469: {
470: int diff;
471: char const * sign;
472:
473: if (t->tm_isdst < 0)
474: continue;
475: #ifdef TM_GMTOFF
476: diff = (int)t->TM_GMTOFF;
477: #else /* !defined TM_GMTOFF */
478: /*
479: ** C99 says that the UTC offset must
480: ** be computed by looking only at
481: ** tm_isdst. This requirement is
482: ** incorrect, since it means the code
483: ** must rely on magic (in this case
484: ** altzone and timezone), and the
485: ** magic might not have the correct
486: ** offset. Doing things correctly is
487: ** tricky and requires disobeying C99;
488: ** see GNU C strftime for details.
489: ** For now, punt and conform to the
490: ** standard, even though it's incorrect.
491: **
492: ** C99 says that %z must be replaced by the
493: ** empty string if the time zone is not
494: ** determinable, so output nothing if the
495: ** appropriate variables are not available.
496: */
1.16 kleink 497: #ifndef STD_INSPIRED
1.12 kleink 498: if (t->tm_isdst == 0)
499: #ifdef USG_COMPAT
500: diff = -timezone;
1.13 kleink 501: #else /* !defined USG_COMPAT */
1.12 kleink 502: continue;
503: #endif /* !defined USG_COMPAT */
504: else
505: #ifdef ALTZONE
506: diff = -altzone;
507: #else /* !defined ALTZONE */
508: continue;
509: #endif /* !defined ALTZONE */
1.16 kleink 510: #else /* defined STD_INSPIRED */
511: {
512: struct tm tmp;
513: time_t lct, gct;
514:
515: /*
516: ** Get calendar time from t
517: ** being treated as local.
518: */
519: tmp = *t; /* mktime discards const */
520: lct = mktime(&tmp);
521:
522: if (lct == (time_t)-1)
523: continue;
524:
525: /*
526: ** Get calendar time from t
527: ** being treated as GMT.
528: **/
529: tmp = *t; /* mktime discards const */
530: gct = timegm(&tmp);
531:
532: if (gct == (time_t)-1)
533: continue;
534:
535: /* LINTED difference will fit int */
536: diff = (intmax_t)gct - (intmax_t)lct;
537: }
538: #endif /* defined STD_INSPIRED */
1.12 kleink 539: #endif /* !defined TM_GMTOFF */
540: if (diff < 0) {
541: sign = "-";
542: diff = -diff;
543: } else sign = "+";
544: pt = _add(sign, pt, ptlim);
545: diff /= 60;
546: pt = _conv((diff/60)*100 + diff%60,
547: "%04d", pt, ptlim);
548: }
1.1 mrg 549: continue;
1.12 kleink 550: #if 0
551: case '+':
552: pt = _fmt(Locale->date_fmt, t, pt, ptlim,
553: warnp);
554: continue;
555: #endif
1.1 mrg 556: case '%':
557: /*
1.12 kleink 558: ** X311J/88-090 (4.12.3.5): if conversion char is
559: ** undefined, behavior is undefined. Print out the
560: ** character itself as printf(3) also does.
561: */
1.1 mrg 562: default:
563: break;
564: }
565: }
1.12 kleink 566: if (pt == ptlim)
567: break;
568: *pt++ = *format;
1.1 mrg 569: }
1.12 kleink 570: return pt;
1.1 mrg 571: }
572:
1.12 kleink 573: static char *
574: _conv(n, format, pt, ptlim)
575: const int n;
576: const char * const format;
577: char * const pt;
578: const char * const ptlim;
1.1 mrg 579: {
1.12 kleink 580: char buf[INT_STRLEN_MAXIMUM(int) + 1];
1.1 mrg 581:
1.12 kleink 582: (void) sprintf(buf, format, n);
583: return _add(buf, pt, ptlim);
1.1 mrg 584: }
585:
1.12 kleink 586: static char *
1.5 kleink 587: _add(str, pt, ptlim)
1.12 kleink 588: const char * str;
589: char * pt;
590: const char * const ptlim;
1.1 mrg 591: {
1.12 kleink 592: while (pt < ptlim && (*pt = *str++) != '\0')
593: ++pt;
594: return pt;
1.1 mrg 595: }
CVSweb <webmaster@jp.NetBSD.org>