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 const char * mon
[MONSPERYEAR
];
43 const char * month
[MONSPERYEAR
];
44 const char * wday
[DAYSPERWEEK
];
45 const char * weekday
[DAYSPERWEEK
];
51 const char * date_fmt
;
54 #define Locale (&C_time_locale)
56 static const struct lc_time_T C_time_locale
= {
58 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
59 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
61 "January", "February", "March", "April", "May", "June",
62 "July", "August", "September", "October", "November", "December"
64 "Sun", "Mon", "Tue", "Wed",
67 "Sunday", "Monday", "Tuesday", "Wednesday",
68 "Thursday", "Friday", "Saturday"
76 ** C99 requires this format.
77 ** Using just numbers (as here) makes Quakers happier;
78 ** it's also compatible with SVR4.
84 ** C99 requires this format.
85 ** Previously this code used "%D %X", but we now conform to C99.
87 ** "%a %b %d %H:%M:%S %Y"
88 ** is used by Solaris 2.3.
99 "%a %b %e %H:%M:%S %Z %Y"
102 static char * _add(const char *, char *, const char *);
103 static char * _conv(int, const char *, char *, const char *);
104 static char * _fmt(const char *, const struct tm
*, char *, const char *,
106 static char * _yconv(int, int, bool, bool, char *, char const *);
108 #if !HAVE_POSIX_DECLS
109 extern char * tzname
[];
112 #ifndef YEAR_2000_NAME
113 #define YEAR_2000_NAME "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS"
114 #endif /* !defined YEAR_2000_NAME */
123 strftime_l(char *s
, size_t maxsize
, char const *format
, struct tm
const *t
,
126 /* Just call strftime, as only the C locale is supported. */
127 return strftime(s
, maxsize
, format
, t
);
132 strftime(char *s
, size_t maxsize
, const char *format
, const struct tm
*t
)
139 p
= _fmt(format
, t
, s
, s
+ maxsize
, &warn
);
140 #ifndef NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU
141 if (warn
!= IN_NONE
&& getenv(YEAR_2000_NAME
) != NULL
) {
142 fprintf(stderr
, "\n");
143 fprintf(stderr
, "strftime format \"%s\" ", format
);
144 fprintf(stderr
, "yields only two digits of years in ");
146 fprintf(stderr
, "some locales");
147 else if (warn
== IN_THIS
)
148 fprintf(stderr
, "the current locale");
149 else fprintf(stderr
, "all locales");
150 fprintf(stderr
, "\n");
152 #endif /* !defined NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU */
153 if (p
== s
+ maxsize
)
160 _fmt(const char *format
, const struct tm
*t
, char *pt
,
161 const char *ptlim
, int *warnp
)
163 for ( ; *format
; ++format
) {
164 if (*format
== '%') {
171 pt
= _add((t
->tm_wday
< 0 ||
172 t
->tm_wday
>= DAYSPERWEEK
) ?
173 "?" : Locale
->weekday
[t
->tm_wday
],
177 pt
= _add((t
->tm_wday
< 0 ||
178 t
->tm_wday
>= DAYSPERWEEK
) ?
179 "?" : Locale
->wday
[t
->tm_wday
],
183 pt
= _add((t
->tm_mon
< 0 ||
184 t
->tm_mon
>= MONSPERYEAR
) ?
185 "?" : Locale
->month
[t
->tm_mon
],
190 pt
= _add((t
->tm_mon
< 0 ||
191 t
->tm_mon
>= MONSPERYEAR
) ?
192 "?" : Locale
->mon
[t
->tm_mon
],
197 ** %C used to do a...
198 ** _fmt("%a %b %e %X %Y", t);
199 ** ...whereas now POSIX 1003.2 calls for
200 ** something completely different.
203 pt
= _yconv(t
->tm_year
, TM_YEAR_BASE
,
204 true, false, pt
, ptlim
);
210 pt
= _fmt(Locale
->c_fmt
, t
, pt
, ptlim
, &warn2
);
218 pt
= _fmt("%m/%d/%y", t
, pt
, ptlim
, warnp
);
221 pt
= _conv(t
->tm_mday
, "%02d", pt
, ptlim
);
226 ** C99 locale modifiers.
228 ** %Ec %EC %Ex %EX %Ey %EY
229 ** %Od %oe %OH %OI %Om %OM
230 ** %OS %Ou %OU %OV %Ow %OW %Oy
231 ** are supposed to provide alternate
236 pt
= _conv(t
->tm_mday
, "%2d", pt
, ptlim
);
239 pt
= _fmt("%Y-%m-%d", t
, pt
, ptlim
, warnp
);
242 pt
= _conv(t
->tm_hour
, "%02d", pt
, ptlim
);
245 pt
= _conv((t
->tm_hour
% 12) ?
246 (t
->tm_hour
% 12) : 12,
250 pt
= _conv(t
->tm_yday
+ 1, "%03d", pt
, ptlim
);
254 ** This used to be...
255 ** _conv(t->tm_hour % 12 ?
256 ** t->tm_hour % 12 : 12, 2, ' ');
257 ** ...and has been changed to the below to
258 ** match SunOS 4.1.1 and Arnold Robbins'
259 ** strftime version 3.0. That is, "%k" and
260 ** "%l" have been swapped.
263 pt
= _conv(t
->tm_hour
, "%2d", pt
, ptlim
);
268 ** After all this time, still unclaimed!
270 pt
= _add("kitchen sink", pt
, ptlim
);
272 #endif /* defined KITCHEN_SINK */
275 ** This used to be...
276 ** _conv(t->tm_hour, 2, ' ');
277 ** ...and has been changed to the below to
278 ** match SunOS 4.1.1 and Arnold Robbin's
279 ** strftime version 3.0. That is, "%k" and
280 ** "%l" have been swapped.
283 pt
= _conv((t
->tm_hour
% 12) ?
284 (t
->tm_hour
% 12) : 12,
288 pt
= _conv(t
->tm_min
, "%02d", pt
, ptlim
);
291 pt
= _conv(t
->tm_mon
+ 1, "%02d", pt
, ptlim
);
294 pt
= _add("\n", pt
, ptlim
);
297 pt
= _add((t
->tm_hour
>= (HOURSPERDAY
/ 2)) ?
303 pt
= _fmt("%H:%M", t
, pt
, ptlim
, warnp
);
306 pt
= _fmt("%I:%M:%S %p", t
, pt
, ptlim
, warnp
);
309 pt
= _conv(t
->tm_sec
, "%02d", pt
, ptlim
);
314 char buf
[INT_STRLEN_MAXIMUM(
320 if (TYPE_SIGNED(time_t))
321 sprintf(buf
, "%"PRIdMAX
,
323 else sprintf(buf
, "%"PRIuMAX
,
325 pt
= _add(buf
, pt
, ptlim
);
329 pt
= _fmt("%H:%M:%S", t
, pt
, ptlim
, warnp
);
332 pt
= _add("\t", pt
, ptlim
);
335 pt
= _conv((t
->tm_yday
+ DAYSPERWEEK
-
336 t
->tm_wday
) / DAYSPERWEEK
,
341 ** From Arnold Robbins' strftime version 3.0:
342 ** "ISO 8601: Weekday as a decimal number
346 pt
= _conv((t
->tm_wday
== 0) ?
347 DAYSPERWEEK
: t
->tm_wday
,
350 case 'V': /* ISO 8601 week number */
351 case 'G': /* ISO 8601 year (four digits) */
352 case 'g': /* ISO 8601 year (two digits) */
354 ** From Arnold Robbins' strftime version 3.0: "the week number of the
355 ** year (the first Monday as the first day of week 1) as a decimal number
359 ** From <http://www.ft.uni-erlangen.de/~mskuhn/iso-time.html> by Markus Kuhn:
360 ** "Week 01 of a year is per definition the first week which has the
361 ** Thursday in this year, which is equivalent to the week which contains
362 ** the fourth day of January. In other words, the first week of a new year
363 ** is the week which has the majority of its days in the new year. Week 01
364 ** might also contain days from the previous year and the week before week
365 ** 01 of a year is the last week (52 or 53) of the previous year even if
366 ** it contains days from the new year. A week starts with Monday (day 1)
367 ** and ends with Sunday (day 7). For example, the first week of the year
368 ** 1997 lasts from 1996-12-30 to 1997-01-05..."
387 len
= isleap_sum(year
, base
) ?
391 ** What yday (-3 ... 3) does
392 ** the ISO year begin on?
394 bot
= ((yday
+ 11 - wday
) %
397 ** What yday does the NEXT
398 ** ISO year begin on?
411 w
= 1 + ((yday
- bot
) /
416 yday
+= isleap_sum(year
, base
) ?
420 #ifdef XPG4_1994_04_09
422 t
->tm_mon
== TM_JANUARY
) ||
424 t
->tm_mon
== TM_DECEMBER
))
426 #endif /* defined XPG4_1994_04_09 */
428 pt
= _conv(w
, "%02d",
430 else if (*format
== 'g') {
432 pt
= _yconv(year
, base
,
435 } else pt
= _yconv(year
, base
,
442 ** From Arnold Robbins' strftime version 3.0:
443 ** "date as dd-bbb-YYYY"
446 pt
= _fmt("%e-%b-%Y", t
, pt
, ptlim
, warnp
);
449 pt
= _conv((t
->tm_yday
+ DAYSPERWEEK
-
452 (DAYSPERWEEK
- 1))) / DAYSPERWEEK
,
456 pt
= _conv(t
->tm_wday
, "%d", pt
, ptlim
);
459 pt
= _fmt(Locale
->X_fmt
, t
, pt
, ptlim
, warnp
);
465 pt
= _fmt(Locale
->x_fmt
, t
, pt
, ptlim
, &warn2
);
474 pt
= _yconv(t
->tm_year
, TM_YEAR_BASE
,
479 pt
= _yconv(t
->tm_year
, TM_YEAR_BASE
,
485 pt
= _add(t
->TM_ZONE
, pt
, ptlim
);
487 if (t
->tm_isdst
>= 0)
488 pt
= _add(tzname
[t
->tm_isdst
!= 0],
492 ** C99 says that %Z must be replaced by the
493 ** empty string if the time zone is not
498 #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)
544 negative
= t
->TM_ZONE
[0] == '-';
548 || tzname
[t
->tm_isdst
!= 0][0] == '-');
555 pt
= _add(sign
, pt
, ptlim
);
557 diff
= (diff
/ MINSPERHOUR
) * 100 +
558 (diff
% MINSPERHOUR
);
559 pt
= _conv(diff
, "%04d", pt
, ptlim
);
564 pt
= _fmt(Locale
->date_fmt
, t
, pt
, ptlim
,
569 ** X311J/88-090 (4.12.3.5): if conversion char is
570 ** undefined, behavior is undefined. Print out the
571 ** character itself as printf(3) also does.
585 _conv(int n
, const char *format
, char *pt
, const char *ptlim
)
587 char buf
[INT_STRLEN_MAXIMUM(int) + 1];
589 sprintf(buf
, format
, n
);
590 return _add(buf
, pt
, ptlim
);
594 _add(const char *str
, char *pt
, const char *ptlim
)
596 while (pt
< ptlim
&& (*pt
= *str
++) != '\0')
602 ** POSIX and the C Standard are unclear or inconsistent about
603 ** what %C and %y do if the year is negative or exceeds 9999.
604 ** Use the convention that %C concatenated with %y yields the
605 ** same output as %Y, and that %Y contains at least 4 bytes,
606 ** with more only if necessary.
610 _yconv(int a
, int b
, bool convert_top
, bool convert_yy
,
611 char *pt
, const char *ptlim
)
617 trail
= a
% DIVISOR
+ b
% DIVISOR
;
618 lead
= a
/ DIVISOR
+ b
/ DIVISOR
+ trail
/ DIVISOR
;
620 if (trail
< 0 && lead
> 0) {
623 } else if (lead
< 0 && trail
> 0) {
628 if (lead
== 0 && trail
< 0)
629 pt
= _add("-0", pt
, ptlim
);
630 else pt
= _conv(lead
, "%02d", pt
, ptlim
);
633 pt
= _conv(((trail
< 0) ? -trail
: trail
), "%02d", pt
, ptlim
);