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