(modules): Add CSN_369103, CWI, DEC-MCS, ECMA-CYRILLIC, GOST_19768-74, GREEK-CCITT...
[glibc.git] / time / strftime.c
blobf724bf3e3b658b1a7754dc606c8d18623419f55d
1 /* Copyright (C) 1991,92,93,94,95,96,97,98 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 /* Some hosts need this in order to declare localtime_r properly. */
24 #ifndef __EXTENSIONS__
25 # define __EXTENSIONS__ 1
26 #endif
28 #ifdef _LIBC
29 # define HAVE_LIMITS_H 1
30 # define HAVE_MBLEN 1
31 # define HAVE_MBRLEN 1
32 # define HAVE_STRUCT_ERA_ENTRY 1
33 # define HAVE_TM_GMTOFF 1
34 # define HAVE_TM_ZONE 1
35 # define HAVE_TZNAME 1
36 # define HAVE_TZSET 1
37 # define MULTIBYTE_IS_FORMAT_SAFE 1
38 # define STDC_HEADERS 1
39 # include "../locale/localeinfo.h"
40 #endif
42 #if defined emacs && !defined HAVE_BCOPY
43 # define HAVE_MEMCPY 1
44 #endif
46 #include <ctype.h>
47 #include <sys/types.h> /* Some systems define `time_t' here. */
49 #ifdef TIME_WITH_SYS_TIME
50 # include <sys/time.h>
51 # include <time.h>
52 #else
53 # ifdef HAVE_SYS_TIME_H
54 # include <sys/time.h>
55 # else
56 # include <time.h>
57 # endif
58 #endif
59 #if HAVE_TZNAME
60 extern char *tzname[];
61 #endif
63 /* Do multibyte processing if multibytes are supported, unless
64 multibyte sequences are safe in formats. Multibyte sequences are
65 safe if they cannot contain byte sequences that look like format
66 conversion specifications. The GNU C Library uses UTF8 multibyte
67 encoding, which is safe for formats, but strftime.c can be used
68 with other C libraries that use unsafe encodings. */
69 #define DO_MULTIBYTE (HAVE_MBLEN && ! MULTIBYTE_IS_FORMAT_SAFE)
71 #if DO_MULTIBYTE
72 # if HAVE_MBRLEN
73 # include <wchar.h>
74 # else
75 /* Simulate mbrlen with mblen as best we can. */
76 # define mbstate_t int
77 # define mbrlen(s, n, ps) mblen (s, n)
78 # define mbsinit(ps) (*(ps) == 0)
79 # endif
80 static const mbstate_t mbstate_zero;
81 #endif
83 #if HAVE_LIMITS_H
84 # include <limits.h>
85 #endif
87 #if STDC_HEADERS
88 # include <stddef.h>
89 # include <stdlib.h>
90 # include <string.h>
91 #else
92 # ifndef HAVE_MEMCPY
93 # define memcpy(d, s, n) bcopy ((s), (d), (n))
94 # endif
95 #endif
97 #ifdef _LIBC
98 # define MEMPCPY(d, s, n) __mempcpy (d, s, n)
99 #else
100 # ifndef HAVE_MEMPCPY
101 # define MEMPCPY(d, s, n) ((void *) ((char *) memcpy (d, s, n) + (n)))
102 # endif
103 #endif
105 #ifndef __P
106 # if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
107 # define __P(args) args
108 # else
109 # define __P(args) ()
110 # endif /* GCC. */
111 #endif /* Not __P. */
113 #ifndef PTR
114 # ifdef __STDC__
115 # define PTR void *
116 # else
117 # define PTR char *
118 # endif
119 #endif
121 #ifndef CHAR_BIT
122 # define CHAR_BIT 8
123 #endif
125 #ifndef NULL
126 # define NULL 0
127 #endif
129 #define TYPE_SIGNED(t) ((t) -1 < 0)
131 /* Bound on length of the string representing an integer value of type t.
132 Subtract one for the sign bit if t is signed;
133 302 / 1000 is log10 (2) rounded up;
134 add one for integer division truncation;
135 add one more for a minus sign if t is signed. */
136 #define INT_STRLEN_BOUND(t) \
137 ((sizeof (t) * CHAR_BIT - TYPE_SIGNED (t)) * 302 / 100 + 1 + TYPE_SIGNED (t))
139 #define TM_YEAR_BASE 1900
141 #ifndef __isleap
142 /* Nonzero if YEAR is a leap year (every 4 years,
143 except every 100th isn't, and every 400th is). */
144 # define __isleap(year) \
145 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
146 #endif
149 #ifdef _LIBC
150 # define gmtime_r __gmtime_r
151 # define localtime_r __localtime_r
152 # define tzname __tzname
153 # define tzset __tzset
154 #else
155 # if ! HAVE_LOCALTIME_R
156 # if ! HAVE_TM_GMTOFF
157 /* Approximate gmtime_r as best we can in its absence. */
158 # undef gmtime_r
159 # define gmtime_r my_gmtime_r
160 static struct tm *gmtime_r __P ((const time_t *, struct tm *));
161 static struct tm *
162 gmtime_r (t, tp)
163 const time_t *t;
164 struct tm *tp;
166 struct tm *l = gmtime (t);
167 if (! l)
168 return 0;
169 *tp = *l;
170 return tp;
172 # endif /* ! HAVE_TM_GMTOFF */
174 /* Approximate localtime_r as best we can in its absence. */
175 # undef localtime_r
176 # define localtime_r my_ftime_localtime_r
177 static struct tm *localtime_r __P ((const time_t *, struct tm *));
178 static struct tm *
179 localtime_r (t, tp)
180 const time_t *t;
181 struct tm *tp;
183 struct tm *l = localtime (t);
184 if (! l)
185 return 0;
186 *tp = *l;
187 return tp;
189 # endif /* ! HAVE_LOCALTIME_R */
190 #endif /* ! defined _LIBC */
193 #if !defined memset && !defined HAVE_MEMSET && !defined _LIBC
194 /* Some systems lack the `memset' function and we don't want to
195 introduce additional dependencies. */
196 /* The SGI compiler reportedly barfs on the trailing null
197 if we use a string constant as the initializer. 28 June 1997, rms. */
198 static const char spaces[16] = /* " " */
199 { ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ' };
200 static const char zeroes[16] = /* "0000000000000000" */
201 { '0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0' };
203 # define memset_space(P, Len) \
204 do { \
205 int _len = (Len); \
207 do \
209 int _this = _len > 16 ? 16 : _len; \
210 (P) = MEMPCPY ((P), spaces, _this); \
211 _len -= _this; \
213 while (_len > 0); \
214 } while (0)
216 # define memset_zero(P, Len) \
217 do { \
218 int _len = (Len); \
220 do \
222 int _this = _len > 16 ? 16 : _len; \
223 (P) = MEMPCPY ((P), zeroes, _this); \
224 _len -= _this; \
226 while (_len > 0); \
227 } while (0)
228 #else
229 # define memset_space(P, Len) (memset ((P), ' ', (Len)), (P) += (Len))
230 # define memset_zero(P, Len) (memset ((P), '0', (Len)), (P) += (Len))
231 #endif
233 #define add(n, f) \
234 do \
236 int _n = (n); \
237 int _delta = width - _n; \
238 int _incr = _n + (_delta > 0 ? _delta : 0); \
239 if (i + _incr >= maxsize) \
240 return 0; \
241 if (p) \
243 if (_delta > 0) \
245 if (pad == '0') \
246 memset_zero (p, _delta); \
247 else \
248 memset_space (p, _delta); \
250 f; \
251 p += _n; \
253 i += _incr; \
254 } while (0)
256 #define cpy(n, s) \
257 add ((n), \
258 if (to_lowcase) \
259 memcpy_lowcase (p, (s), _n); \
260 else if (to_uppcase) \
261 memcpy_uppcase (p, (s), _n); \
262 else \
263 memcpy ((PTR) p, (PTR) (s), _n))
267 #ifdef _LIBC
268 # define TOUPPER(Ch) toupper (Ch)
269 # define TOLOWER(Ch) tolower (Ch)
270 #else
271 # define TOUPPER(Ch) (islower (Ch) ? toupper (Ch) : (Ch))
272 # define TOLOWER(Ch) (isupper (Ch) ? tolower (Ch) : (Ch))
273 #endif
274 /* We don't use `isdigit' here since the locale dependent
275 interpretation is not what we want here. We only need to accept
276 the arabic digits in the ASCII range. One day there is perhaps a
277 more reliable way to accept other sets of digits. */
278 #define ISDIGIT(Ch) ((unsigned int) (Ch) - '0' <= 9)
280 static char *memcpy_lowcase __P ((char *dest, const char *src, size_t len));
282 static char *
283 memcpy_lowcase (dest, src, len)
284 char *dest;
285 const char *src;
286 size_t len;
288 while (len-- > 0)
289 dest[len] = TOLOWER ((unsigned char) src[len]);
290 return dest;
293 static char *memcpy_uppcase __P ((char *dest, const char *src, size_t len));
295 static char *
296 memcpy_uppcase (dest, src, len)
297 char *dest;
298 const char *src;
299 size_t len;
301 while (len-- > 0)
302 dest[len] = TOUPPER ((unsigned char) src[len]);
303 return dest;
307 #if ! HAVE_TM_GMTOFF
308 /* Yield the difference between *A and *B,
309 measured in seconds, ignoring leap seconds. */
310 # define tm_diff ftime_tm_diff
311 static int tm_diff __P ((const struct tm *, const struct tm *));
312 static int
313 tm_diff (a, b)
314 const struct tm *a;
315 const struct tm *b;
317 /* Compute intervening leap days correctly even if year is negative.
318 Take care to avoid int overflow in leap day calculations,
319 but it's OK to assume that A and B are close to each other. */
320 int a4 = (a->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (a->tm_year & 3);
321 int b4 = (b->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (b->tm_year & 3);
322 int a100 = a4 / 25 - (a4 % 25 < 0);
323 int b100 = b4 / 25 - (b4 % 25 < 0);
324 int a400 = a100 >> 2;
325 int b400 = b100 >> 2;
326 int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
327 int years = a->tm_year - b->tm_year;
328 int days = (365 * years + intervening_leap_days
329 + (a->tm_yday - b->tm_yday));
330 return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
331 + (a->tm_min - b->tm_min))
332 + (a->tm_sec - b->tm_sec));
334 #endif /* ! HAVE_TM_GMTOFF */
338 /* The number of days from the first day of the first ISO week of this
339 year to the year day YDAY with week day WDAY. ISO weeks start on
340 Monday; the first ISO week has the year's first Thursday. YDAY may
341 be as small as YDAY_MINIMUM. */
342 #define ISO_WEEK_START_WDAY 1 /* Monday */
343 #define ISO_WEEK1_WDAY 4 /* Thursday */
344 #define YDAY_MINIMUM (-366)
345 static int iso_week_days __P ((int, int));
346 #ifdef __GNUC__
347 __inline__
348 #endif
349 static int
350 iso_week_days (yday, wday)
351 int yday;
352 int wday;
354 /* Add enough to the first operand of % to make it nonnegative. */
355 int big_enough_multiple_of_7 = (-YDAY_MINIMUM / 7 + 2) * 7;
356 return (yday
357 - (yday - wday + ISO_WEEK1_WDAY + big_enough_multiple_of_7) % 7
358 + ISO_WEEK1_WDAY - ISO_WEEK_START_WDAY);
362 #if !(defined _NL_CURRENT || HAVE_STRFTIME)
363 static char const weekday_name[][10] =
365 "Sunday", "Monday", "Tuesday", "Wednesday",
366 "Thursday", "Friday", "Saturday"
368 static char const month_name[][10] =
370 "January", "February", "March", "April", "May", "June",
371 "July", "August", "September", "October", "November", "December"
373 #endif
376 #ifdef emacs
377 # define my_strftime emacs_strftime
378 #else
379 # define my_strftime strftime
380 #endif
382 #if !defined _LIBC && HAVE_TZNAME && HAVE_TZSET
383 /* Solaris 2.5 tzset sometimes modifies the storage returned by localtime.
384 Work around this bug by copying *tp before it might be munged. */
385 size_t _strftime_copytm __P ((char *, size_t, const char *,
386 const struct tm *));
387 size_t
388 my_strftime (s, maxsize, format, tp)
389 char *s;
390 size_t maxsize;
391 const char *format;
392 const struct tm *tp;
394 struct tm tmcopy;
395 tmcopy = *tp;
396 return _strftime_copytm (s, maxsize, format, &tmcopy);
398 # undef my_strftime
399 # define my_strftime(S, Maxsize, Format, Tp) \
400 _strftime_copytm (S, Maxsize, Format, Tp)
401 #endif
404 /* Write information from TP into S according to the format
405 string FORMAT, writing no more that MAXSIZE characters
406 (including the terminating '\0') and returning number of
407 characters written. If S is NULL, nothing will be written
408 anywhere, so to determine how many characters would be
409 written, use NULL for S and (size_t) UINT_MAX for MAXSIZE. */
410 size_t
411 my_strftime (s, maxsize, format, tp)
412 char *s;
413 size_t maxsize;
414 const char *format;
415 const struct tm *tp;
417 int hour12 = tp->tm_hour;
418 #ifdef _NL_CURRENT
419 const char *const a_wkday = _NL_CURRENT (LC_TIME, ABDAY_1 + tp->tm_wday);
420 const char *const f_wkday = _NL_CURRENT (LC_TIME, DAY_1 + tp->tm_wday);
421 const char *const a_month = _NL_CURRENT (LC_TIME, ABMON_1 + tp->tm_mon);
422 const char *const f_month = _NL_CURRENT (LC_TIME, MON_1 + tp->tm_mon);
423 const char *const ampm = _NL_CURRENT (LC_TIME,
424 hour12 > 11 ? PM_STR : AM_STR);
425 size_t aw_len = strlen (a_wkday);
426 size_t am_len = strlen (a_month);
427 size_t ap_len = strlen (ampm);
428 #else
429 # if !HAVE_STRFTIME
430 const char *const f_wkday = weekday_name[tp->tm_wday];
431 const char *const f_month = month_name[tp->tm_mon];
432 const char *const a_wkday = f_wkday;
433 const char *const a_month = f_month;
434 const char *const ampm = "AMPM" + 2 * (hour12 > 11);
435 size_t aw_len = 3;
436 size_t am_len = 3;
437 size_t ap_len = 2;
438 # endif
439 #endif
440 #if defined _NL_CURRENT || !HAVE_STRFTIME
441 size_t wkday_len = strlen (f_wkday);
442 size_t month_len = strlen (f_month);
443 #endif
444 const char *zone;
445 size_t zonelen;
446 size_t i = 0;
447 char *p = s;
448 const char *f;
450 zone = NULL;
451 #if HAVE_TM_ZONE
452 /* The POSIX test suite assumes that setting
453 the environment variable TZ to a new value before calling strftime()
454 will influence the result (the %Z format) even if the information in
455 TP is computed with a totally different time zone.
456 This is bogus: though POSIX allows bad behavior like this,
457 POSIX does not require it. Do the right thing instead. */
458 zone = (const char *) tp->tm_zone;
459 #endif
460 #if HAVE_TZNAME
461 /* POSIX.1 8.1.1 requires that whenever strftime() is called, the
462 time zone names contained in the external variable `tzname' shall
463 be set as if the tzset() function had been called. */
464 # if HAVE_TZSET
465 tzset ();
466 # endif
468 if (!(zone && *zone) && tp->tm_isdst >= 0)
469 zone = tzname[tp->tm_isdst];
470 #endif
471 if (! zone)
472 zone = ""; /* POSIX.2 requires the empty string here. */
474 zonelen = strlen (zone);
476 if (hour12 > 12)
477 hour12 -= 12;
478 else
479 if (hour12 == 0) hour12 = 12;
481 for (f = format; *f != '\0'; ++f)
483 int pad = 0; /* Padding for number ('-', '_', or 0). */
484 int modifier; /* Field modifier ('E', 'O', or 0). */
485 int digits; /* Max digits for numeric format. */
486 int number_value; /* Numeric value to be printed. */
487 int negative_number; /* 1 if the number is negative. */
488 const char *subfmt;
489 char *bufp;
490 char buf[1 + (sizeof (int) < sizeof (time_t)
491 ? INT_STRLEN_BOUND (time_t)
492 : INT_STRLEN_BOUND (int))];
493 int width = -1;
494 int to_lowcase = 0;
495 int to_uppcase = 0;
496 int change_case = 0;
497 int format_char;
499 #if DO_MULTIBYTE
501 switch (*f)
503 case '%':
504 break;
506 case '\a': case '\b': case '\t': case '\n':
507 case '\v': case '\f': case '\r':
508 case ' ': case '!': case '"': case '#': case '&': case'\'':
509 case '(': case ')': case '*': case '+': case ',': case '-':
510 case '.': case '/': case '0': case '1': case '2': case '3':
511 case '4': case '5': case '6': case '7': case '8': case '9':
512 case ':': case ';': case '<': case '=': case '>': case '?':
513 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
514 case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
515 case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
516 case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
517 case 'Y': case 'Z': case '[': case'\\': case ']': case '^':
518 case '_': case 'a': case 'b': case 'c': case 'd': case 'e':
519 case 'f': case 'g': case 'h': case 'i': case 'j': case 'k':
520 case 'l': case 'm': case 'n': case 'o': case 'p': case 'q':
521 case 'r': case 's': case 't': case 'u': case 'v': case 'w':
522 case 'x': case 'y': case 'z': case '{': case '|': case '}':
523 case '~':
524 /* The C Standard requires these 98 characters (plus '%') to
525 be in the basic execution character set. None of these
526 characters can start a multibyte sequence, so they need
527 not be analyzed further. */
528 add (1, *p = *f);
529 continue;
531 default:
532 /* Copy this multibyte sequence until we reach its end, find
533 an error, or come back to the initial shift state. */
535 mbstate_t mbstate = mbstate_zero;
536 size_t len = 0;
540 size_t bytes = mbrlen (f + len, (size_t) -1, &mbstate);
542 if (bytes == 0)
543 break;
545 if (bytes == (size_t) -2)
547 len += strlen (f + len);
548 break;
551 if (bytes == (size_t) -1)
553 len++;
554 break;
557 len += bytes;
559 while (! mbsinit (&mbstate));
561 cpy (len, f);
562 f += len - 1;
563 continue;
567 #else /* ! DO_MULTIBYTE */
569 /* Either multibyte encodings are not supported, or they are
570 safe for formats, so any non-'%' byte can be copied through. */
571 if (*f != '%')
573 add (1, *p = *f);
574 continue;
577 #endif /* ! DO_MULTIBYTE */
579 /* Check for flags that can modify a format. */
580 while (1)
582 switch (*++f)
584 /* This influences the number formats. */
585 case '_':
586 case '-':
587 case '0':
588 pad = *f;
589 continue;
591 /* This changes textual output. */
592 case '^':
593 to_uppcase = 1;
594 continue;
595 case '#':
596 change_case = 1;
597 continue;
599 default:
600 break;
602 break;
605 /* As a GNU extension we allow to specify the field width. */
606 if (ISDIGIT (*f))
608 width = 0;
611 width *= 10;
612 width += *f - '0';
613 ++f;
615 while (ISDIGIT (*f));
618 /* Check for modifiers. */
619 switch (*f)
621 case 'E':
622 case 'O':
623 modifier = *f++;
624 break;
626 default:
627 modifier = 0;
628 break;
631 /* Now do the specified format. */
632 format_char = *f;
633 switch (format_char)
635 #define DO_NUMBER(d, v) \
636 digits = width == -1 ? d : width; \
637 number_value = v; goto do_number
638 #define DO_NUMBER_SPACEPAD(d, v) \
639 digits = width == -1 ? d : width; \
640 number_value = v; goto do_number_spacepad
642 case '%':
643 if (modifier != 0)
644 goto bad_format;
645 add (1, *p = *f);
646 break;
648 case 'a':
649 if (modifier != 0)
650 goto bad_format;
651 if (change_case)
653 to_uppcase = 1;
654 to_lowcase = 0;
656 #if defined _NL_CURRENT || !HAVE_STRFTIME
657 cpy (aw_len, a_wkday);
658 break;
659 #else
660 goto underlying_strftime;
661 #endif
663 case 'A':
664 if (modifier != 0)
665 goto bad_format;
666 if (change_case)
668 to_uppcase = 1;
669 to_lowcase = 0;
671 #if defined _NL_CURRENT || !HAVE_STRFTIME
672 cpy (wkday_len, f_wkday);
673 break;
674 #else
675 goto underlying_strftime;
676 #endif
678 case 'b':
679 case 'h': /* POSIX.2 extension. */
680 if (modifier != 0)
681 goto bad_format;
682 #if defined _NL_CURRENT || !HAVE_STRFTIME
683 cpy (am_len, a_month);
684 break;
685 #else
686 goto underlying_strftime;
687 #endif
689 case 'B':
690 if (modifier != 0)
691 goto bad_format;
692 if (change_case)
694 to_uppcase = 1;
695 to_lowcase = 0;
697 #if defined _NL_CURRENT || !HAVE_STRFTIME
698 cpy (month_len, f_month);
699 break;
700 #else
701 goto underlying_strftime;
702 #endif
704 case 'c':
705 if (modifier == 'O')
706 goto bad_format;
707 #ifdef _NL_CURRENT
708 if (! (modifier == 'E'
709 && *(subfmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT)) != '\0'))
710 subfmt = _NL_CURRENT (LC_TIME, D_T_FMT);
711 #else
712 # if HAVE_STRFTIME
713 goto underlying_strftime;
714 # else
715 subfmt = "%a %b %e %H:%M:%S %Y";
716 # endif
717 #endif
719 subformat:
721 char *old_start = p;
722 size_t len = my_strftime (NULL, maxsize - i, subfmt, tp);
723 if (len == 0 && *subfmt)
724 return 0;
725 add (len, my_strftime (p, maxsize - i, subfmt, tp));
727 if (to_uppcase)
728 while (old_start < p)
730 *old_start = TOUPPER ((unsigned char) *old_start);
731 ++old_start;
734 break;
736 #if HAVE_STRFTIME && ! (defined _NL_CURRENT && HAVE_STRUCT_ERA_ENTRY)
737 underlying_strftime:
739 /* The relevant information is available only via the
740 underlying strftime implementation, so use that. */
741 char ufmt[4];
742 char *u = ufmt;
743 char ubuf[1024]; /* enough for any single format in practice */
744 size_t len;
745 *u++ = '%';
746 if (modifier != 0)
747 *u++ = modifier;
748 *u++ = format_char;
749 *u = '\0';
750 len = strftime (ubuf, sizeof ubuf, ufmt, tp);
751 if (len == 0)
752 return 0;
753 cpy (len, ubuf);
755 break;
756 #endif
758 case 'C': /* POSIX.2 extension. */
759 if (modifier == 'O')
760 goto bad_format;
761 if (modifier == 'E')
763 #if HAVE_STRUCT_ERA_ENTRY
764 struct era_entry *era = _nl_get_era_entry (tp);
765 if (era)
767 size_t len = strlen (era->name_fmt);
768 cpy (len, era->name_fmt);
769 break;
771 #else
772 # if HAVE_STRFTIME
773 goto underlying_strftime;
774 # endif
775 #endif
779 int year = tp->tm_year + TM_YEAR_BASE;
780 DO_NUMBER (1, year / 100 - (year % 100 < 0));
783 case 'x':
784 if (modifier == 'O')
785 goto bad_format;
786 #ifdef _NL_CURRENT
787 if (! (modifier == 'E'
788 && *(subfmt = _NL_CURRENT (LC_TIME, ERA_D_FMT)) != '\0'))
789 subfmt = _NL_CURRENT (LC_TIME, D_FMT);
790 goto subformat;
791 #else
792 # if HAVE_STRFTIME
793 goto underlying_strftime;
794 # else
795 /* Fall through. */
796 # endif
797 #endif
798 case 'D': /* POSIX.2 extension. */
799 if (modifier != 0)
800 goto bad_format;
801 subfmt = "%m/%d/%y";
802 goto subformat;
804 case 'd':
805 if (modifier == 'E')
806 goto bad_format;
808 DO_NUMBER (2, tp->tm_mday);
810 case 'e': /* POSIX.2 extension. */
811 if (modifier == 'E')
812 goto bad_format;
814 DO_NUMBER_SPACEPAD (2, tp->tm_mday);
816 /* All numeric formats set DIGITS and NUMBER_VALUE and then
817 jump to one of these two labels. */
819 do_number_spacepad:
820 /* Force `_' flag unless overwritten by `0' flag. */
821 if (pad != '0')
822 pad = '_';
824 do_number:
825 /* Format the number according to the MODIFIER flag. */
827 if (modifier == 'O' && 0 <= number_value)
829 #ifdef _NL_CURRENT
830 /* Get the locale specific alternate representation of
831 the number NUMBER_VALUE. If none exist NULL is returned. */
832 const char *cp = _nl_get_alt_digit (number_value);
834 if (cp != NULL)
836 size_t digitlen = strlen (cp);
837 if (digitlen != 0)
839 cpy (digitlen, cp);
840 break;
843 #else
844 # if HAVE_STRFTIME
845 goto underlying_strftime;
846 # endif
847 #endif
850 unsigned int u = number_value;
852 bufp = buf + sizeof (buf);
853 negative_number = number_value < 0;
855 if (negative_number)
856 u = -u;
859 *--bufp = u % 10 + '0';
860 while ((u /= 10) != 0);
863 do_number_sign_and_padding:
864 if (negative_number)
865 *--bufp = '-';
867 if (pad != '-')
869 int padding = digits - (buf + sizeof (buf) - bufp);
871 if (pad == '_')
873 while (0 < padding--)
874 *--bufp = ' ';
876 else
878 bufp += negative_number;
879 while (0 < padding--)
880 *--bufp = '0';
881 if (negative_number)
882 *--bufp = '-';
886 cpy (buf + sizeof (buf) - bufp, bufp);
887 break;
889 case 'F':
890 if (modifier != 0)
891 goto bad_format;
892 subfmt = "%Y-%m-%d";
893 goto subformat;
895 case 'H':
896 if (modifier == 'E')
897 goto bad_format;
899 DO_NUMBER (2, tp->tm_hour);
901 case 'I':
902 if (modifier == 'E')
903 goto bad_format;
905 DO_NUMBER (2, hour12);
907 case 'k': /* GNU extension. */
908 if (modifier == 'E')
909 goto bad_format;
911 DO_NUMBER_SPACEPAD (2, tp->tm_hour);
913 case 'l': /* GNU extension. */
914 if (modifier == 'E')
915 goto bad_format;
917 DO_NUMBER_SPACEPAD (2, hour12);
919 case 'j':
920 if (modifier == 'E')
921 goto bad_format;
923 DO_NUMBER (3, 1 + tp->tm_yday);
925 case 'M':
926 if (modifier == 'E')
927 goto bad_format;
929 DO_NUMBER (2, tp->tm_min);
931 case 'm':
932 if (modifier == 'E')
933 goto bad_format;
935 DO_NUMBER (2, tp->tm_mon + 1);
937 case 'n': /* POSIX.2 extension. */
938 add (1, *p = '\n');
939 break;
941 case 'P':
942 to_lowcase = 1;
943 #if !defined _NL_CURRENT && HAVE_STRFTIME
944 format_char = 'p';
945 #endif
946 /* FALLTHROUGH */
948 case 'p':
949 if (change_case)
951 to_uppcase = 0;
952 to_lowcase = 1;
954 #if defined _NL_CURRENT || !HAVE_STRFTIME
955 cpy (ap_len, ampm);
956 break;
957 #else
958 goto underlying_strftime;
959 #endif
961 case 'R': /* GNU extension. */
962 subfmt = "%H:%M";
963 goto subformat;
965 case 'r': /* POSIX.2 extension. */
966 #ifdef _NL_CURRENT
967 if (*(subfmt = _NL_CURRENT (LC_TIME, T_FMT_AMPM)) == '\0')
968 #endif
969 subfmt = "%I:%M:%S %p";
970 goto subformat;
972 case 'S':
973 if (modifier == 'E')
974 goto bad_format;
976 DO_NUMBER (2, tp->tm_sec);
978 case 's': /* GNU extension. */
980 struct tm ltm;
981 time_t t;
983 ltm = *tp;
984 t = mktime (&ltm);
986 /* Generate string value for T using time_t arithmetic;
987 this works even if sizeof (long) < sizeof (time_t). */
989 bufp = buf + sizeof (buf);
990 negative_number = t < 0;
994 int d = t % 10;
995 t /= 10;
997 if (negative_number)
999 d = -d;
1001 /* Adjust if division truncates to minus infinity. */
1002 if (0 < -1 % 10 && d < 0)
1004 t++;
1005 d += 10;
1009 *--bufp = d + '0';
1011 while (t != 0);
1013 digits = 1;
1014 goto do_number_sign_and_padding;
1017 case 'X':
1018 if (modifier == 'O')
1019 goto bad_format;
1020 #ifdef _NL_CURRENT
1021 if (! (modifier == 'E'
1022 && *(subfmt = _NL_CURRENT (LC_TIME, ERA_T_FMT)) != '\0'))
1023 subfmt = _NL_CURRENT (LC_TIME, T_FMT);
1024 goto subformat;
1025 #else
1026 # if HAVE_STRFTIME
1027 goto underlying_strftime;
1028 # else
1029 /* Fall through. */
1030 # endif
1031 #endif
1032 case 'T': /* POSIX.2 extension. */
1033 subfmt = "%H:%M:%S";
1034 goto subformat;
1036 case 't': /* POSIX.2 extension. */
1037 add (1, *p = '\t');
1038 break;
1040 case 'f':
1041 case 'u': /* POSIX.2 extension. */
1042 DO_NUMBER (1, (tp->tm_wday - 1 + 7) % 7 + 1);
1044 case 'U':
1045 if (modifier == 'E')
1046 goto bad_format;
1048 DO_NUMBER (2, (tp->tm_yday - tp->tm_wday + 7) / 7);
1050 case 'V':
1051 case 'g': /* GNU extension. */
1052 case 'G': /* GNU extension. */
1053 if (modifier == 'E')
1054 goto bad_format;
1056 int year = tp->tm_year + TM_YEAR_BASE;
1057 int days = iso_week_days (tp->tm_yday, tp->tm_wday);
1059 if (days < 0)
1061 /* This ISO week belongs to the previous year. */
1062 year--;
1063 days = iso_week_days (tp->tm_yday + (365 + __isleap (year)),
1064 tp->tm_wday);
1066 else
1068 int d = iso_week_days (tp->tm_yday - (365 + __isleap (year)),
1069 tp->tm_wday);
1070 if (0 <= d)
1072 /* This ISO week belongs to the next year. */
1073 year++;
1074 days = d;
1078 switch (*f)
1080 case 'g':
1081 DO_NUMBER (2, (year % 100 + 100) % 100);
1083 case 'G':
1084 DO_NUMBER (1, year);
1086 default:
1087 DO_NUMBER (2, days / 7 + 1);
1091 case 'W':
1092 if (modifier == 'E')
1093 goto bad_format;
1095 DO_NUMBER (2, (tp->tm_yday - (tp->tm_wday - 1 + 7) % 7 + 7) / 7);
1097 case 'w':
1098 if (modifier == 'E')
1099 goto bad_format;
1101 DO_NUMBER (1, tp->tm_wday);
1103 case 'Y':
1104 if (modifier == 'E')
1106 #if HAVE_STRUCT_ERA_ENTRY
1107 struct era_entry *era = _nl_get_era_entry (tp);
1108 if (era)
1110 subfmt = strchr (era->name_fmt, '\0') + 1;
1111 goto subformat;
1113 #else
1114 # if HAVE_STRFTIME
1115 goto underlying_strftime;
1116 # endif
1117 #endif
1119 if (modifier == 'O')
1120 goto bad_format;
1121 else
1122 DO_NUMBER (1, tp->tm_year + TM_YEAR_BASE);
1124 case 'y':
1125 if (modifier == 'E')
1127 #if HAVE_STRUCT_ERA_ENTRY
1128 struct era_entry *era = _nl_get_era_entry (tp);
1129 if (era)
1131 int delta = tp->tm_year - era->start_date[0];
1132 DO_NUMBER (1, (era->offset
1133 + (era->direction == '-' ? -delta : delta)));
1135 #else
1136 # if HAVE_STRFTIME
1137 goto underlying_strftime;
1138 # endif
1139 #endif
1141 DO_NUMBER (2, (tp->tm_year % 100 + 100) % 100);
1143 case 'Z':
1144 if (change_case)
1146 to_uppcase = 0;
1147 to_lowcase = 1;
1149 cpy (zonelen, zone);
1150 break;
1152 case 'z': /* GNU extension. */
1153 if (tp->tm_isdst < 0)
1154 break;
1157 int diff;
1158 #if HAVE_TM_GMTOFF
1159 diff = tp->tm_gmtoff;
1160 #else
1161 struct tm gtm;
1162 struct tm ltm;
1163 time_t lt;
1165 ltm = *tp;
1166 lt = mktime (&ltm);
1168 if (lt == (time_t) -1)
1170 /* mktime returns -1 for errors, but -1 is also a
1171 valid time_t value. Check whether an error really
1172 occurred. */
1173 struct tm tm;
1175 if (! localtime_r (&lt, &tm)
1176 || ((ltm.tm_sec ^ tm.tm_sec)
1177 | (ltm.tm_min ^ tm.tm_min)
1178 | (ltm.tm_hour ^ tm.tm_hour)
1179 | (ltm.tm_mday ^ tm.tm_mday)
1180 | (ltm.tm_mon ^ tm.tm_mon)
1181 | (ltm.tm_year ^ tm.tm_year)))
1182 break;
1185 if (! gmtime_r (&lt, &gtm))
1186 break;
1188 diff = tm_diff (&ltm, &gtm);
1189 #endif
1191 if (diff < 0)
1193 add (1, *p = '-');
1194 diff = -diff;
1196 else
1197 add (1, *p = '+');
1199 diff /= 60;
1200 DO_NUMBER (4, (diff / 60) * 100 + diff % 60);
1203 case '\0': /* GNU extension: % at end of format. */
1204 --f;
1205 /* Fall through. */
1206 default:
1207 /* Unknown format; output the format, including the '%',
1208 since this is most likely the right thing to do if a
1209 multibyte string has been misparsed. */
1210 bad_format:
1212 int flen;
1213 for (flen = 1; f[1 - flen] != '%'; flen++)
1214 continue;
1215 cpy (flen, &f[1 - flen]);
1217 break;
1221 if (p)
1222 *p = '\0';
1223 return i;