To generate stub warnings.
[glibc/history.git] / time / strftime.c
blob891d301f5c33c19dfc30232e0d3cfca3fe2789ec
1 /* Copyright (C) 1991, 92, 93, 94, 95, 96, 97 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public License as
6 published by the Free Software Foundation; either version 2 of the
7 License, or (at your option) any later version.
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
14 You should have received a copy of the GNU Library General Public
15 License along with the GNU C Library; see the file COPYING.LIB. If not,
16 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 Boston, MA 02111-1307, USA. */
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
23 #ifdef _LIBC
24 # define HAVE_LIMITS_H 1
25 # define HAVE_MBLEN 1
26 # define HAVE_MBRLEN 1
27 # define HAVE_STRUCT_ERA_ENTRY 1
28 # define HAVE_TM_GMTOFF 1
29 # define HAVE_TM_ZONE 1
30 # define HAVE_TZNAME 1
31 # define HAVE_TZSET 1
32 # define MULTIBYTE_IS_FORMAT_SAFE 1
33 # define STDC_HEADERS 1
34 # include "../locale/localeinfo.h"
35 #endif
37 #include <ctype.h>
38 #include <sys/types.h> /* Some systems define `time_t' here. */
40 #ifdef TIME_WITH_SYS_TIME
41 # include <sys/time.h>
42 # include <time.h>
43 #else
44 # ifdef HAVE_SYS_TIME_H
45 # include <sys/time.h>
46 # else
47 # include <time.h>
48 # endif
49 #endif
50 #if HAVE_TZNAME
51 extern char *tzname[];
52 #endif
54 /* Do multibyte processing if multibytes are supported, unless
55 multibyte sequences are safe in formats. Multibyte sequences are
56 safe if they cannot contain byte sequences that look like format
57 conversion specifications. The GNU C Library uses UTF8 multibyte
58 encoding, which is safe for formats, but strftime.c can be used
59 with other C libraries that use unsafe encodings. */
60 #define DO_MULTIBYTE (HAVE_MBLEN && ! MULTIBYTE_IS_FORMAT_SAFE)
62 #if DO_MULTIBYTE
63 # if HAVE_MBRLEN
64 # include <wchar.h>
65 # else
66 /* Simulate mbrlen with mblen as best we can. */
67 # define mbstate_t int
68 # define mbrlen(s, n, ps) mblen (s, n)
69 # define mbsinit(ps) (*(ps) == 0)
70 # endif
71 static const mbstate_t mbstate_zero;
72 #endif
74 #if HAVE_LIMITS_H
75 # include <limits.h>
76 #endif
78 #if STDC_HEADERS
79 # include <stddef.h>
80 # include <stdlib.h>
81 # include <string.h>
82 #else
83 # ifndef HAVE_MEMCPY
84 # define memcpy(d, s, n) bcopy ((s), (d), (n))
85 # endif
86 #endif
88 #ifndef __P
89 # if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
90 # define __P(args) args
91 # else
92 # define __P(args) ()
93 # endif /* GCC. */
94 #endif /* Not __P. */
96 #ifndef PTR
97 # ifdef __STDC__
98 # define PTR void *
99 # else
100 # define PTR char *
101 # endif
102 #endif
104 #ifndef CHAR_BIT
105 # define CHAR_BIT 8
106 #endif
108 #ifndef NULL
109 # define NULL 0
110 #endif
112 #define TYPE_SIGNED(t) ((t) -1 < 0)
114 /* Bound on length of the string representing an integer value of type t.
115 Subtract one for the sign bit if t is signed;
116 302 / 1000 is log10 (2) rounded up;
117 add one for integer division truncation;
118 add one more for a minus sign if t is signed. */
119 #define INT_STRLEN_BOUND(t) \
120 ((sizeof (t) * CHAR_BIT - TYPE_SIGNED (t)) * 302 / 100 + 1 + TYPE_SIGNED (t))
122 #define TM_YEAR_BASE 1900
124 #ifndef __isleap
125 /* Nonzero if YEAR is a leap year (every 4 years,
126 except every 100th isn't, and every 400th is). */
127 # define __isleap(year) \
128 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
129 #endif
132 #ifdef _LIBC
133 # define gmtime_r __gmtime_r
134 # define localtime_r __localtime_r
135 extern int __tz_compute __P ((time_t timer, const struct tm *tm));
136 # define tzname __tzname
137 # define tzset __tzset
138 #else
139 # if ! HAVE_LOCALTIME_R
140 # if ! HAVE_TM_GMTOFF
141 /* Approximate gmtime_r as best we can in its absence. */
142 # define gmtime_r my_gmtime_r
143 static struct tm *gmtime_r __P ((const time_t *, struct tm *));
144 static struct tm *
145 gmtime_r (t, tp)
146 const time_t *t;
147 struct tm *tp;
149 struct tm *l = gmtime (t);
150 if (! l)
151 return 0;
152 *tp = *l;
153 return tp;
155 # endif /* ! HAVE_TM_GMTOFF */
157 /* Approximate localtime_r as best we can in its absence. */
158 # define localtime_r my_ftime_localtime_r
159 static struct tm *localtime_r __P ((const time_t *, struct tm *));
160 static struct tm *
161 localtime_r (t, tp)
162 const time_t *t;
163 struct tm *tp;
165 struct tm *l = localtime (t);
166 if (! l)
167 return 0;
168 *tp = *l;
169 return tp;
171 # endif /* ! HAVE_LOCALTIME_R */
172 #endif /* ! defined (_LIBC) */
175 #if !defined memset && !defined HAVE_MEMSET && !defined _LIBC
176 /* Some systems lack the `memset' function and we don't want to
177 introduce additional dependencies. */
178 /* The SGI compiler reportedly barfs on the trailing null
179 if we use a string constant as the initializer. 28 June 1997, rms. */
180 static const char spaces[16] = /* " " */
181 { ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ' };
182 static const char zeroes[16] = /* "0000000000000000" */
183 { '0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0' };
185 # define memset_space(P, Len) \
186 do { \
187 int _len = (Len); \
189 do \
191 int _this = _len > 16 ? 16 : _len; \
192 memcpy ((P), spaces, _this); \
193 (P) += _this; \
194 _len -= _this; \
196 while (_len > 0); \
197 } while (0)
199 # define memset_zero(P, Len) \
200 do { \
201 int _len = (Len); \
203 do \
205 int _this = _len > 16 ? 16 : _len; \
206 memcpy ((P), zeroes, _this); \
207 (P) += _this; \
208 _len -= _this; \
210 while (_len > 0); \
211 } while (0)
212 #else
213 # define memset_space(P, Len) (memset ((P), ' ', (Len)), (P) += (Len))
214 # define memset_zero(P, Len) (memset ((P), '0', (Len)), (P) += (Len))
215 #endif
217 #define add(n, f) \
218 do \
220 int _n = (n); \
221 int _delta = width - _n; \
222 int _incr = _n + (_delta > 0 ? _delta : 0); \
223 if (i + _incr >= maxsize) \
224 return 0; \
225 if (p) \
227 if (_delta > 0) \
229 if (pad == '0') \
230 memset_zero (p, _delta); \
231 else \
232 memset_space (p, _delta); \
234 f; \
235 p += _n; \
237 i += _incr; \
238 } while (0)
240 #define cpy(n, s) \
241 add ((n), \
242 if (to_lowcase) \
243 memcpy_lowcase (p, (s), _n); \
244 else if (to_uppcase) \
245 memcpy_uppcase (p, (s), _n); \
246 else \
247 memcpy ((PTR) p, (PTR) (s), _n))
251 #ifdef _LIBC
252 # define TOUPPER(Ch) toupper (Ch)
253 # define TOLOWER(Ch) tolower (Ch)
254 #else
255 # define TOUPPER(Ch) (islower (Ch) ? toupper (Ch) : (Ch))
256 # define TOLOWER(Ch) (isupper (Ch) ? tolower (Ch) : (Ch))
257 #endif
258 /* We don't use `isdigit' here since the locale dependent
259 interpretation is not what we want here. We only need to accept
260 the arabic digits in the ASCII range. One day there is perhaps a
261 more reliable way to accept other sets of digits. */
262 #define ISDIGIT(Ch) ((unsigned int) (Ch) - '0' <= 9)
264 static char *memcpy_lowcase __P ((char *dest, const char *src, size_t len));
266 static char *
267 memcpy_lowcase (dest, src, len)
268 char *dest;
269 const char *src;
270 size_t len;
272 while (len-- > 0)
273 dest[len] = TOLOWER (src[len]);
274 return dest;
277 static char *memcpy_uppcase __P ((char *dest, const char *src, size_t len));
279 static char *
280 memcpy_uppcase (dest, src, len)
281 char *dest;
282 const char *src;
283 size_t len;
285 while (len-- > 0)
286 dest[len] = TOUPPER (src[len]);
287 return dest;
291 #if ! HAVE_TM_GMTOFF
292 /* Yield the difference between *A and *B,
293 measured in seconds, ignoring leap seconds. */
294 # define tm_diff ftime_tm_diff
295 static int tm_diff __P ((const struct tm *, const struct tm *));
296 static int
297 tm_diff (a, b)
298 const struct tm *a;
299 const struct tm *b;
301 /* Compute intervening leap days correctly even if year is negative.
302 Take care to avoid int overflow in leap day calculations,
303 but it's OK to assume that A and B are close to each other. */
304 int a4 = (a->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (a->tm_year & 3);
305 int b4 = (b->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (b->tm_year & 3);
306 int a100 = a4 / 25 - (a4 % 25 < 0);
307 int b100 = b4 / 25 - (b4 % 25 < 0);
308 int a400 = a100 >> 2;
309 int b400 = b100 >> 2;
310 int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
311 int years = a->tm_year - b->tm_year;
312 int days = (365 * years + intervening_leap_days
313 + (a->tm_yday - b->tm_yday));
314 return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
315 + (a->tm_min - b->tm_min))
316 + (a->tm_sec - b->tm_sec));
318 #endif /* ! HAVE_TM_GMTOFF */
322 /* The number of days from the first day of the first ISO week of this
323 year to the year day YDAY with week day WDAY. ISO weeks start on
324 Monday; the first ISO week has the year's first Thursday. YDAY may
325 be as small as YDAY_MINIMUM. */
326 #define ISO_WEEK_START_WDAY 1 /* Monday */
327 #define ISO_WEEK1_WDAY 4 /* Thursday */
328 #define YDAY_MINIMUM (-366)
329 static int iso_week_days __P ((int, int));
330 #ifdef __GNUC__
331 inline
332 #endif
333 static int
334 iso_week_days (yday, wday)
335 int yday;
336 int wday;
338 /* Add enough to the first operand of % to make it nonnegative. */
339 int big_enough_multiple_of_7 = (-YDAY_MINIMUM / 7 + 2) * 7;
340 return (yday
341 - (yday - wday + ISO_WEEK1_WDAY + big_enough_multiple_of_7) % 7
342 + ISO_WEEK1_WDAY - ISO_WEEK_START_WDAY);
346 #ifndef _NL_CURRENT
347 static char const weekday_name[][10] =
349 "Sunday", "Monday", "Tuesday", "Wednesday",
350 "Thursday", "Friday", "Saturday"
352 static char const month_name[][10] =
354 "January", "February", "March", "April", "May", "June",
355 "July", "August", "September", "October", "November", "December"
357 #endif
360 #if !defined _LIBC && HAVE_TZNAME && HAVE_TZSET
361 /* Solaris 2.5 tzset sometimes modifies the storage returned by localtime.
362 Work around this bug by copying *tp before it might be munged. */
363 size_t _strftime_copytm __P ((char *, size_t, const char *,
364 const struct tm *));
365 size_t
366 strftime (s, maxsize, format, tp)
367 char *s;
368 size_t maxsize;
369 const char *format;
370 const struct tm *tp;
372 struct tm tmcopy;
373 tmcopy = *tp;
374 return _strftime_copytm (s, maxsize, format, &tmcopy);
376 # ifdef strftime
377 # undef strftime
378 # endif
379 # define strftime(S, Maxsize, Format, Tp) \
380 _strftime_copytm (S, Maxsize, Format, Tp)
381 #endif
384 /* Write information from TP into S according to the format
385 string FORMAT, writing no more that MAXSIZE characters
386 (including the terminating '\0') and returning number of
387 characters written. If S is NULL, nothing will be written
388 anywhere, so to determine how many characters would be
389 written, use NULL for S and (size_t) UINT_MAX for MAXSIZE. */
390 size_t
391 strftime (s, maxsize, format, tp)
392 char *s;
393 size_t maxsize;
394 const char *format;
395 const struct tm *tp;
397 int hour12 = tp->tm_hour;
398 #ifdef _NL_CURRENT
399 const char *const a_wkday = _NL_CURRENT (LC_TIME, ABDAY_1 + tp->tm_wday);
400 const char *const f_wkday = _NL_CURRENT (LC_TIME, DAY_1 + tp->tm_wday);
401 const char *const a_month = _NL_CURRENT (LC_TIME, ABMON_1 + tp->tm_mon);
402 const char *const f_month = _NL_CURRENT (LC_TIME, MON_1 + tp->tm_mon);
403 const char *const ampm = _NL_CURRENT (LC_TIME,
404 hour12 > 11 ? PM_STR : AM_STR);
405 size_t aw_len = strlen (a_wkday);
406 size_t am_len = strlen (a_month);
407 size_t ap_len = strlen (ampm);
408 #else
409 const char *const f_wkday = weekday_name[tp->tm_wday];
410 const char *const f_month = month_name[tp->tm_mon];
411 const char *const a_wkday = f_wkday;
412 const char *const a_month = f_month;
413 const char *const ampm = "AMPM" + 2 * (hour12 > 11);
414 size_t aw_len = 3;
415 size_t am_len = 3;
416 size_t ap_len = 2;
417 #endif
418 size_t wkday_len = strlen (f_wkday);
419 size_t month_len = strlen (f_month);
420 const char *zone;
421 size_t zonelen;
422 size_t i = 0;
423 char *p = s;
424 const char *f;
426 zone = NULL;
427 #if HAVE_TM_ZONE
428 /* The POSIX test suite assumes that setting
429 the environment variable TZ to a new value before calling strftime()
430 will influence the result (the %Z format) even if the information in
431 TP is computed with a totally different time zone.
432 This is bogus: though POSIX allows bad behavior like this,
433 POSIX does not require it. Do the right thing instead. */
434 zone = (const char *) tp->tm_zone;
435 #endif
436 #if HAVE_TZNAME
437 /* POSIX.1 8.1.1 requires that whenever strftime() is called, the
438 time zone names contained in the external variable `tzname' shall
439 be set as if the tzset() function had been called. */
440 # if HAVE_TZSET
441 tzset ();
442 # endif
444 if (!(zone && *zone) && tp->tm_isdst >= 0)
445 zone = tzname[tp->tm_isdst];
446 #endif
447 if (! zone)
448 zone = ""; /* POSIX.2 requires the empty string here. */
450 zonelen = strlen (zone);
452 if (hour12 > 12)
453 hour12 -= 12;
454 else
455 if (hour12 == 0) hour12 = 12;
457 for (f = format; *f != '\0'; ++f)
459 int pad; /* Padding for number ('-', '_', or 0). */
460 int modifier; /* Field modifier ('E', 'O', or 0). */
461 int digits; /* Max digits for numeric format. */
462 int number_value; /* Numeric value to be printed. */
463 int negative_number; /* 1 if the number is negative. */
464 const char *subfmt;
465 char *bufp;
466 char buf[1 + (sizeof (int) < sizeof (time_t)
467 ? INT_STRLEN_BOUND (time_t)
468 : INT_STRLEN_BOUND (int))];
469 int width = -1;
470 int to_lowcase = 0;
471 int to_uppcase = 0;
472 int change_case = 0;
474 #if DO_MULTIBYTE
476 switch (*f)
478 case '%':
479 break;
481 case '\a': case '\b': case '\t': case '\n':
482 case '\v': case '\f': case '\r':
483 case ' ': case '!': case '"': case '#': case '&': case'\'':
484 case '(': case ')': case '*': case '+': case ',': case '-':
485 case '.': case '/': case '0': case '1': case '2': case '3':
486 case '4': case '5': case '6': case '7': case '8': case '9':
487 case ':': case ';': case '<': case '=': case '>': case '?':
488 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
489 case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
490 case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
491 case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
492 case 'Y': case 'Z': case '[': case'\\': case ']': case '^':
493 case '_': case 'a': case 'b': case 'c': case 'd': case 'e':
494 case 'f': case 'g': case 'h': case 'i': case 'j': case 'k':
495 case 'l': case 'm': case 'n': case 'o': case 'p': case 'q':
496 case 'r': case 's': case 't': case 'u': case 'v': case 'w':
497 case 'x': case 'y': case 'z': case '{': case '|': case '}':
498 case '~':
499 /* The C Standard requires these 98 characters (plus '%') to
500 be in the basic execution character set. None of these
501 characters can start a multibyte sequence, so they need
502 not be analyzed further. */
503 add (1, *p = *f);
504 continue;
506 default:
507 /* Copy this multibyte sequence until we reach its end, find
508 an error, or come back to the initial shift state. */
510 mbstate_t mbstate = mbstate_zero;
511 size_t len = 0;
515 size_t bytes = mbrlen (f + len, (size_t) -1, &mbstate);
517 if (bytes == 0)
518 break;
520 if (bytes == (size_t) -2 || bytes == (size_t) -1)
522 len++;
523 break;
526 len += bytes;
528 while (! mbsinit (&mbstate));
530 cpy (len, f);
531 continue;
535 #else /* ! DO_MULTIBYTE */
537 /* Either multibyte encodings are not supported, or they are
538 safe for formats, so any non-'%' byte can be copied through. */
539 if (*f != '%')
541 add (1, *p = *f);
542 continue;
545 #endif /* ! DO_MULTIBYTE */
547 /* Check for flags that can modify a format. */
548 pad = 0;
549 while (1)
551 switch (*++f)
553 /* This influences the number formats. */
554 case '_':
555 case '-':
556 case '0':
557 pad = *f;
558 continue;
560 /* This changes textual output. */
561 case '^':
562 to_uppcase = 1;
563 continue;
564 case '#':
565 change_case = 1;
566 continue;
568 default:
569 break;
571 break;
574 /* As a GNU extension we allow to specify the field width. */
575 if (ISDIGIT (*f))
577 width = 0;
580 width *= 10;
581 width += *f - '0';
582 ++f;
584 while (ISDIGIT (*f));
587 /* Check for modifiers. */
588 switch (*f)
590 case 'E':
591 case 'O':
592 modifier = *f++;
593 break;
595 default:
596 modifier = 0;
597 break;
600 /* Now do the specified format. */
601 switch (*f)
603 #define DO_NUMBER(d, v) \
604 digits = width == -1 ? d : width; \
605 number_value = v; goto do_number
606 #define DO_NUMBER_SPACEPAD(d, v) \
607 digits = width == -1 ? d : width; \
608 number_value = v; goto do_number_spacepad
610 case '%':
611 if (modifier != 0)
612 goto bad_format;
613 add (1, *p = *f);
614 break;
616 case 'a':
617 if (modifier != 0)
618 goto bad_format;
619 if (change_case)
621 to_uppcase = 1;
622 to_lowcase = 0;
624 cpy (aw_len, a_wkday);
625 break;
627 case 'A':
628 if (modifier != 0)
629 goto bad_format;
630 if (change_case)
632 to_uppcase = 1;
633 to_lowcase = 0;
635 cpy (wkday_len, f_wkday);
636 break;
638 case 'b':
639 case 'h': /* POSIX.2 extension. */
640 if (modifier != 0)
641 goto bad_format;
642 cpy (am_len, a_month);
643 break;
645 case 'B':
646 if (modifier != 0)
647 goto bad_format;
648 if (change_case)
650 to_uppcase = 1;
651 to_lowcase = 0;
653 cpy (month_len, f_month);
654 break;
656 case 'c':
657 if (modifier == 'O')
658 goto bad_format;
659 #ifdef _NL_CURRENT
660 if (! (modifier == 'E'
661 && *(subfmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT)) != '\0'))
662 subfmt = _NL_CURRENT (LC_TIME, D_T_FMT);
663 #else
664 subfmt = "%a %b %e %H:%M:%S %Y";
665 #endif
667 subformat:
669 char *old_start = p;
670 size_t len = strftime (NULL, maxsize - i, subfmt, tp);
671 if (len == 0 && *subfmt)
672 return 0;
673 add (len, strftime (p, maxsize - i, subfmt, tp));
675 if (to_uppcase)
676 while (old_start < p)
678 *old_start = TOUPPER (*old_start);
679 ++old_start;
682 break;
684 case 'C': /* POSIX.2 extension. */
685 if (modifier == 'O')
686 goto bad_format;
687 #if HAVE_STRUCT_ERA_ENTRY
688 if (modifier == 'E')
690 struct era_entry *era = _nl_get_era_entry (tp);
691 if (era)
693 size_t len = strlen (era->name_fmt);
694 cpy (len, era->name_fmt);
695 break;
698 #endif
700 int year = tp->tm_year + TM_YEAR_BASE;
701 DO_NUMBER (1, year / 100 - (year % 100 < 0));
704 case 'x':
705 if (modifier == 'O')
706 goto bad_format;
707 #ifdef _NL_CURRENT
708 if (! (modifier == 'E'
709 && *(subfmt = _NL_CURRENT (LC_TIME, ERA_D_FMT)) != '\0'))
710 subfmt = _NL_CURRENT (LC_TIME, D_FMT);
711 goto subformat;
712 #endif
713 /* Fall through. */
714 case 'D': /* POSIX.2 extension. */
715 if (modifier != 0)
716 goto bad_format;
717 subfmt = "%m/%d/%y";
718 goto subformat;
720 case 'd':
721 if (modifier == 'E')
722 goto bad_format;
724 DO_NUMBER (2, tp->tm_mday);
726 case 'e': /* POSIX.2 extension. */
727 if (modifier == 'E')
728 goto bad_format;
730 DO_NUMBER_SPACEPAD (2, tp->tm_mday);
732 /* All numeric formats set DIGITS and NUMBER_VALUE and then
733 jump to one of these two labels. */
735 do_number_spacepad:
736 /* Force `_' flag unless overwritten by `0' flag. */
737 if (pad != '0')
738 pad = '_';
740 do_number:
741 /* Format the number according to the MODIFIER flag. */
743 #ifdef _NL_CURRENT
744 if (modifier == 'O' && 0 <= number_value)
746 /* Get the locale specific alternate representation of
747 the number NUMBER_VALUE. If none exist NULL is returned. */
748 const char *cp = _nl_get_alt_digit (number_value);
750 if (cp != NULL)
752 size_t digitlen = strlen (cp);
753 if (digitlen != 0)
755 cpy (digitlen, cp);
756 break;
760 #endif
762 unsigned int u = number_value;
764 bufp = buf + sizeof (buf);
765 negative_number = number_value < 0;
767 if (negative_number)
768 u = -u;
771 *--bufp = u % 10 + '0';
772 while ((u /= 10) != 0);
775 do_number_sign_and_padding:
776 if (negative_number)
777 *--bufp = '-';
779 if (pad != '-')
781 int padding = digits - (buf + sizeof (buf) - bufp);
783 if (pad == '_')
785 while (0 < padding--)
786 *--bufp = ' ';
788 else
790 bufp += negative_number;
791 while (0 < padding--)
792 *--bufp = '0';
793 if (negative_number)
794 *--bufp = '-';
798 cpy (buf + sizeof (buf) - bufp, bufp);
799 break;
802 case 'H':
803 if (modifier == 'E')
804 goto bad_format;
806 DO_NUMBER (2, tp->tm_hour);
808 case 'I':
809 if (modifier == 'E')
810 goto bad_format;
812 DO_NUMBER (2, hour12);
814 case 'k': /* GNU extension. */
815 if (modifier == 'E')
816 goto bad_format;
818 DO_NUMBER_SPACEPAD (2, tp->tm_hour);
820 case 'l': /* GNU extension. */
821 if (modifier == 'E')
822 goto bad_format;
824 DO_NUMBER_SPACEPAD (2, hour12);
826 case 'j':
827 if (modifier == 'E')
828 goto bad_format;
830 DO_NUMBER (3, 1 + tp->tm_yday);
832 case 'M':
833 if (modifier == 'E')
834 goto bad_format;
836 DO_NUMBER (2, tp->tm_min);
838 case 'm':
839 if (modifier == 'E')
840 goto bad_format;
842 DO_NUMBER (2, tp->tm_mon + 1);
844 case 'n': /* POSIX.2 extension. */
845 add (1, *p = '\n');
846 break;
848 case 'P':
849 to_lowcase = 1;
850 /* FALLTHROUGH */
852 case 'p':
853 if (change_case)
855 to_uppcase = 0;
856 to_lowcase = 1;
858 cpy (ap_len, ampm);
859 break;
861 case 'R': /* GNU extension. */
862 subfmt = "%H:%M";
863 goto subformat;
865 case 'r': /* POSIX.2 extension. */
866 #ifdef _NL_CURRENT
867 if (*(subfmt = _NL_CURRENT (LC_TIME, T_FMT_AMPM)) == '\0')
868 #endif
869 subfmt = "%I:%M:%S %p";
870 goto subformat;
872 case 'S':
873 if (modifier == 'E')
874 goto bad_format;
876 DO_NUMBER (2, tp->tm_sec);
878 case 's': /* GNU extension. */
880 struct tm ltm;
881 time_t t;
883 ltm = *tp;
884 t = mktime (&ltm);
886 /* Generate string value for T using time_t arithmetic;
887 this works even if sizeof (long) < sizeof (time_t). */
889 bufp = buf + sizeof (buf);
890 negative_number = t < 0;
894 int d = t % 10;
895 t /= 10;
897 if (negative_number)
899 d = -d;
901 /* Adjust if division truncates to minus infinity. */
902 if (0 < -1 % 10 && d < 0)
904 t++;
905 d += 10;
909 *--bufp = d + '0';
911 while (t != 0);
913 digits = 1;
914 goto do_number_sign_and_padding;
917 case 'X':
918 if (modifier == 'O')
919 goto bad_format;
920 #ifdef _NL_CURRENT
921 if (! (modifier == 'E'
922 && *(subfmt = _NL_CURRENT (LC_TIME, ERA_T_FMT)) != '\0'))
923 subfmt = _NL_CURRENT (LC_TIME, T_FMT);
924 goto subformat;
925 #endif
926 /* Fall through. */
927 case 'T': /* POSIX.2 extension. */
928 subfmt = "%H:%M:%S";
929 goto subformat;
931 case 't': /* POSIX.2 extension. */
932 add (1, *p = '\t');
933 break;
935 case 'u': /* POSIX.2 extension. */
936 DO_NUMBER (1, (tp->tm_wday - 1 + 7) % 7 + 1);
938 case 'U':
939 if (modifier == 'E')
940 goto bad_format;
942 DO_NUMBER (2, (tp->tm_yday - tp->tm_wday + 7) / 7);
944 case 'V':
945 case 'g': /* GNU extension. */
946 case 'G': /* GNU extension. */
947 if (modifier == 'E')
948 goto bad_format;
950 int year = tp->tm_year + TM_YEAR_BASE;
951 int days = iso_week_days (tp->tm_yday, tp->tm_wday);
953 if (days < 0)
955 /* This ISO week belongs to the previous year. */
956 year--;
957 days = iso_week_days (tp->tm_yday + (365 + __isleap (year)),
958 tp->tm_wday);
960 else
962 int d = iso_week_days (tp->tm_yday - (365 + __isleap (year)),
963 tp->tm_wday);
964 if (0 <= d)
966 /* This ISO week belongs to the next year. */
967 year++;
968 days = d;
972 switch (*f)
974 case 'g':
975 DO_NUMBER (2, (year % 100 + 100) % 100);
977 case 'G':
978 DO_NUMBER (1, year);
980 default:
981 DO_NUMBER (2, days / 7 + 1);
985 case 'W':
986 if (modifier == 'E')
987 goto bad_format;
989 DO_NUMBER (2, (tp->tm_yday - (tp->tm_wday - 1 + 7) % 7 + 7) / 7);
991 case 'w':
992 if (modifier == 'E')
993 goto bad_format;
995 DO_NUMBER (1, tp->tm_wday);
997 case 'Y':
998 #if HAVE_STRUCT_ERA_ENTRY
999 if (modifier == 'E')
1001 struct era_entry *era = _nl_get_era_entry (tp);
1002 if (era)
1004 subfmt = strchr (era->name_fmt, '\0') + 1;
1005 goto subformat;
1008 #endif
1009 if (modifier == 'O')
1010 goto bad_format;
1011 else
1012 DO_NUMBER (1, tp->tm_year + TM_YEAR_BASE);
1014 case 'y':
1015 #if HAVE_STRUCT_ERA_ENTRY
1016 if (modifier == 'E')
1018 struct era_entry *era = _nl_get_era_entry (tp);
1019 if (era)
1021 int delta = tp->tm_year - era->start_date[0];
1022 DO_NUMBER (1, (era->offset
1023 + (era->direction == '-' ? -delta : delta)));
1026 #endif
1027 DO_NUMBER (2, (tp->tm_year % 100 + 100) % 100);
1029 case 'Z':
1030 if (change_case)
1032 to_uppcase = 0;
1033 to_lowcase = 1;
1035 cpy (zonelen, zone);
1036 break;
1038 case 'z': /* GNU extension. */
1039 if (tp->tm_isdst < 0)
1040 break;
1043 int diff;
1044 #if HAVE_TM_GMTOFF
1045 diff = tp->tm_gmtoff;
1046 #else
1047 struct tm gtm;
1048 struct tm ltm;
1049 time_t lt;
1051 ltm = *tp;
1052 lt = mktime (&ltm);
1054 if (lt == (time_t) -1)
1056 /* mktime returns -1 for errors, but -1 is also a
1057 valid time_t value. Check whether an error really
1058 occurred. */
1059 struct tm tm;
1060 localtime_r (&lt, &tm);
1062 if ((ltm.tm_sec ^ tm.tm_sec)
1063 | (ltm.tm_min ^ tm.tm_min)
1064 | (ltm.tm_hour ^ tm.tm_hour)
1065 | (ltm.tm_mday ^ tm.tm_mday)
1066 | (ltm.tm_mon ^ tm.tm_mon)
1067 | (ltm.tm_year ^ tm.tm_year))
1068 break;
1071 if (! gmtime_r (&lt, &gtm))
1072 break;
1074 diff = tm_diff (&ltm, &gtm);
1075 #endif
1077 if (diff < 0)
1079 add (1, *p = '-');
1080 diff = -diff;
1082 else
1083 add (1, *p = '+');
1085 diff /= 60;
1086 DO_NUMBER (4, (diff / 60) * 100 + diff % 60);
1089 case '\0': /* GNU extension: % at end of format. */
1090 --f;
1091 /* Fall through. */
1092 default:
1093 /* Unknown format; output the format, including the '%',
1094 since this is most likely the right thing to do if a
1095 multibyte string has been misparsed. */
1096 bad_format:
1098 int flen;
1099 for (flen = 1; f[1 - flen] != '%'; flen++)
1100 continue;
1101 cpy (flen, &f[1 - flen]);
1103 break;
1107 if (p)
1108 *p = '\0';
1109 return i;