1 /* Copyright (C) 1991-1999, 2000, 2001, 2002 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 Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the 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 Lesser General Public License for more details.
14 You should have received a copy of the GNU Lesser General Public
15 License along with the GNU C Library; if not, write to the Free
16 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
24 # define HAVE_LIMITS_H 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
32 # define MULTIBYTE_IS_FORMAT_SAFE 1
33 # define STDC_HEADERS 1
34 # include "../locale/localeinfo.h"
37 #if defined emacs && !defined HAVE_BCOPY
38 # define HAVE_MEMCPY 1
42 #include <sys/types.h> /* Some systems define `time_t' here. */
44 #ifdef TIME_WITH_SYS_TIME
45 # include <sys/time.h>
48 # ifdef HAVE_SYS_TIME_H
49 # include <sys/time.h>
55 extern char *tzname
[];
58 /* Do multibyte processing if multibytes are supported, unless
59 multibyte sequences are safe in formats. Multibyte sequences are
60 safe if they cannot contain byte sequences that look like format
61 conversion specifications. The GNU C Library uses UTF8 multibyte
62 encoding, which is safe for formats, but strftime.c can be used
63 with other C libraries that use unsafe encodings. */
64 #define DO_MULTIBYTE (HAVE_MBLEN && ! MULTIBYTE_IS_FORMAT_SAFE)
70 /* Simulate mbrlen with mblen as best we can. */
71 # define mbstate_t int
72 # define mbrlen(s, n, ps) mblen (s, n)
73 # define mbsinit(ps) (*(ps) == 0)
75 static const mbstate_t mbstate_zero
;
88 # define memcpy(d, s, n) bcopy ((s), (d), (n))
94 # define CHAR_T wchar_t
95 # define UCHAR_T unsigned int
96 # define L_(Str) L##Str
97 # define NLW(Sym) _NL_W##Sym
99 # define MEMCPY(d, s, n) __wmemcpy (d, s, n)
100 # define STRLEN(s) __wcslen (s)
104 # define UCHAR_T unsigned char
106 # define NLW(Sym) Sym
108 # if !defined STDC_HEADERS && !defined HAVE_MEMCPY
109 # define MEMCPY(d, s, n) bcopy ((s), (d), (n))
111 # define MEMCPY(d, s, n) memcpy ((d), (s), (n))
113 # define STRLEN(s) strlen (s)
116 # define MEMPCPY(d, s, n) __mempcpy (d, s, n)
118 # ifndef HAVE_MEMPCPY
119 # define MEMPCPY(d, s, n) ((void *) ((char *) memcpy (d, s, n) + (n)))
125 # if defined __GNUC__ || (defined __STDC__ && __STDC__)
126 # define __P(args) args
128 # define __P(args) ()
130 #endif /* Not __P. */
148 #define TYPE_SIGNED(t) ((t) -1 < 0)
150 /* Bound on length of the string representing an integer value of type t.
151 Subtract one for the sign bit if t is signed;
152 302 / 1000 is log10 (2) rounded up;
153 add one for integer division truncation;
154 add one more for a minus sign if t is signed. */
155 #define INT_STRLEN_BOUND(t) \
156 ((sizeof (t) * CHAR_BIT - TYPE_SIGNED (t)) * 302 / 1000 + 1 + TYPE_SIGNED (t))
158 #define TM_YEAR_BASE 1900
161 /* Nonzero if YEAR is a leap year (every 4 years,
162 except every 100th isn't, and every 400th is). */
163 # define __isleap(year) \
164 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
169 # define my_strftime_gmtime_r __gmtime_r
170 # define my_strftime_localtime_r __localtime_r
171 # define tzname __tzname
172 # define tzset __tzset
175 /* If we're a strftime substitute in a GNU program, then prefer gmtime
176 to gmtime_r, since many gmtime_r implementations are buggy.
177 Similarly for localtime_r. */
179 # if ! HAVE_TM_GMTOFF
180 static struct tm
*my_strftime_gmtime_r
__P ((const time_t *, struct tm
*));
182 my_strftime_gmtime_r (t
, tp
)
186 struct tm
*l
= gmtime (t
);
192 # endif /* ! HAVE_TM_GMTOFF */
194 static struct tm
*my_strftime_localtime_r
__P ((const time_t *, struct tm
*));
196 my_strftime_localtime_r (t
, tp
)
200 struct tm
*l
= localtime (t
);
206 #endif /* ! defined _LIBC */
209 #if !defined memset && !defined HAVE_MEMSET && !defined _LIBC
210 /* Some systems lack the `memset' function and we don't want to
211 introduce additional dependencies. */
212 /* The SGI compiler reportedly barfs on the trailing null
213 if we use a string constant as the initializer. 28 June 1997, rms. */
214 static const CHAR_T spaces
[16] = /* " " */
216 L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),
217 L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),L_(' ')
219 static const CHAR_T zeroes
[16] = /* "0000000000000000" */
221 L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),
222 L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),L_('0')
225 # define memset_space(P, Len) \
231 int _this = _len > 16 ? 16 : _len; \
232 (P) = MEMPCPY ((P), spaces, _this * sizeof (CHAR_T)); \
238 # define memset_zero(P, Len) \
244 int _this = _len > 16 ? 16 : _len; \
245 (P) = MEMPCPY ((P), zeroes, _this * sizeof (CHAR_T)); \
252 # define memset_space(P, Len) (wmemset ((P), L' ', (Len)), (P) += (Len))
253 # define memset_zero(P, Len) (wmemset ((P), L'0', (Len)), (P) += (Len))
255 # define memset_space(P, Len) (memset ((P), ' ', (Len)), (P) += (Len))
256 # define memset_zero(P, Len) (memset ((P), '0', (Len)), (P) += (Len))
264 int _delta = width - _n; \
265 int _incr = _n + (_delta > 0 ? _delta : 0); \
266 if (i + _incr >= maxsize) \
272 if (pad == L_('0')) \
273 memset_zero (p, _delta); \
275 memset_space (p, _delta); \
286 memcpy_lowcase (p, (s), _n); \
287 else if (to_uppcase) \
288 memcpy_uppcase (p, (s), _n); \
290 MEMCPY ((PTR) p, (const PTR) (s), _n))
293 # define widen(os, ws, l) \
296 const char *__s = os; \
297 memset (&__st, '\0', sizeof (__st)); \
298 l = __mbsrtowcs (NULL, &__s, 0, &__st); \
299 ws = alloca ((l + 1) * sizeof (wchar_t)); \
300 (void) __mbsrtowcs (ws, &__s, l, &__st); \
305 #if defined _LIBC && defined USE_IN_EXTENDED_LOCALE_MODEL
306 /* We use this code also for the extended locale handling where the
307 function gets as an additional argument the locale which has to be
308 used. To access the values we have to redefine the _NL_CURRENT
310 # define strftime __strftime_l
311 # define wcsftime __wcsftime_l
313 # define _NL_CURRENT(category, item) \
314 (current->values[_NL_ITEM_INDEX (item)].string)
315 # define LOCALE_PARAM , loc
316 # define LOCALE_ARG , loc
317 # define LOCALE_PARAM_DECL __locale_t loc;
319 # define LOCALE_PARAM
321 # define LOCALE_PARAM_DECL
325 # define TOUPPER(Ch) towupper (Ch)
326 # define TOLOWER(Ch) towlower (Ch)
329 # define TOUPPER(Ch) toupper (Ch)
330 # define TOLOWER(Ch) tolower (Ch)
332 # define TOUPPER(Ch) (islower (Ch) ? toupper (Ch) : (Ch))
333 # define TOLOWER(Ch) (isupper (Ch) ? tolower (Ch) : (Ch))
336 /* We don't use `isdigit' here since the locale dependent
337 interpretation is not what we want here. We only need to accept
338 the arabic digits in the ASCII range. One day there is perhaps a
339 more reliable way to accept other sets of digits. */
340 #define ISDIGIT(Ch) ((unsigned int) (Ch) - L_('0') <= 9)
342 static CHAR_T
*memcpy_lowcase
__P ((CHAR_T
*dest
, const CHAR_T
*src
,
346 memcpy_lowcase (dest
, src
, len
)
352 dest
[len
] = TOLOWER ((UCHAR_T
) src
[len
]);
356 static CHAR_T
*memcpy_uppcase
__P ((CHAR_T
*dest
, const CHAR_T
*src
,
360 memcpy_uppcase (dest
, src
, len
)
366 dest
[len
] = TOUPPER ((UCHAR_T
) src
[len
]);
372 /* Yield the difference between *A and *B,
373 measured in seconds, ignoring leap seconds. */
374 # define tm_diff ftime_tm_diff
375 static int tm_diff
__P ((const struct tm
*, const struct tm
*));
381 /* Compute intervening leap days correctly even if year is negative.
382 Take care to avoid int overflow in leap day calculations,
383 but it's OK to assume that A and B are close to each other. */
384 int a4
= (a
->tm_year
>> 2) + (TM_YEAR_BASE
>> 2) - ! (a
->tm_year
& 3);
385 int b4
= (b
->tm_year
>> 2) + (TM_YEAR_BASE
>> 2) - ! (b
->tm_year
& 3);
386 int a100
= a4
/ 25 - (a4
% 25 < 0);
387 int b100
= b4
/ 25 - (b4
% 25 < 0);
388 int a400
= a100
>> 2;
389 int b400
= b100
>> 2;
390 int intervening_leap_days
= (a4
- b4
) - (a100
- b100
) + (a400
- b400
);
391 int years
= a
->tm_year
- b
->tm_year
;
392 int days
= (365 * years
+ intervening_leap_days
393 + (a
->tm_yday
- b
->tm_yday
));
394 return (60 * (60 * (24 * days
+ (a
->tm_hour
- b
->tm_hour
))
395 + (a
->tm_min
- b
->tm_min
))
396 + (a
->tm_sec
- b
->tm_sec
));
398 #endif /* ! HAVE_TM_GMTOFF */
402 /* The number of days from the first day of the first ISO week of this
403 year to the year day YDAY with week day WDAY. ISO weeks start on
404 Monday; the first ISO week has the year's first Thursday. YDAY may
405 be as small as YDAY_MINIMUM. */
406 #define ISO_WEEK_START_WDAY 1 /* Monday */
407 #define ISO_WEEK1_WDAY 4 /* Thursday */
408 #define YDAY_MINIMUM (-366)
409 static int iso_week_days
__P ((int, int));
414 iso_week_days (yday
, wday
)
418 /* Add enough to the first operand of % to make it nonnegative. */
419 int big_enough_multiple_of_7
= (-YDAY_MINIMUM
/ 7 + 2) * 7;
421 - (yday
- wday
+ ISO_WEEK1_WDAY
+ big_enough_multiple_of_7
) % 7
422 + ISO_WEEK1_WDAY
- ISO_WEEK_START_WDAY
);
426 #if !(defined _NL_CURRENT || HAVE_STRFTIME)
427 static CHAR_T
const weekday_name
[][10] =
429 L_("Sunday"), L_("Monday"), L_("Tuesday"), L_("Wednesday"),
430 L_("Thursday"), L_("Friday"), L_("Saturday")
432 static CHAR_T
const month_name
[][10] =
434 L_("January"), L_("February"), L_("March"), L_("April"), L_("May"),
435 L_("June"), L_("July"), L_("August"), L_("September"), L_("October"),
436 L_("November"), L_("December")
442 # define my_strftime emacs_strftimeu
443 # define ut_argument , ut
444 # define ut_argument_spec int ut;
445 # define ut_argument_spec_iso , int ut
448 # define my_strftime wcsftime
450 # define my_strftime strftime
453 # define ut_argument_spec
454 # define ut_argument_spec_iso
455 /* We don't have this information in general. */
459 #if !defined _LIBC && HAVE_TZNAME && HAVE_TZSET
460 /* Solaris 2.5 tzset sometimes modifies the storage returned by localtime.
461 Work around this bug by copying *tp before it might be munged. */
462 size_t _strftime_copytm
__P ((char *, size_t, const char *,
463 const struct tm
* ut_argument_spec_iso
));
465 my_strftime (s
, maxsize
, format
, tp ut_argument
)
468 const CHAR_T
*format
;
474 return _strftime_copytm (s
, maxsize
, format
, &tmcopy ut_argument
);
477 # define my_strftime _strftime_copytm
481 /* Write information from TP into S according to the format
482 string FORMAT, writing no more that MAXSIZE characters
483 (including the terminating '\0') and returning number of
484 characters written. If S is NULL, nothing will be written
485 anywhere, so to determine how many characters would be
486 written, use NULL for S and (size_t) UINT_MAX for MAXSIZE. */
488 my_strftime (s
, maxsize
, format
, tp ut_argument LOCALE_PARAM
)
491 const CHAR_T
*format
;
496 #if defined _LIBC && defined USE_IN_EXTENDED_LOCALE_MODEL
497 const struct locale_data
*const current
= loc
->__locales
[LC_TIME
];
500 int hour12
= tp
->tm_hour
;
502 /* We cannot make the following values variables since we must delay
503 the evaluation of these values until really needed since some
504 expressions might not be valid in every situation. The `struct tm'
505 might be generated by a strptime() call that initialized
506 only a few elements. Dereference the pointers only if the format
507 requires this. Then it is ok to fail if the pointers are invalid. */
509 ((const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(ABDAY_1) + tp->tm_wday))
511 ((const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(DAY_1) + tp->tm_wday))
513 ((const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(ABMON_1) + tp->tm_mon))
515 ((const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(MON_1) + tp->tm_mon))
517 ((const CHAR_T *) _NL_CURRENT (LC_TIME, tp->tm_hour > 11 \
518 ? NLW(PM_STR) : NLW(AM_STR)))
520 # define aw_len STRLEN (a_wkday)
521 # define am_len STRLEN (a_month)
522 # define ap_len STRLEN (ampm)
525 # define f_wkday (weekday_name[tp->tm_wday])
526 # define f_month (month_name[tp->tm_mon])
527 # define a_wkday f_wkday
528 # define a_month f_month
529 # define ampm (L_("AMPM") + 2 * (tp->tm_hour > 11))
540 #if DO_MULTIBYTE && !defined COMPILE_WIDE
541 const char *format_end
= NULL
;
546 /* The POSIX test suite assumes that setting
547 the environment variable TZ to a new value before calling strftime()
548 will influence the result (the %Z format) even if the information in
549 TP is computed with a totally different time zone.
550 This is bogus: though POSIX allows bad behavior like this,
551 POSIX does not require it. Do the right thing instead. */
552 zone
= (const char *) tp
->tm_zone
;
557 if (! (zone
&& *zone
))
562 /* POSIX.1 requires that local time zone information is used as
563 though strftime called tzset. */
576 for (f
= format
; *f
!= '\0'; ++f
)
578 int pad
= 0; /* Padding for number ('-', '_', or 0). */
579 int modifier
; /* Field modifier ('E', 'O', or 0). */
580 int digits
; /* Max digits for numeric format. */
581 int number_value
; /* Numeric value to be printed. */
582 int negative_number
; /* 1 if the number is negative. */
583 const CHAR_T
*subfmt
;
585 CHAR_T buf
[1 + (sizeof (int) < sizeof (time_t)
586 ? INT_STRLEN_BOUND (time_t)
587 : INT_STRLEN_BOUND (int))];
594 #if DO_MULTIBYTE && !defined COMPILE_WIDE
600 case L_('\b'): case L_('\t'): case L_('\n'):
601 case L_('\v'): case L_('\f'): case L_('\r'):
602 case L_(' '): case L_('!'): case L_('"'): case L_('#'): case L_('&'):
603 case L_('\''): case L_('('): case L_(')'): case L_('*'): case L_('+'):
604 case L_(','): case L_('-'): case L_('.'): case L_('/'): case L_('0'):
605 case L_('1'): case L_('2'): case L_('3'): case L_('4'): case L_('5'):
606 case L_('6'): case L_('7'): case L_('8'): case L_('9'): case L_(':'):
607 case L_(';'): case L_('<'): case L_('='): case L_('>'): case L_('?'):
608 case L_('A'): case L_('B'): case L_('C'): case L_('D'): case L_('E'):
609 case L_('F'): case L_('G'): case L_('H'): case L_('I'): case L_('J'):
610 case L_('K'): case L_('L'): case L_('M'): case L_('N'): case L_('O'):
611 case L_('P'): case L_('Q'): case L_('R'): case L_('S'): case L_('T'):
612 case L_('U'): case L_('V'): case L_('W'): case L_('X'): case L_('Y'):
613 case L_('Z'): case L_('['): case L_('\\'): case L_(']'): case L_('^'):
614 case L_('_'): case L_('a'): case L_('b'): case L_('c'): case L_('d'):
615 case L_('e'): case L_('f'): case L_('g'): case L_('h'): case L_('i'):
616 case L_('j'): case L_('k'): case L_('l'): case L_('m'): case L_('n'):
617 case L_('o'): case L_('p'): case L_('q'): case L_('r'): case L_('s'):
618 case L_('t'): case L_('u'): case L_('v'): case L_('w'): case L_('x'):
619 case L_('y'): case L_('z'): case L_('{'): case L_('|'): case L_('}'):
621 /* The C Standard requires these 98 characters (plus '%') to
622 be in the basic execution character set. None of these
623 characters can start a multibyte sequence, so they need
624 not be analyzed further. */
629 /* Copy this multibyte sequence until we reach its end, find
630 an error, or come back to the initial shift state. */
632 mbstate_t mbstate
= mbstate_zero
;
637 format_end
= f
+ strlen (f
) + 1;
638 fsize
= format_end
- f
;
642 size_t bytes
= mbrlen (f
+ len
, fsize
- len
, &mbstate
);
647 if (bytes
== (size_t) -2)
649 len
+= strlen (f
+ len
);
653 if (bytes
== (size_t) -1)
661 while (! mbsinit (&mbstate
));
669 #else /* ! DO_MULTIBYTE */
671 /* Either multibyte encodings are not supported, they are
672 safe for formats, so any non-'%' byte can be copied through,
673 or this is the wide character version. */
680 #endif /* ! DO_MULTIBYTE */
682 /* Check for flags that can modify a format. */
687 /* This influences the number formats. */
694 /* This changes textual output. */
708 /* As a GNU extension we allow to specify the field width. */
715 width
+= *f
- L_('0');
718 while (ISDIGIT (*f
));
721 /* Check for modifiers. */
734 /* Now do the specified format. */
738 #define DO_NUMBER(d, v) \
739 digits = width == -1 ? d : width; \
740 number_value = v; goto do_number
741 #define DO_NUMBER_SPACEPAD(d, v) \
742 digits = width == -1 ? d : width; \
743 number_value = v; goto do_number_spacepad
759 #if defined _NL_CURRENT || !HAVE_STRFTIME
760 cpy (aw_len
, a_wkday
);
763 goto underlying_strftime
;
774 #if defined _NL_CURRENT || !HAVE_STRFTIME
775 cpy (STRLEN (f_wkday
), f_wkday
);
778 goto underlying_strftime
;
790 #if defined _NL_CURRENT || !HAVE_STRFTIME
791 cpy (am_len
, a_month
);
794 goto underlying_strftime
;
805 #if defined _NL_CURRENT || !HAVE_STRFTIME
806 cpy (STRLEN (f_month
), f_month
);
809 goto underlying_strftime
;
813 if (modifier
== L_('O'))
816 if (! (modifier
== 'E'
818 (const CHAR_T
*) _NL_CURRENT (LC_TIME
,
821 subfmt
= (const CHAR_T
*) _NL_CURRENT (LC_TIME
, NLW(D_T_FMT
));
824 goto underlying_strftime
;
826 subfmt
= L_("%a %b %e %H:%M:%S %Y");
832 CHAR_T
*old_start
= p
;
833 size_t len
= my_strftime (NULL
, (size_t) -1, subfmt
,
834 tp ut_argument LOCALE_ARG
);
835 add (len
, my_strftime (p
, maxsize
- i
, subfmt
,
836 tp ut_argument LOCALE_ARG
));
839 while (old_start
< p
)
841 *old_start
= TOUPPER ((UCHAR_T
) *old_start
);
847 #if HAVE_STRFTIME && ! (defined _NL_CURRENT && HAVE_STRUCT_ERA_ENTRY)
850 /* The relevant information is available only via the
851 underlying strftime implementation, so use that. */
854 char ubuf
[1024]; /* enough for any single format in practice */
856 /* Make sure we're calling the actual underlying strftime.
857 In some cases, config.h contains something like
858 "#define strftime rpl_strftime". */
869 len
= strftime (ubuf
, sizeof ubuf
, ufmt
, tp
);
870 if (len
== 0 && ubuf
[0] != '\0')
878 if (modifier
== L_('O'))
880 if (modifier
== L_('E'))
882 #if HAVE_STRUCT_ERA_ENTRY
883 struct era_entry
*era
= _nl_get_era_entry (tp
);
887 size_t len
= __wcslen (era
->era_wname
);
888 cpy (len
, era
->era_wname
);
890 size_t len
= strlen (era
->era_name
);
891 cpy (len
, era
->era_name
);
897 goto underlying_strftime
;
903 int year
= tp
->tm_year
+ TM_YEAR_BASE
;
904 DO_NUMBER (1, year
/ 100 - (year
% 100 < 0));
908 if (modifier
== L_('O'))
911 if (! (modifier
== L_('E')
913 (const CHAR_T
*)_NL_CURRENT (LC_TIME
, NLW(ERA_D_FMT
)))
915 subfmt
= (const CHAR_T
*) _NL_CURRENT (LC_TIME
, NLW(D_FMT
));
919 goto underlying_strftime
;
927 subfmt
= L_("%m/%d/%y");
931 if (modifier
== L_('E'))
934 DO_NUMBER (2, tp
->tm_mday
);
937 if (modifier
== L_('E'))
940 DO_NUMBER_SPACEPAD (2, tp
->tm_mday
);
942 /* All numeric formats set DIGITS and NUMBER_VALUE and then
943 jump to one of these two labels. */
946 /* Force `_' flag unless overwritten by `0' flag. */
951 /* Format the number according to the MODIFIER flag. */
953 if (modifier
== L_('O') && 0 <= number_value
)
956 /* Get the locale specific alternate representation of
957 the number NUMBER_VALUE. If none exist NULL is returned. */
959 const wchar_t *cp
= _nl_get_walt_digit (number_value
);
961 const char *cp
= _nl_get_alt_digit (number_value
);
966 size_t digitlen
= STRLEN (cp
);
975 goto underlying_strftime
;
980 unsigned int u
= number_value
;
982 bufp
= buf
+ sizeof (buf
) / sizeof (buf
[0]);
983 negative_number
= number_value
< 0;
989 *--bufp
= u
% 10 + L_('0');
990 while ((u
/= 10) != 0);
993 do_number_sign_and_padding
:
999 int padding
= digits
- (buf
+ (sizeof (buf
) / sizeof (buf
[0]))
1004 while (0 < padding
--)
1009 bufp
+= negative_number
;
1010 while (0 < padding
--)
1012 if (negative_number
)
1017 cpy (buf
+ sizeof (buf
) / sizeof (buf
[0]) - bufp
, bufp
);
1023 subfmt
= L_("%Y-%m-%d");
1027 if (modifier
== L_('E'))
1030 DO_NUMBER (2, tp
->tm_hour
);
1033 if (modifier
== L_('E'))
1036 DO_NUMBER (2, hour12
);
1038 case L_('k'): /* GNU extension. */
1039 if (modifier
== L_('E'))
1042 DO_NUMBER_SPACEPAD (2, tp
->tm_hour
);
1044 case L_('l'): /* GNU extension. */
1045 if (modifier
== L_('E'))
1048 DO_NUMBER_SPACEPAD (2, hour12
);
1051 if (modifier
== L_('E'))
1054 DO_NUMBER (3, 1 + tp
->tm_yday
);
1057 if (modifier
== L_('E'))
1060 DO_NUMBER (2, tp
->tm_min
);
1063 if (modifier
== L_('E'))
1066 DO_NUMBER (2, tp
->tm_mon
+ 1);
1069 add (1, *p
= L_('\n'));
1074 #if !defined _NL_CURRENT && HAVE_STRFTIME
1075 format_char
= L_('p');
1085 #if defined _NL_CURRENT || !HAVE_STRFTIME
1089 goto underlying_strftime
;
1093 subfmt
= L_("%H:%M");
1098 if (*(subfmt
= (const CHAR_T
*) _NL_CURRENT (LC_TIME
,
1102 subfmt
= L_("%I:%M:%S %p");
1106 if (modifier
== L_('E'))
1109 DO_NUMBER (2, tp
->tm_sec
);
1111 case L_('s'): /* GNU extension. */
1119 /* Generate string value for T using time_t arithmetic;
1120 this works even if sizeof (long) < sizeof (time_t). */
1122 bufp
= buf
+ sizeof (buf
) / sizeof (buf
[0]);
1123 negative_number
= t
< 0;
1130 if (negative_number
)
1134 /* Adjust if division truncates to minus infinity. */
1135 if (0 < -1 % 10 && d
< 0)
1142 *--bufp
= d
+ L_('0');
1147 goto do_number_sign_and_padding
;
1151 if (modifier
== L_('O'))
1154 if (! (modifier
== L_('E')
1156 (const CHAR_T
*) _NL_CURRENT (LC_TIME
, NLW(ERA_T_FMT
)))
1158 subfmt
= (const CHAR_T
*) _NL_CURRENT (LC_TIME
, NLW(T_FMT
));
1162 goto underlying_strftime
;
1168 subfmt
= L_("%H:%M:%S");
1172 add (1, *p
= L_('\t'));
1176 DO_NUMBER (1, (tp
->tm_wday
- 1 + 7) % 7 + 1);
1179 if (modifier
== L_('E'))
1182 DO_NUMBER (2, (tp
->tm_yday
- tp
->tm_wday
+ 7) / 7);
1187 if (modifier
== L_('E'))
1190 int year
= tp
->tm_year
+ TM_YEAR_BASE
;
1191 int days
= iso_week_days (tp
->tm_yday
, tp
->tm_wday
);
1195 /* This ISO week belongs to the previous year. */
1197 days
= iso_week_days (tp
->tm_yday
+ (365 + __isleap (year
)),
1202 int d
= iso_week_days (tp
->tm_yday
- (365 + __isleap (year
)),
1206 /* This ISO week belongs to the next year. */
1215 DO_NUMBER (2, (year
% 100 + 100) % 100);
1218 DO_NUMBER (1, year
);
1221 DO_NUMBER (2, days
/ 7 + 1);
1226 if (modifier
== L_('E'))
1229 DO_NUMBER (2, (tp
->tm_yday
- (tp
->tm_wday
- 1 + 7) % 7 + 7) / 7);
1232 if (modifier
== L_('E'))
1235 DO_NUMBER (1, tp
->tm_wday
);
1238 if (modifier
== 'E')
1240 #if HAVE_STRUCT_ERA_ENTRY
1241 struct era_entry
*era
= _nl_get_era_entry (tp
);
1244 # ifdef COMPILE_WIDE
1245 subfmt
= era
->era_wformat
;
1247 subfmt
= era
->era_format
;
1253 goto underlying_strftime
;
1257 if (modifier
== L_('O'))
1260 DO_NUMBER (1, tp
->tm_year
+ TM_YEAR_BASE
);
1263 if (modifier
== L_('E'))
1265 #if HAVE_STRUCT_ERA_ENTRY
1266 struct era_entry
*era
= _nl_get_era_entry (tp
);
1269 int delta
= tp
->tm_year
- era
->start_date
[0];
1270 DO_NUMBER (1, (era
->offset
1271 + delta
* era
->absolute_direction
));
1275 goto underlying_strftime
;
1279 DO_NUMBER (2, (tp
->tm_year
% 100 + 100) % 100);
1289 /* The tzset() call might have changed the value. */
1290 if (!(zone
&& *zone
) && tp
->tm_isdst
>= 0)
1291 zone
= tzname
[tp
->tm_isdst
];
1298 /* The zone string is always given in multibyte form. We have
1299 to transform it first. */
1302 widen (zone
, wczone
, len
);
1306 cpy (strlen (zone
), zone
);
1311 if (tp
->tm_isdst
< 0)
1317 diff
= tp
->tm_gmtoff
;
1330 if (lt
== (time_t) -1)
1332 /* mktime returns -1 for errors, but -1 is also a
1333 valid time_t value. Check whether an error really
1337 if (! my_strftime_localtime_r (<
, &tm
)
1338 || ((ltm
.tm_sec
^ tm
.tm_sec
)
1339 | (ltm
.tm_min
^ tm
.tm_min
)
1340 | (ltm
.tm_hour
^ tm
.tm_hour
)
1341 | (ltm
.tm_mday
^ tm
.tm_mday
)
1342 | (ltm
.tm_mon
^ tm
.tm_mon
)
1343 | (ltm
.tm_year
^ tm
.tm_year
)))
1347 if (! my_strftime_gmtime_r (<
, >m
))
1350 diff
= tm_diff (<m
, >m
);
1356 add (1, *p
= L_('-'));
1360 add (1, *p
= L_('+'));
1363 DO_NUMBER (4, (diff
/ 60) * 100 + diff
% 60);
1366 case L_('\0'): /* GNU extension: % at end of format. */
1370 /* Unknown format; output the format, including the '%',
1371 since this is most likely the right thing to do if a
1372 multibyte string has been misparsed. */
1376 for (flen
= 1; f
[1 - flen
] != L_('%'); flen
++)
1378 cpy (flen
, &f
[1 - flen
]);
1384 if (p
&& maxsize
!= 0)
1388 #if defined _LIBC && !defined USE_IN_EXTENDED_LOCALE_MODEL
1389 libc_hidden_def (my_strftime
)
1394 /* For Emacs we have a separate interface which corresponds to the normal
1395 strftime function and does not have the extra information whether the
1396 TP arguments comes from a `gmtime' call or not. */
1398 emacs_strftime (s
, maxsize
, format
, tp
)
1402 const struct tm
*tp
;
1404 return my_strftime (s
, maxsize
, format
, tp
, 0);