1 /* Convert a broken-down timestamp to a string. */
3 /* Copyright 1989 The Regents of the University of California.
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
9 1. Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
11 2. Redistributions in binary form must reproduce the above copyright
12 notice, this list of conditions and the following disclaimer in the
13 documentation and/or other materials provided with the distribution.
14 3. Neither the name of the University nor the names of its contributors
15 may be used to endorse or promote products derived from this software
16 without specific prior written permission.
18 THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
19 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 ** Based on the UCB version with the copyright notice appearing above.
33 ** This is ANSIish only when "multibyte character == plain character".
42 #ifndef DEPRECATE_TWO_DIGIT_YEARS
43 # define DEPRECATE_TWO_DIGIT_YEARS false
47 const char * mon[MONSPERYEAR];
48 const char * month[MONSPERYEAR];
49 const char * wday[DAYSPERWEEK];
50 const char * weekday[DAYSPERWEEK];
56 const char * date_fmt;
59 #define Locale (&C_time_locale)
61 static const struct lc_time_T C_time_locale = {
63 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
64 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
66 "January", "February", "March", "April", "May", "June",
67 "July", "August", "September", "October", "November", "December"
69 "Sun", "Mon", "Tue", "Wed",
72 "Sunday", "Monday", "Tuesday", "Wednesday",
73 "Thursday", "Friday", "Saturday"
81 ** C99 and later require this format.
82 ** Using just numbers (as here) makes Quakers happier;
83 ** it's also compatible with SVR4.
89 ** C99 and later require this format.
90 ** Previously this code used "%D %X", but we now conform to C99.
92 ** "%a %b %d %H:%M:%S %Y"
93 ** is used by Solaris 2.3.
104 "%a %b %e %H:%M:%S %Z %Y"
107 enum warn { IN_NONE, IN_SOME, IN_THIS, IN_ALL };
109 static char * _add(const char *, char *, const char *);
110 static char * _conv(int, const char *, char *, const char *);
111 static char * _fmt(const char *, const struct tm *, char *, const char *,
113 static char * _yconv(int, int, bool, bool, char *, char const *);
115 #ifndef YEAR_2000_NAME
116 #define YEAR_2000_NAME "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS"
117 #endif /* !defined YEAR_2000_NAME */
121 strftime_l(char *s, size_t maxsize, char const *format, struct tm const *t,
124 /* Just call strftime, as only the C locale is supported. */
125 return strftime(s, maxsize, format, t);
130 strftime(char *s, size_t maxsize, const char *format, const struct tm *t)
133 enum warn warn = IN_NONE;
136 p = _fmt(format, t, s, s + maxsize, &warn);
137 if (DEPRECATE_TWO_DIGIT_YEARS
138 && warn != IN_NONE && getenv(YEAR_2000_NAME)) {
139 fprintf(stderr, "\n");
140 fprintf(stderr, "strftime format \"%s\" ", format);
141 fprintf(stderr, "yields only two digits of years in ");
143 fprintf(stderr, "some locales");
144 else if (warn == IN_THIS)
145 fprintf(stderr, "the current locale");
146 else fprintf(stderr, "all locales");
147 fprintf(stderr, "\n");
149 if (p == s + maxsize)
156 _fmt(const char *format, const struct tm *t, char *pt,
157 const char *ptlim, enum warn *warnp)
159 for ( ; *format; ++format) {
160 if (*format == '%') {
167 pt = _add((t->tm_wday < 0 ||
168 t->tm_wday >= DAYSPERWEEK) ?
169 "?" : Locale->weekday[t->tm_wday],
173 pt = _add((t->tm_wday < 0 ||
174 t->tm_wday >= DAYSPERWEEK) ?
175 "?" : Locale->wday[t->tm_wday],
179 pt = _add((t->tm_mon < 0 ||
180 t->tm_mon >= MONSPERYEAR) ?
181 "?" : Locale->month[t->tm_mon],
186 pt = _add((t->tm_mon < 0 ||
187 t->tm_mon >= MONSPERYEAR) ?
188 "?" : Locale->mon[t->tm_mon],
193 ** %C used to do a...
194 ** _fmt("%a %b %e %X %Y", t);
195 ** ...whereas now POSIX 1003.2 calls for
196 ** something completely different.
199 pt = _yconv(t->tm_year, TM_YEAR_BASE,
200 true, false, pt, ptlim);
204 enum warn warn2 = IN_SOME;
206 pt = _fmt(Locale->c_fmt, t, pt, ptlim, &warn2);
214 pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp);
217 pt = _conv(t->tm_mday, "%02d", pt, ptlim);
222 ** Locale modifiers of C99 and later.
224 ** %Ec %EC %Ex %EX %Ey %EY
225 ** %Od %oe %OH %OI %Om %OM
226 ** %OS %Ou %OU %OV %Ow %OW %Oy
227 ** are supposed to provide alternate
232 pt = _conv(t->tm_mday, "%2d", pt, ptlim);
235 pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp);
238 pt = _conv(t->tm_hour, "%02d", pt, ptlim);
241 pt = _conv((t->tm_hour % 12) ?
242 (t->tm_hour % 12) : 12,
246 pt = _conv(t->tm_yday + 1, "%03d", pt, ptlim);
250 ** This used to be...
251 ** _conv(t->tm_hour % 12 ?
252 ** t->tm_hour % 12 : 12, 2, ' ');
253 ** ...and has been changed to the below to
254 ** match SunOS 4.1.1 and Arnold Robbins'
255 ** strftime version 3.0. That is, "%k" and
256 ** "%l" have been swapped.
259 pt = _conv(t->tm_hour, "%2d", pt, ptlim);
264 ** After all this time, still unclaimed!
266 pt = _add("kitchen sink", pt, ptlim);
268 #endif /* defined KITCHEN_SINK */
271 ** This used to be...
272 ** _conv(t->tm_hour, 2, ' ');
273 ** ...and has been changed to the below to
274 ** match SunOS 4.1.1 and Arnold Robbin's
275 ** strftime version 3.0. That is, "%k" and
276 ** "%l" have been swapped.
279 pt = _conv((t->tm_hour % 12) ?
280 (t->tm_hour % 12) : 12,
284 pt = _conv(t->tm_min, "%02d", pt, ptlim);
287 pt = _conv(t->tm_mon + 1, "%02d", pt, ptlim);
290 pt = _add("\n", pt, ptlim);
293 pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ?
299 pt = _fmt("%H:%M", t, pt, ptlim, warnp);
302 pt = _fmt("%I:%M:%S %p", t, pt, ptlim, warnp);
305 pt = _conv(t->tm_sec, "%02d", pt, ptlim);
310 char buf[INT_STRLEN_MAXIMUM(
316 if (TYPE_SIGNED(time_t))
317 sprintf(buf, "%"PRIdMAX,
319 else sprintf(buf, "%"PRIuMAX,
321 pt = _add(buf, pt, ptlim);
325 pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp);
328 pt = _add("\t", pt, ptlim);
331 pt = _conv((t->tm_yday + DAYSPERWEEK -
332 t->tm_wday) / DAYSPERWEEK,
337 ** From Arnold Robbins' strftime version 3.0:
338 ** "ISO 8601: Weekday as a decimal number
342 pt = _conv((t->tm_wday == 0) ?
343 DAYSPERWEEK : t->tm_wday,
346 case 'V': /* ISO 8601 week number */
347 case 'G': /* ISO 8601 year (four digits) */
348 case 'g': /* ISO 8601 year (two digits) */
350 ** From Arnold Robbins' strftime version 3.0: "the week number of the
351 ** year (the first Monday as the first day of week 1) as a decimal number
355 ** From <https://www.cl.cam.ac.uk/~mgk25/iso-time.html> by Markus Kuhn:
356 ** "Week 01 of a year is per definition the first week which has the
357 ** Thursday in this year, which is equivalent to the week which contains
358 ** the fourth day of January. In other words, the first week of a new year
359 ** is the week which has the majority of its days in the new year. Week 01
360 ** might also contain days from the previous year and the week before week
361 ** 01 of a year is the last week (52 or 53) of the previous year even if
362 ** it contains days from the new year. A week starts with Monday (day 1)
363 ** and ends with Sunday (day 7). For example, the first week of the year
364 ** 1997 lasts from 1996-12-30 to 1997-01-05..."
383 len = isleap_sum(year, base) ?
387 ** What yday (-3 ... 3) does
388 ** the ISO year begin on?
390 bot = ((yday + 11 - wday) %
393 ** What yday does the NEXT
394 ** ISO year begin on?
407 w = 1 + ((yday - bot) /
412 yday += isleap_sum(year, base) ?
416 #ifdef XPG4_1994_04_09
418 t->tm_mon == TM_JANUARY) ||
420 t->tm_mon == TM_DECEMBER))
422 #endif /* defined XPG4_1994_04_09 */
424 pt = _conv(w, "%02d",
426 else if (*format == 'g') {
428 pt = _yconv(year, base,
431 } else pt = _yconv(year, base,
438 ** From Arnold Robbins' strftime version 3.0:
439 ** "date as dd-bbb-YYYY"
442 pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp);
445 pt = _conv((t->tm_yday + DAYSPERWEEK -
448 (DAYSPERWEEK - 1))) / DAYSPERWEEK,
452 pt = _conv(t->tm_wday, "%d", pt, ptlim);
455 pt = _fmt(Locale->X_fmt, t, pt, ptlim, warnp);
459 enum warn warn2 = IN_SOME;
461 pt = _fmt(Locale->x_fmt, t, pt, ptlim, &warn2);
470 pt = _yconv(t->tm_year, TM_YEAR_BASE,
475 pt = _yconv(t->tm_year, TM_YEAR_BASE,
481 pt = _add(t->TM_ZONE, pt, ptlim);
483 if (t->tm_isdst >= 0)
484 pt = _add(tzname[t->tm_isdst != 0],
488 ** C99 and later say that %Z must be
489 ** replaced by the empty string if the
490 ** time zone is not determinable.
494 #if defined TM_GMTOFF || USG_COMPAT || defined ALTZONE
504 ** C99 and later say that the UT offset must
505 ** be computed by looking only at
506 ** tm_isdst. This requirement is
507 ** incorrect, since it means the code
508 ** must rely on magic (in this case
509 ** altzone and timezone), and the
510 ** magic might not have the correct
511 ** offset. Doing things correctly is
512 ** tricky and requires disobeying the standard;
513 ** see GNU C strftime for details.
514 ** For now, punt and conform to the
515 ** standard, even though it's incorrect.
517 ** C99 and later say that %z must be replaced by
518 ** the empty string if the time zone is not
519 ** determinable, so output nothing if the
520 ** appropriate variables are not available.
524 if (t->tm_isdst == 0)
540 negative = t->TM_ZONE[0] == '-';
542 negative = t->tm_isdst < 0;
544 if (tzname[t->tm_isdst != 0][0] == '-')
553 pt = _add(sign, pt, ptlim);
555 diff = (diff / MINSPERHOUR) * 100 +
556 (diff % MINSPERHOUR);
557 pt = _conv(diff, "%04d", pt, ptlim);
562 pt = _fmt(Locale->date_fmt, t, pt, ptlim,
567 ** X311J/88-090 (4.12.3.5): if conversion char is
568 ** undefined, behavior is undefined. Print out the
569 ** character itself as printf(3) also does.
583 _conv(int n, const char *format, char *pt, const char *ptlim)
585 char buf[INT_STRLEN_MAXIMUM(int) + 1];
587 sprintf(buf, format, n);
588 return _add(buf, pt, ptlim);
592 _add(const char *str, char *pt, const char *ptlim)
594 while (pt < ptlim && (*pt = *str++) != '\0')
600 ** POSIX and the C Standard are unclear or inconsistent about
601 ** what %C and %y do if the year is negative or exceeds 9999.
602 ** Use the convention that %C concatenated with %y yields the
603 ** same output as %Y, and that %Y contains at least 4 bytes,
604 ** with more only if necessary.
608 _yconv(int a, int b, bool convert_top, bool convert_yy,
609 char *pt, const char *ptlim)
615 trail = a % DIVISOR + b % DIVISOR;
616 lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR;
618 if (trail < 0 && lead > 0) {
621 } else if (lead < 0 && trail > 0) {
626 if (lead == 0 && trail < 0)
627 pt = _add("-0", pt, ptlim);
628 else pt = _conv(lead, "%02d", pt, ptlim);
631 pt = _conv(((trail < 0) ? -trail : trail), "%02d", pt, ptlim);