1 /* Convert a broken-down time stamp 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".
43 const char * mon[MONSPERYEAR];
44 const char * month[MONSPERYEAR];
45 const char * wday[DAYSPERWEEK];
46 const char * weekday[DAYSPERWEEK];
52 const char * date_fmt;
55 #define Locale (&C_time_locale)
57 static const struct lc_time_T C_time_locale = {
59 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
60 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
62 "January", "February", "March", "April", "May", "June",
63 "July", "August", "September", "October", "November", "December"
65 "Sun", "Mon", "Tue", "Wed",
68 "Sunday", "Monday", "Tuesday", "Wednesday",
69 "Thursday", "Friday", "Saturday"
77 ** C99 requires this format.
78 ** Using just numbers (as here) makes Quakers happier;
79 ** it's also compatible with SVR4.
85 ** C99 requires this format.
86 ** Previously this code used "%D %X", but we now conform to C99.
88 ** "%a %b %d %H:%M:%S %Y"
89 ** is used by Solaris 2.3.
100 "%a %b %e %H:%M:%S %Z %Y"
103 static char * _add(const char *, char *, const char *);
104 static char * _conv(int, const char *, char *, const char *);
105 static char * _fmt(const char *, const struct tm *, char *, const char *,
107 static char * _yconv(int, int, bool, bool, char *, char const *);
109 #if !HAVE_POSIX_DECLS
110 extern char * tzname[];
113 #ifndef YEAR_2000_NAME
114 #define YEAR_2000_NAME "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS"
115 #endif /* !defined YEAR_2000_NAME */
124 strftime_l(char *s, size_t maxsize, char const *format, struct tm const *t,
127 /* Just call strftime, as only the C locale is supported. */
128 return strftime(s, maxsize, format, t);
133 strftime(char *s, size_t maxsize, const char *format, const struct tm *t)
140 p = _fmt(format, t, s, s + maxsize, &warn);
141 #ifndef NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU
142 if (warn != IN_NONE && getenv(YEAR_2000_NAME) != NULL) {
143 fprintf(stderr, "\n");
144 fprintf(stderr, "strftime format \"%s\" ", format);
145 fprintf(stderr, "yields only two digits of years in ");
147 fprintf(stderr, "some locales");
148 else if (warn == IN_THIS)
149 fprintf(stderr, "the current locale");
150 else fprintf(stderr, "all locales");
151 fprintf(stderr, "\n");
153 #endif /* !defined NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU */
154 if (p == s + maxsize)
161 _fmt(const char *format, const struct tm *t, char *pt,
162 const char *ptlim, int *warnp)
164 for ( ; *format; ++format) {
165 if (*format == '%') {
172 pt = _add((t->tm_wday < 0 ||
173 t->tm_wday >= DAYSPERWEEK) ?
174 "?" : Locale->weekday[t->tm_wday],
178 pt = _add((t->tm_wday < 0 ||
179 t->tm_wday >= DAYSPERWEEK) ?
180 "?" : Locale->wday[t->tm_wday],
184 pt = _add((t->tm_mon < 0 ||
185 t->tm_mon >= MONSPERYEAR) ?
186 "?" : Locale->month[t->tm_mon],
191 pt = _add((t->tm_mon < 0 ||
192 t->tm_mon >= MONSPERYEAR) ?
193 "?" : Locale->mon[t->tm_mon],
198 ** %C used to do a...
199 ** _fmt("%a %b %e %X %Y", t);
200 ** ...whereas now POSIX 1003.2 calls for
201 ** something completely different.
204 pt = _yconv(t->tm_year, TM_YEAR_BASE,
205 true, false, pt, ptlim);
211 pt = _fmt(Locale->c_fmt, t, pt, ptlim, &warn2);
219 pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp);
222 pt = _conv(t->tm_mday, "%02d", pt, ptlim);
227 ** C99 locale modifiers.
229 ** %Ec %EC %Ex %EX %Ey %EY
230 ** %Od %oe %OH %OI %Om %OM
231 ** %OS %Ou %OU %OV %Ow %OW %Oy
232 ** are supposed to provide alternate
237 pt = _conv(t->tm_mday, "%2d", pt, ptlim);
240 pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp);
243 pt = _conv(t->tm_hour, "%02d", pt, ptlim);
246 pt = _conv((t->tm_hour % 12) ?
247 (t->tm_hour % 12) : 12,
251 pt = _conv(t->tm_yday + 1, "%03d", pt, ptlim);
255 ** This used to be...
256 ** _conv(t->tm_hour % 12 ?
257 ** t->tm_hour % 12 : 12, 2, ' ');
258 ** ...and has been changed to the below to
259 ** match SunOS 4.1.1 and Arnold Robbins'
260 ** strftime version 3.0. That is, "%k" and
261 ** "%l" have been swapped.
264 pt = _conv(t->tm_hour, "%2d", pt, ptlim);
269 ** After all this time, still unclaimed!
271 pt = _add("kitchen sink", pt, ptlim);
273 #endif /* defined KITCHEN_SINK */
276 ** This used to be...
277 ** _conv(t->tm_hour, 2, ' ');
278 ** ...and has been changed to the below to
279 ** match SunOS 4.1.1 and Arnold Robbin's
280 ** strftime version 3.0. That is, "%k" and
281 ** "%l" have been swapped.
284 pt = _conv((t->tm_hour % 12) ?
285 (t->tm_hour % 12) : 12,
289 pt = _conv(t->tm_min, "%02d", pt, ptlim);
292 pt = _conv(t->tm_mon + 1, "%02d", pt, ptlim);
295 pt = _add("\n", pt, ptlim);
298 pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ?
304 pt = _fmt("%H:%M", t, pt, ptlim, warnp);
307 pt = _fmt("%I:%M:%S %p", t, pt, ptlim, warnp);
310 pt = _conv(t->tm_sec, "%02d", pt, ptlim);
315 char buf[INT_STRLEN_MAXIMUM(
321 if (TYPE_SIGNED(time_t))
322 sprintf(buf, "%"PRIdMAX,
324 else sprintf(buf, "%"PRIuMAX,
326 pt = _add(buf, pt, ptlim);
330 pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp);
333 pt = _add("\t", pt, ptlim);
336 pt = _conv((t->tm_yday + DAYSPERWEEK -
337 t->tm_wday) / DAYSPERWEEK,
342 ** From Arnold Robbins' strftime version 3.0:
343 ** "ISO 8601: Weekday as a decimal number
347 pt = _conv((t->tm_wday == 0) ?
348 DAYSPERWEEK : t->tm_wday,
351 case 'V': /* ISO 8601 week number */
352 case 'G': /* ISO 8601 year (four digits) */
353 case 'g': /* ISO 8601 year (two digits) */
355 ** From Arnold Robbins' strftime version 3.0: "the week number of the
356 ** year (the first Monday as the first day of week 1) as a decimal number
360 ** From <http://www.ft.uni-erlangen.de/~mskuhn/iso-time.html> by Markus Kuhn:
361 ** "Week 01 of a year is per definition the first week which has the
362 ** Thursday in this year, which is equivalent to the week which contains
363 ** the fourth day of January. In other words, the first week of a new year
364 ** is the week which has the majority of its days in the new year. Week 01
365 ** might also contain days from the previous year and the week before week
366 ** 01 of a year is the last week (52 or 53) of the previous year even if
367 ** it contains days from the new year. A week starts with Monday (day 1)
368 ** and ends with Sunday (day 7). For example, the first week of the year
369 ** 1997 lasts from 1996-12-30 to 1997-01-05..."
388 len = isleap_sum(year, base) ?
392 ** What yday (-3 ... 3) does
393 ** the ISO year begin on?
395 bot = ((yday + 11 - wday) %
398 ** What yday does the NEXT
399 ** ISO year begin on?
412 w = 1 + ((yday - bot) /
417 yday += isleap_sum(year, base) ?
421 #ifdef XPG4_1994_04_09
423 t->tm_mon == TM_JANUARY) ||
425 t->tm_mon == TM_DECEMBER))
427 #endif /* defined XPG4_1994_04_09 */
429 pt = _conv(w, "%02d",
431 else if (*format == 'g') {
433 pt = _yconv(year, base,
436 } else pt = _yconv(year, base,
443 ** From Arnold Robbins' strftime version 3.0:
444 ** "date as dd-bbb-YYYY"
447 pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp);
450 pt = _conv((t->tm_yday + DAYSPERWEEK -
453 (DAYSPERWEEK - 1))) / DAYSPERWEEK,
457 pt = _conv(t->tm_wday, "%d", pt, ptlim);
460 pt = _fmt(Locale->X_fmt, t, pt, ptlim, warnp);
466 pt = _fmt(Locale->x_fmt, t, pt, ptlim, &warn2);
475 pt = _yconv(t->tm_year, TM_YEAR_BASE,
480 pt = _yconv(t->tm_year, TM_YEAR_BASE,
486 pt = _add(t->TM_ZONE, pt, ptlim);
488 if (t->tm_isdst >= 0)
489 pt = _add(tzname[t->tm_isdst != 0],
493 ** C99 says that %Z must be replaced by the
494 ** empty string if the time zone is not
499 #if defined TM_GMTOFF || defined USG_COMPAT || defined ALTZONE
508 ** C99 says that the UT offset must
509 ** be computed by looking only at
510 ** tm_isdst. This requirement is
511 ** incorrect, since it means the code
512 ** must rely on magic (in this case
513 ** altzone and timezone), and the
514 ** magic might not have the correct
515 ** offset. Doing things correctly is
516 ** tricky and requires disobeying C99;
517 ** see GNU C strftime for details.
518 ** For now, punt and conform to the
519 ** standard, even though it's incorrect.
521 ** C99 says that %z must be replaced by the
522 ** empty string if the time zone is not
523 ** determinable, so output nothing if the
524 ** appropriate variables are not available.
528 if (t->tm_isdst == 0)
545 pt = _add(sign, pt, ptlim);
547 diff = (diff / MINSPERHOUR) * 100 +
548 (diff % MINSPERHOUR);
549 pt = _conv(diff, "%04d", pt, ptlim);
554 pt = _fmt(Locale->date_fmt, t, pt, ptlim,
559 ** X311J/88-090 (4.12.3.5): if conversion char is
560 ** undefined, behavior is undefined. Print out the
561 ** character itself as printf(3) also does.
575 _conv(int n, const char *format, char *pt, const char *ptlim)
577 char buf[INT_STRLEN_MAXIMUM(int) + 1];
579 sprintf(buf, format, n);
580 return _add(buf, pt, ptlim);
584 _add(const char *str, char *pt, const char *ptlim)
586 while (pt < ptlim && (*pt = *str++) != '\0')
592 ** POSIX and the C Standard are unclear or inconsistent about
593 ** what %C and %y do if the year is negative or exceeds 9999.
594 ** Use the convention that %C concatenated with %y yields the
595 ** same output as %Y, and that %Y contains at least 4 bytes,
596 ** with more only if necessary.
600 _yconv(int a, int b, bool convert_top, bool convert_yy,
601 char *pt, const char *ptlim)
607 trail = a % DIVISOR + b % DIVISOR;
608 lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR;
610 if (trail < 0 && lead > 0) {
613 } else if (lead < 0 && trail > 0) {
618 if (lead == 0 && trail < 0)
619 pt = _add("-0", pt, ptlim);
620 else pt = _conv(lead, "%02d", pt, ptlim);
623 pt = _conv(((trail < 0) ? -trail : trail), "%02d", pt, ptlim);