1 /* Copyright (C) 1991-2023 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3
4 This file is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation, either version 3 of the
7 License, or (at your option) any later version.
8
9 This file 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
12 GNU Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
16
17 #ifdef _LIBC
18 # define USE_IN_EXTENDED_LOCALE_MODEL 1
19 # define HAVE_STRUCT_ERA_ENTRY 1
20 # define HAVE_TM_GMTOFF 1
21 # define HAVE_STRUCT_TM_TM_ZONE 1
22 # define HAVE_TZNAME 1
23 # include "../locale/localeinfo.h"
24 #else
25 # include <libc-config.h>
26 # if FPRINTFTIME
27 # include "fprintftime.h"
28 # else
29 # include "strftime.h"
30 # endif
31 # include "time-internal.h"
32 #endif
33
34 #include <ctype.h>
35 #include <errno.h>
36 #include <time.h>
37
38 #if HAVE_TZNAME && !HAVE_DECL_TZNAME
39 extern char *tzname[];
40 #endif
41
42 /* Do multibyte processing if multibyte encodings are supported, unless
43 multibyte sequences are safe in formats. Multibyte sequences are
44 safe if they cannot contain byte sequences that look like format
45 conversion specifications. The multibyte encodings used by the
46 C library on the various platforms (UTF-8, GB2312, GBK, CP936,
47 GB18030, EUC-TW, BIG5, BIG5-HKSCS, CP950, EUC-JP, EUC-KR, CP949,
48 SHIFT_JIS, CP932, JOHAB) are safe for formats, because the byte '%'
49 cannot occur in a multibyte character except in the first byte.
50
51 The DEC-HANYU encoding used on OSF/1 is not safe for formats, but
52 this encoding has never been seen in real-life use, so we ignore
53 it. */
54 #if !(defined __osf__ && 0)
55 # define MULTIBYTE_IS_FORMAT_SAFE 1
56 #endif
57 #define DO_MULTIBYTE (! MULTIBYTE_IS_FORMAT_SAFE)
58
59 #if DO_MULTIBYTE
60 # include <wchar.h>
61 static const mbstate_t mbstate_zero;
62 #endif
63
64 #include <limits.h>
65 #include <stdckdint.h>
66 #include <stddef.h>
67 #include <stdlib.h>
68 #include <string.h>
69
70 #include "attribute.h"
71 #include <intprops.h>
72
73 #ifdef COMPILE_WIDE
74 # include <endian.h>
75 # define CHAR_T wchar_t
76 # define UCHAR_T unsigned int
77 # define L_(Str) L##Str
78 # define NLW(Sym) _NL_W##Sym
79
80 # define MEMCPY(d, s, n) __wmemcpy (d, s, n)
81 # define STRLEN(s) __wcslen (s)
82
83 #else
84 # define CHAR_T char
85 # define UCHAR_T unsigned char
86 # define L_(Str) Str
87 # define NLW(Sym) Sym
88 # define ABALTMON_1 _NL_ABALTMON_1
89
90 # define MEMCPY(d, s, n) memcpy (d, s, n)
91 # define STRLEN(s) strlen (s)
92
93 #endif
94
95 /* Shift A right by B bits portably, by dividing A by 2**B and
96 truncating towards minus infinity. A and B should be free of side
97 effects, and B should be in the range 0 <= B <= INT_BITS - 2, where
98 INT_BITS is the number of useful bits in an int. GNU code can
99 assume that INT_BITS is at least 32.
100
101 ISO C99 says that A >> B is implementation-defined if A < 0. Some
102 implementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shift
103 right in the usual way when A < 0, so SHR falls back on division if
104 ordinary A >> B doesn't seem to be the usual signed shift. */
105 #define SHR(a, b) \
106 (-1 >> 1 == -1 \
107 ? (a) >> (b) \
108 : ((a) + ((a) < 0)) / (1 << (b)) - ((a) < 0))
109
110 #define TM_YEAR_BASE 1900
111
112 #ifndef __isleap
113 /* Nonzero if YEAR is a leap year (every 4 years,
114 except every 100th isn't, and every 400th is). */
115 # define __isleap(year) \
116 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
117 #endif
118
119
120 #ifdef _LIBC
121 # define mktime_z(tz, tm) mktime (tm)
122 # define tzname __tzname
123 # define tzset __tzset
124 #endif
125
126 #ifndef FPRINTFTIME
127 # define FPRINTFTIME 0
128 #endif
129
130 #if FPRINTFTIME
131 # define STREAM_OR_CHAR_T FILE
132 # define STRFTIME_ARG(x) /* empty */
133 #else
134 # define STREAM_OR_CHAR_T CHAR_T
135 # define STRFTIME_ARG(x) x,
136 #endif
137
138 #if FPRINTFTIME
139 # define memset_byte(P, Len, Byte) \
140 do { size_t _i; for (_i = 0; _i < Len; _i++) fputc (Byte, P); } while (0)
141 # define memset_space(P, Len) memset_byte (P, Len, ' ')
142 # define memset_zero(P, Len) memset_byte (P, Len, '0')
143 #elif defined COMPILE_WIDE
144 # define memset_space(P, Len) (wmemset (P, L' ', Len), (P) += (Len))
145 # define memset_zero(P, Len) (wmemset (P, L'0', Len), (P) += (Len))
146 #else
147 # define memset_space(P, Len) (memset (P, ' ', Len), (P) += (Len))
148 # define memset_zero(P, Len) (memset (P, '0', Len), (P) += (Len))
149 #endif
150
151 #if FPRINTFTIME
152 # define advance(P, N)
153 #else
154 # define advance(P, N) ((P) += (N))
155 #endif
156
157 #define add(n, f) width_add (width, n, f)
158 #define width_add(width, n, f) \
159 do \
160 { \
161 size_t _n = (n); \
162 size_t _w = pad == L_('-') || width < 0 ? 0 : width; \
163 size_t _incr = _n < _w ? _w : _n; \
164 if (_incr >= maxsize - i) \
165 { \
166 errno = ERANGE; \
167 return 0; \
168 } \
169 if (p) \
170 { \
171 if (_n < _w) \
172 { \
173 size_t _delta = _w - _n; \
174 if (pad == L_('0') || pad == L_('+')) \
175 memset_zero (p, _delta); \
176 else \
177 memset_space (p, _delta); \
178 } \
179 f; \
180 advance (p, _n); \
181 } \
182 i += _incr; \
183 } while (0)
184
185 #define add1(c) width_add1 (width, c)
186 #if FPRINTFTIME
187 # define width_add1(width, c) width_add (width, 1, fputc (c, p))
188 #else
189 # define width_add1(width, c) width_add (width, 1, *p = c)
190 #endif
191
192 #define cpy(n, s) width_cpy (width, n, s)
193 #if FPRINTFTIME
194 # define width_cpy(width, n, s) \
195 width_add (width, n, \
196 do \
197 { \
198 if (to_lowcase) \
199 fwrite_lowcase (p, (s), _n); \
200 else if (to_uppcase) \
201 fwrite_uppcase (p, (s), _n); \
202 else \
203 { \
204 /* Ignore the value of fwrite. The caller can determine whether \
205 an error occurred by inspecting ferror (P). All known fwrite \
206 implementations set the stream's error indicator when they \
207 fail due to ENOMEM etc., even though C11 and POSIX.1-2008 do \
208 not require this. */ \
209 fwrite (s, _n, 1, p); \
210 } \
211 } \
212 while (0) \
213 )
214 #else
215 # define width_cpy(width, n, s) \
216 width_add (width, n, \
217 if (to_lowcase) \
218 memcpy_lowcase (p, (s), _n LOCALE_ARG); \
219 else if (to_uppcase) \
220 memcpy_uppcase (p, (s), _n LOCALE_ARG); \
221 else \
222 MEMCPY ((void *) p, (void const *) (s), _n))
223 #endif
224
225 #ifdef COMPILE_WIDE
226 # ifndef USE_IN_EXTENDED_LOCALE_MODEL
227 # undef __mbsrtowcs_l
228 # define __mbsrtowcs_l(d, s, l, st, loc) __mbsrtowcs (d, s, l, st)
229 # endif
230 #endif
231
232
233 #if defined _LIBC && defined USE_IN_EXTENDED_LOCALE_MODEL
234 /* We use this code also for the extended locale handling where the
235 function gets as an additional argument the locale which has to be
236 used. To access the values we have to redefine the _NL_CURRENT
237 macro. */
238 # define strftime __strftime_l
239 # define wcsftime __wcsftime_l
240 # undef _NL_CURRENT
241 # define _NL_CURRENT(category, item) \
242 (current->values[_NL_ITEM_INDEX (item)].string)
243 # define LOCALE_PARAM , locale_t loc
244 # define LOCALE_ARG , loc
245 # define HELPER_LOCALE_ARG , current
246 #else
247 # define LOCALE_PARAM
248 # define LOCALE_ARG
249 # ifdef _LIBC
250 # define HELPER_LOCALE_ARG , _NL_CURRENT_DATA (LC_TIME)
251 # else
252 # define HELPER_LOCALE_ARG
253 # endif
254 #endif
255
256 #ifdef COMPILE_WIDE
257 # ifdef USE_IN_EXTENDED_LOCALE_MODEL
258 # define TOUPPER(Ch, L) __towupper_l (Ch, L)
259 # define TOLOWER(Ch, L) __towlower_l (Ch, L)
260 # else
261 # define TOUPPER(Ch, L) towupper (Ch)
262 # define TOLOWER(Ch, L) towlower (Ch)
263 # endif
264 #else
265 # ifdef USE_IN_EXTENDED_LOCALE_MODEL
266 # define TOUPPER(Ch, L) __toupper_l (Ch, L)
267 # define TOLOWER(Ch, L) __tolower_l (Ch, L)
268 # else
269 # define TOUPPER(Ch, L) toupper (Ch)
270 # define TOLOWER(Ch, L) tolower (Ch)
271 # endif
272 #endif
273 /* We don't use 'isdigit' here since the locale dependent
274 interpretation is not what we want here. We only need to accept
275 the arabic digits in the ASCII range. One day there is perhaps a
276 more reliable way to accept other sets of digits. */
277 #define ISDIGIT(Ch) ((unsigned int) (Ch) - L_('0') <= 9)
278
279 /* Avoid false GCC warning "'memset' specified size 18446744073709551615 exceeds
280 maximum object size 9223372036854775807", caused by insufficient data flow
281 analysis and value propagation of the 'width_add' expansion when GCC is not
282 optimizing. Cf. <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88443>. */
283 #if __GNUC__ >= 7 && !__OPTIMIZE__
284 # pragma GCC diagnostic ignored "-Wstringop-overflow"
285 #endif
286
287 #if FPRINTFTIME
288 static void
289 fwrite_lowcase (FILE *fp, const CHAR_T *src, size_t len)
290 {
291 while (len-- > 0)
292 {
293 fputc (TOLOWER ((UCHAR_T) *src, loc), fp);
294 ++src;
295 }
296 }
297
298 static void
299 fwrite_uppcase (FILE *fp, const CHAR_T *src, size_t len)
300 {
301 while (len-- > 0)
302 {
303 fputc (TOUPPER ((UCHAR_T) *src, loc), fp);
304 ++src;
305 }
306 }
307 #else
308 static CHAR_T *memcpy_lowcase (CHAR_T *dest, const CHAR_T *src,
309 size_t len LOCALE_PARAM);
310
311 static CHAR_T *
312 memcpy_lowcase (CHAR_T *dest, const CHAR_T *src, size_t len LOCALE_PARAM)
313 {
314 while (len-- > 0)
315 dest[len] = TOLOWER ((UCHAR_T) src[len], loc);
316 return dest;
317 }
318
319 static CHAR_T *memcpy_uppcase (CHAR_T *dest, const CHAR_T *src,
320 size_t len LOCALE_PARAM);
321
322 static CHAR_T *
323 memcpy_uppcase (CHAR_T *dest, const CHAR_T *src, size_t len LOCALE_PARAM)
324 {
325 while (len-- > 0)
326 dest[len] = TOUPPER ((UCHAR_T) src[len], loc);
327 return dest;
328 }
329 #endif
330
331
332 #if ! HAVE_TM_GMTOFF
333 /* Yield the difference between *A and *B,
334 measured in seconds, ignoring leap seconds. */
335 # define tm_diff ftime_tm_diff
336 static int tm_diff (const struct tm *, const struct tm *);
337 static int
338 tm_diff (const struct tm *a, const struct tm *b)
339 {
340 /* Compute intervening leap days correctly even if year is negative.
341 Take care to avoid int overflow in leap day calculations,
342 but it's OK to assume that A and B are close to each other. */
343 int a4 = SHR (a->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (a->tm_year & 3);
344 int b4 = SHR (b->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (b->tm_year & 3);
345 int a100 = (a4 + (a4 < 0)) / 25 - (a4 < 0);
346 int b100 = (b4 + (b4 < 0)) / 25 - (b4 < 0);
347 int a400 = SHR (a100, 2);
348 int b400 = SHR (b100, 2);
349 int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
350 int years = a->tm_year - b->tm_year;
351 int days = (365 * years + intervening_leap_days
352 + (a->tm_yday - b->tm_yday));
353 return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
354 + (a->tm_min - b->tm_min))
355 + (a->tm_sec - b->tm_sec));
356 }
357 #endif /* ! HAVE_TM_GMTOFF */
358
359
360
361 /* The number of days from the first day of the first ISO week of this
362 year to the year day YDAY with week day WDAY. ISO weeks start on
363 Monday; the first ISO week has the year's first Thursday. YDAY may
364 be as small as YDAY_MINIMUM. */
365 #define ISO_WEEK_START_WDAY 1 /* Monday */
366 #define ISO_WEEK1_WDAY 4 /* Thursday */
367 #define YDAY_MINIMUM (-366)
368 static int iso_week_days (int, int);
369 static __inline int
370 iso_week_days (int yday, int wday)
371 {
372 /* Add enough to the first operand of % to make it nonnegative. */
373 int big_enough_multiple_of_7 = (-YDAY_MINIMUM / 7 + 2) * 7;
374 return (yday
375 - (yday - wday + ISO_WEEK1_WDAY + big_enough_multiple_of_7) % 7
376 + ISO_WEEK1_WDAY - ISO_WEEK_START_WDAY);
377 }
378
379
380 /* When compiling this file, GNU applications can #define my_strftime
381 to a symbol (typically nstrftime) to get an extended strftime with
382 extra arguments TZ and NS. */
383
384 #if FPRINTFTIME
385 # undef my_strftime
386 # define my_strftime fprintftime
387 #endif
388
389 #ifdef my_strftime
390 # define extra_args , tz, ns
391 # define extra_args_spec , timezone_t tz, int ns
392 #else
393 # if defined COMPILE_WIDE
394 # define my_strftime wcsftime
395 # define nl_get_alt_digit _nl_get_walt_digit
396 # else
397 # define my_strftime strftime
398 # define nl_get_alt_digit _nl_get_alt_digit
399 # endif
400 # define extra_args
401 # define extra_args_spec
402 /* We don't have this information in general. */
403 # define tz 1
404 # define ns 0
405 #endif
406
407 static size_t __strftime_internal (STREAM_OR_CHAR_T *, STRFTIME_ARG (size_t)
408 const CHAR_T *, const struct tm *,
409 bool, int, int, bool *
410 extra_args_spec LOCALE_PARAM);
411
412 /* Write information from TP into S according to the format
413 string FORMAT, writing no more that MAXSIZE characters
414 (including the terminating '\0') and returning number of
415 characters written. If S is NULL, nothing will be written
416 anywhere, so to determine how many characters would be
417 written, use NULL for S and (size_t) -1 for MAXSIZE. */
418 size_t
419 my_strftime (STREAM_OR_CHAR_T *s, STRFTIME_ARG (size_t maxsize)
420 const CHAR_T *format,
421 const struct tm *tp extra_args_spec LOCALE_PARAM)
422 {
423 bool tzset_called = false;
424 return __strftime_internal (s, STRFTIME_ARG (maxsize) format, tp, false,
425 0, -1, &tzset_called extra_args LOCALE_ARG);
426 }
427 libc_hidden_def (my_strftime)
428
429 /* Just like my_strftime, above, but with more parameters.
430 UPCASE indicates that the result should be converted to upper case.
431 YR_SPEC and WIDTH specify the padding and width for the year.
432 *TZSET_CALLED indicates whether tzset has been called here. */
433 static size_t
434 __strftime_internal (STREAM_OR_CHAR_T *s, STRFTIME_ARG (size_t maxsize)
435 const CHAR_T *format,
436 const struct tm *tp, bool upcase,
437 int yr_spec, int width, bool *tzset_called
438 extra_args_spec LOCALE_PARAM)
439 {
440 #if defined _LIBC && defined USE_IN_EXTENDED_LOCALE_MODEL
441 struct __locale_data *const current = loc->__locales[LC_TIME];
442 #endif
443 #if FPRINTFTIME
444 size_t maxsize = (size_t) -1;
445 #endif
446
447 int saved_errno = errno;
448 int hour12 = tp->tm_hour;
449 #ifdef _NL_CURRENT
450 /* We cannot make the following values variables since we must delay
451 the evaluation of these values until really needed since some
452 expressions might not be valid in every situation. The 'struct tm'
453 might be generated by a strptime() call that initialized
454 only a few elements. Dereference the pointers only if the format
455 requires this. Then it is ok to fail if the pointers are invalid. */
456 # define a_wkday \
457 ((const CHAR_T *) (tp->tm_wday < 0 || tp->tm_wday > 6 \
458 ? "?" : _NL_CURRENT (LC_TIME, NLW(ABDAY_1) + tp->tm_wday)))
459 # define f_wkday \
460 ((const CHAR_T *) (tp->tm_wday < 0 || tp->tm_wday > 6 \
461 ? "?" : _NL_CURRENT (LC_TIME, NLW(DAY_1) + tp->tm_wday)))
462 # define a_month \
463 ((const CHAR_T *) (tp->tm_mon < 0 || tp->tm_mon > 11 \
464 ? "?" : _NL_CURRENT (LC_TIME, NLW(ABMON_1) + tp->tm_mon)))
465 # define f_month \
466 ((const CHAR_T *) (tp->tm_mon < 0 || tp->tm_mon > 11 \
467 ? "?" : _NL_CURRENT (LC_TIME, NLW(MON_1) + tp->tm_mon)))
468 # define a_altmonth \
469 ((const CHAR_T *) (tp->tm_mon < 0 || tp->tm_mon > 11 \
470 ? "?" : _NL_CURRENT (LC_TIME, NLW(ABALTMON_1) + tp->tm_mon)))
471 # define f_altmonth \
472 ((const CHAR_T *) (tp->tm_mon < 0 || tp->tm_mon > 11 \
473 ? "?" : _NL_CURRENT (LC_TIME, NLW(ALTMON_1) + tp->tm_mon)))
474 # define ampm \
475 ((const CHAR_T *) _NL_CURRENT (LC_TIME, tp->tm_hour > 11 \
476 ? NLW(PM_STR) : NLW(AM_STR)))
477
478 # define aw_len STRLEN (a_wkday)
479 # define am_len STRLEN (a_month)
480 # define aam_len STRLEN (a_altmonth)
481 # define ap_len STRLEN (ampm)
482 #endif
483 #if HAVE_TZNAME
484 char **tzname_vec = tzname;
485 #endif
486 const char *zone;
487 size_t i = 0;
488 STREAM_OR_CHAR_T *p = s;
489 const CHAR_T *f;
490 #if DO_MULTIBYTE && !defined COMPILE_WIDE
491 const char *format_end = NULL;
492 #endif
493
494 zone = NULL;
495 #if HAVE_STRUCT_TM_TM_ZONE
496 /* The POSIX test suite assumes that setting
497 the environment variable TZ to a new value before calling strftime()
498 will influence the result (the %Z format) even if the information in
499 TP is computed with a totally different time zone.
500 This is bogus: though POSIX allows bad behavior like this,
501 POSIX does not require it. Do the right thing instead. */
502 zone = (const char *) tp->tm_zone;
503 #endif
504 #if HAVE_TZNAME
505 if (!tz)
506 {
507 if (! (zone && *zone))
508 zone = "GMT";
509 }
510 else
511 {
512 # if !HAVE_STRUCT_TM_TM_ZONE
513 /* Infer the zone name from *TZ instead of from TZNAME. */
514 tzname_vec = tz->tzname_copy;
515 # endif
516 }
517 /* The tzset() call might have changed the value. */
518 if (!(zone && *zone) && tp->tm_isdst >= 0)
519 {
520 /* POSIX.1 requires that local time zone information be used as
521 though strftime called tzset. */
522 # ifndef my_strftime
523 if (!*tzset_called)
524 {
525 tzset ();
526 *tzset_called = true;
527 }
528 # endif
529 zone = tzname_vec[tp->tm_isdst != 0];
530 }
531 #endif
532 if (! zone)
533 zone = "";
534
535 if (hour12 > 12)
536 hour12 -= 12;
537 else
538 if (hour12 == 0)
539 hour12 = 12;
540
541 for (f = format; *f != '\0'; width = -1, f++)
542 {
543 int pad = 0; /* Padding for number ('_', '-', '+', '0', or 0). */
544 int modifier; /* Field modifier ('E', 'O', or 0). */
545 int digits = 0; /* Max digits for numeric format. */
546 int number_value; /* Numeric value to be printed. */
547 unsigned int u_number_value; /* (unsigned int) number_value. */
548 bool negative_number; /* The number is negative. */
549 bool always_output_a_sign; /* +/- should always be output. */
550 int tz_colon_mask; /* Bitmask of where ':' should appear. */
551 const CHAR_T *subfmt;
552 CHAR_T *bufp;
553 CHAR_T buf[1
554 + 2 /* for the two colons in a %::z or %:::z time zone */
555 + (sizeof (int) < sizeof (time_t)
556 ? INT_STRLEN_BOUND (time_t)
557 : INT_STRLEN_BOUND (int))];
558 bool to_lowcase = false;
559 bool to_uppcase = upcase;
560 size_t colons;
561 bool change_case = false;
562 int format_char;
563 int subwidth;
564
565 #if DO_MULTIBYTE && !defined COMPILE_WIDE
566 switch (*f)
567 {
568 case L_('%'):
569 break;
570
571 case L_('\b'): case L_('\t'): case L_('\n'):
572 case L_('\v'): case L_('\f'): case L_('\r'):
573 case L_(' '): case L_('!'): case L_('"'): case L_('#'): case L_('&'):
574 case L_('\''): case L_('('): case L_(')'): case L_('*'): case L_('+'):
575 case L_(','): case L_('-'): case L_('.'): case L_('/'): case L_('0'):
576 case L_('1'): case L_('2'): case L_('3'): case L_('4'): case L_('5'):
577 case L_('6'): case L_('7'): case L_('8'): case L_('9'): case L_(':'):
578 case L_(';'): case L_('<'): case L_('='): case L_('>'): case L_('?'):
579 case L_('A'): case L_('B'): case L_('C'): case L_('D'): case L_('E'):
580 case L_('F'): case L_('G'): case L_('H'): case L_('I'): case L_('J'):
581 case L_('K'): case L_('L'): case L_('M'): case L_('N'): case L_('O'):
582 case L_('P'): case L_('Q'): case L_('R'): case L_('S'): case L_('T'):
583 case L_('U'): case L_('V'): case L_('W'): case L_('X'): case L_('Y'):
584 case L_('Z'): case L_('['): case L_('\\'): case L_(']'): case L_('^'):
585 case L_('_'): case L_('a'): case L_('b'): case L_('c'): case L_('d'):
586 case L_('e'): case L_('f'): case L_('g'): case L_('h'): case L_('i'):
587 case L_('j'): case L_('k'): case L_('l'): case L_('m'): case L_('n'):
588 case L_('o'): case L_('p'): case L_('q'): case L_('r'): case L_('s'):
589 case L_('t'): case L_('u'): case L_('v'): case L_('w'): case L_('x'):
590 case L_('y'): case L_('z'): case L_('{'): case L_('|'): case L_('}'):
591 case L_('~'):
592 /* The C Standard requires these 98 characters (plus '%') to
593 be in the basic execution character set. None of these
594 characters can start a multibyte sequence, so they need
595 not be analyzed further. */
596 add1 (*f);
597 continue;
598
599 default:
600 /* Copy this multibyte sequence until we reach its end, find
601 an error, or come back to the initial shift state. */
602 {
603 mbstate_t mbstate = mbstate_zero;
604 size_t len = 0;
605 size_t fsize;
606
607 if (! format_end)
608 format_end = f + strlen (f) + 1;
609 fsize = format_end - f;
610
611 do
612 {
613 size_t bytes = mbrlen (f + len, fsize - len, &mbstate);
614
615 if (bytes == 0)
616 break;
617
618 if (bytes == (size_t) -2)
619 {
620 len += strlen (f + len);
621 break;
622 }
623
624 if (bytes == (size_t) -1)
625 {
626 len++;
627 break;
628 }
629
630 len += bytes;
631 }
632 while (! mbsinit (&mbstate));
633
634 cpy (len, f);
635 f += len - 1;
636 continue;
637 }
638 }
639
640 #else /* ! DO_MULTIBYTE */
641
642 /* Either multibyte encodings are not supported, they are
643 safe for formats, so any non-'%' byte can be copied through,
644 or this is the wide character version. */
645 if (*f != L_('%'))
646 {
647 add1 (*f);
648 continue;
649 }
650
651 #endif /* ! DO_MULTIBYTE */
652
653 char const *percent = f;
654
655 /* Check for flags that can modify a format. */
656 while (1)
657 {
658 switch (*++f)
659 {
660 /* This influences the number formats. */
661 case L_('_'):
662 case L_('-'):
663 case L_('+'):
664 case L_('0'):
665 pad = *f;
666 continue;
667
668 /* This changes textual output. */
669 case L_('^'):
670 to_uppcase = true;
671 continue;
672 case L_('#'):
673 change_case = true;
674 continue;
675
676 default:
677 break;
678 }
679 break;
680 }
681
682 if (ISDIGIT (*f))
683 {
684 width = 0;
685 do
686 {
687 if (ckd_mul (&width, width, 10)
688 || ckd_add (&width, width, *f - L_('0')))
689 width = INT_MAX;
690 ++f;
691 }
692 while (ISDIGIT (*f));
693 }
694
695 /* Check for modifiers. */
696 switch (*f)
697 {
698 case L_('E'):
699 case L_('O'):
700 modifier = *f++;
701 break;
702
703 default:
704 modifier = 0;
705 break;
706 }
707
708 /* Now do the specified format. */
709 format_char = *f;
710 switch (format_char)
711 {
712 #define DO_NUMBER(d, v) \
713 do \
714 { \
715 digits = d; \
716 number_value = v; \
717 goto do_number; \
718 } \
719 while (0)
720 #define DO_SIGNED_NUMBER(d, negative, v) \
721 DO_MAYBE_SIGNED_NUMBER (d, negative, v, do_signed_number)
722 #define DO_YEARISH(d, negative, v) \
723 DO_MAYBE_SIGNED_NUMBER (d, negative, v, do_yearish)
724 #define DO_MAYBE_SIGNED_NUMBER(d, negative, v, label) \
725 do \
726 { \
727 digits = d; \
728 negative_number = negative; \
729 u_number_value = v; \
730 goto label; \
731 } \
732 while (0)
733
734 /* The mask is not what you might think.
735 When the ordinal i'th bit is set, insert a colon
736 before the i'th digit of the time zone representation. */
737 #define DO_TZ_OFFSET(d, mask, v) \
738 do \
739 { \
740 digits = d; \
741 tz_colon_mask = mask; \
742 u_number_value = v; \
743 goto do_tz_offset; \
744 } \
745 while (0)
746 #define DO_NUMBER_SPACEPAD(d, v) \
747 do \
748 { \
749 digits = d; \
750 number_value = v; \
751 goto do_number_spacepad; \
752 } \
753 while (0)
754
755 case L_('%'):
756 if (f - 1 != percent)
757 goto bad_percent;
758 add1 (*f);
759 break;
760
761 case L_('a'):
762 if (modifier != 0)
763 goto bad_format;
764 if (change_case)
765 {
766 to_uppcase = true;
767 to_lowcase = false;
768 }
769 #ifdef _NL_CURRENT
770 cpy (aw_len, a_wkday);
771 break;
772 #else
773 goto underlying_strftime;
774 #endif
775
776 case 'A':
777 if (modifier != 0)
778 goto bad_format;
779 if (change_case)
780 {
781 to_uppcase = true;
782 to_lowcase = false;
783 }
784 #ifdef _NL_CURRENT
785 cpy (STRLEN (f_wkday), f_wkday);
786 break;
787 #else
788 goto underlying_strftime;
789 #endif
790
791 case L_('b'):
792 case L_('h'):
793 if (change_case)
794 {
795 to_uppcase = true;
796 to_lowcase = false;
797 }
798 if (modifier == L_('E'))
799 goto bad_format;
800 #ifdef _NL_CURRENT
801 if (modifier == L_('O'))
802 cpy (aam_len, a_altmonth);
803 else
804 cpy (am_len, a_month);
805 break;
806 #else
807 goto underlying_strftime;
808 #endif
809
810 case L_('B'):
811 if (modifier == L_('E'))
812 goto bad_format;
813 if (change_case)
814 {
815 to_uppcase = true;
816 to_lowcase = false;
817 }
818 #ifdef _NL_CURRENT
819 if (modifier == L_('O'))
820 cpy (STRLEN (f_altmonth), f_altmonth);
821 else
822 cpy (STRLEN (f_month), f_month);
823 break;
824 #else
825 goto underlying_strftime;
826 #endif
827
828 case L_('c'):
829 if (modifier == L_('O'))
830 goto bad_format;
831 #ifdef _NL_CURRENT
832 if (! (modifier == L_('E')
833 && (*(subfmt =
834 (const CHAR_T *) _NL_CURRENT (LC_TIME,
835 NLW(ERA_D_T_FMT)))
836 != '\0')))
837 subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(D_T_FMT));
838 #else
839 goto underlying_strftime;
840 #endif
841
842 subformat:
843 subwidth = -1;
844 subformat_width:
845 {
846 size_t len = __strftime_internal (NULL, STRFTIME_ARG ((size_t) -1)
847 subfmt, tp, to_uppcase,
848 pad, subwidth, tzset_called
849 extra_args LOCALE_ARG);
850 add (len, __strftime_internal (p,
851 STRFTIME_ARG (maxsize - i)
852 subfmt, tp, to_uppcase,
853 pad, subwidth, tzset_called
854 extra_args LOCALE_ARG));
855 }
856 break;
857
858 #if !(defined _NL_CURRENT && HAVE_STRUCT_ERA_ENTRY)
859 underlying_strftime:
860 {
861 /* The relevant information is available only via the
862 underlying strftime implementation, so use that. */
863 char ufmt[5];
864 char *u = ufmt;
865 char ubuf[1024]; /* enough for any single format in practice */
866 size_t len;
867 /* Make sure we're calling the actual underlying strftime.
868 In some cases, config.h contains something like
869 "#define strftime rpl_strftime". */
870 # ifdef strftime
871 # undef strftime
872 size_t strftime ();
873 # endif
874
875 /* The space helps distinguish strftime failure from empty
876 output. */
877 *u++ = ' ';
878 *u++ = '%';
879 if (modifier != 0)
880 *u++ = modifier;
881 *u++ = format_char;
882 *u = '\0';
883 len = strftime (ubuf, sizeof ubuf, ufmt, tp);
884 if (len != 0)
885 cpy (len - 1, ubuf + 1);
886 }
887 break;
888 #endif
889
890 case L_('C'):
891 if (modifier == L_('E'))
892 {
893 #if HAVE_STRUCT_ERA_ENTRY
894 struct era_entry *era = _nl_get_era_entry (tp HELPER_LOCALE_ARG);
895 if (era)
896 {
897 # ifdef COMPILE_WIDE
898 size_t len = __wcslen (era->era_wname);
899 cpy (len, era->era_wname);
900 # else
901 size_t len = strlen (era->era_name);
902 cpy (len, era->era_name);
903 # endif
904 break;
905 }
906 #else
907 goto underlying_strftime;
908 #endif
909 }
910
911 {
912 bool negative_year = tp->tm_year < - TM_YEAR_BASE;
913 bool zero_thru_1899 = !negative_year & (tp->tm_year < 0);
914 int century = ((tp->tm_year - 99 * zero_thru_1899) / 100
915 + TM_YEAR_BASE / 100);
916 DO_YEARISH (2, negative_year, century);
917 }
918
919 case L_('x'):
920 if (modifier == L_('O'))
921 goto bad_format;
922 #ifdef _NL_CURRENT
923 if (! (modifier == L_('E')
924 && (*(subfmt =
925 (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(ERA_D_FMT)))
926 != L_('\0'))))
927 subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(D_FMT));
928 goto subformat;
929 #else
930 goto underlying_strftime;
931 #endif
932 case L_('D'):
933 if (modifier != 0)
934 goto bad_format;
935 subfmt = L_("%m/%d/%y");
936 goto subformat;
937
938 case L_('d'):
939 if (modifier == L_('E'))
940 goto bad_format;
941
942 DO_NUMBER (2, tp->tm_mday);
943
944 case L_('e'):
945 if (modifier == L_('E'))
946 goto bad_format;
947
948 DO_NUMBER_SPACEPAD (2, tp->tm_mday);
949
950 /* All numeric formats set DIGITS and NUMBER_VALUE (or U_NUMBER_VALUE)
951 and then jump to one of these labels. */
952
953 do_tz_offset:
954 always_output_a_sign = true;
955 goto do_number_body;
956
957 do_yearish:
958 if (pad == 0)
959 pad = yr_spec;
960 always_output_a_sign
961 = (pad == L_('+')
962 && ((digits == 2 ? 99 : 9999) < u_number_value
963 || digits < width));
964 goto do_maybe_signed_number;
965
966 do_number_spacepad:
967 if (pad == 0)
968 pad = L_('_');
969
970 do_number:
971 /* Format NUMBER_VALUE according to the MODIFIER flag. */
972 negative_number = number_value < 0;
973 u_number_value = number_value;
974
975 do_signed_number:
976 always_output_a_sign = false;
977
978 do_maybe_signed_number:
979 tz_colon_mask = 0;
980
981 do_number_body:
982 /* Format U_NUMBER_VALUE according to the MODIFIER flag.
983 NEGATIVE_NUMBER is nonzero if the original number was
984 negative; in this case it was converted directly to
985 unsigned int (i.e., modulo (UINT_MAX + 1)) without
986 negating it. */
987 if (modifier == L_('O') && !negative_number)
988 {
989 #ifdef _NL_CURRENT
990 /* Get the locale specific alternate representation of
991 the number. If none exist NULL is returned. */
992 const CHAR_T *cp = nl_get_alt_digit (u_number_value
993 HELPER_LOCALE_ARG);
994
995 if (cp != NULL)
996 {
997 size_t digitlen = STRLEN (cp);
998 if (digitlen != 0)
999 {
1000 cpy (digitlen, cp);
1001 break;
1002 }
1003 }
1004 #else
1005 goto underlying_strftime;
1006 #endif
1007 }
1008
1009 bufp = buf + sizeof (buf) / sizeof (buf[0]);
1010
1011 if (negative_number)
1012 u_number_value = - u_number_value;
1013
1014 do
1015 {
1016 if (tz_colon_mask & 1)
1017 *--bufp = ':';
1018 tz_colon_mask >>= 1;
1019 *--bufp = u_number_value % 10 + L_('0');
1020 u_number_value /= 10;
1021 }
1022 while (u_number_value != 0 || tz_colon_mask != 0);
1023
1024 do_number_sign_and_padding:
1025 if (pad == 0)
1026 pad = L_('0');
1027 if (width < 0)
1028 width = digits;
1029
1030 {
1031 CHAR_T sign_char = (negative_number ? L_('-')
1032 : always_output_a_sign ? L_('+')
1033 : 0);
1034 int numlen = buf + sizeof buf / sizeof buf[0] - bufp;
1035 int shortage = width - !!sign_char - numlen;
1036 int padding = pad == L_('-') || shortage <= 0 ? 0 : shortage;
1037
1038 if (sign_char)
1039 {
1040 if (pad == L_('_'))
1041 {
1042 if (p)
1043 memset_space (p, padding);
1044 i += padding;
1045 width -= padding;
1046 }
1047 width_add1 (0, sign_char);
1048 width--;
1049 }
1050
1051 cpy (numlen, bufp);
1052 }
1053 break;
1054
1055 case L_('F'):
1056 if (modifier != 0)
1057 goto bad_format;
1058 if (pad == 0 && width < 0)
1059 {
1060 pad = L_('+');
1061 subwidth = 4;
1062 }
1063 else
1064 {
1065 subwidth = width - 6;
1066 if (subwidth < 0)
1067 subwidth = 0;
1068 }
1069 subfmt = L_("%Y-%m-%d");
1070 goto subformat_width;
1071
1072 case L_('H'):
1073 if (modifier == L_('E'))
1074 goto bad_format;
1075
1076 DO_NUMBER (2, tp->tm_hour);
1077
1078 case L_('I'):
1079 if (modifier == L_('E'))
1080 goto bad_format;
1081
1082 DO_NUMBER (2, hour12);
1083
1084 case L_('k'): /* GNU extension. */
1085 if (modifier == L_('E'))
1086 goto bad_format;
1087
1088 DO_NUMBER_SPACEPAD (2, tp->tm_hour);
1089
1090 case L_('l'): /* GNU extension. */
1091 if (modifier == L_('E'))
1092 goto bad_format;
1093
1094 DO_NUMBER_SPACEPAD (2, hour12);
1095
1096 case L_('j'):
1097 if (modifier == L_('E'))
1098 goto bad_format;
1099
1100 DO_SIGNED_NUMBER (3, tp->tm_yday < -1, tp->tm_yday + 1U);
1101
1102 case L_('M'):
1103 if (modifier == L_('E'))
1104 goto bad_format;
1105
1106 DO_NUMBER (2, tp->tm_min);
1107
1108 case L_('m'):
1109 if (modifier == L_('E'))
1110 goto bad_format;
1111
1112 DO_SIGNED_NUMBER (2, tp->tm_mon < -1, tp->tm_mon + 1U);
1113
1114 #ifndef _LIBC
1115 case L_('N'): /* GNU extension. */
1116 if (modifier == L_('E'))
1117 goto bad_format;
1118 {
1119 int n = ns, ns_digits = 9;
1120 if (width <= 0)
1121 width = ns_digits;
1122 int ndigs = ns_digits;
1123 while (width < ndigs || (1 < ndigs && n % 10 == 0))
1124 ndigs--, n /= 10;
1125 for (int j = ndigs; 0 < j; j--)
1126 buf[j - 1] = n % 10 + L_('0'), n /= 10;
1127 if (!pad)
1128 pad = L_('0');
1129 width_cpy (0, ndigs, buf);
1130 width_add (width - ndigs, 0, (void) 0);
1131 }
1132 break;
1133 #endif
1134
1135 case L_('n'):
1136 add1 (L_('\n'));
1137 break;
1138
1139 case L_('P'):
1140 to_lowcase = true;
1141 #ifndef _NL_CURRENT
1142 format_char = L_('p');
1143 #endif
1144 FALLTHROUGH;
1145 case L_('p'):
1146 if (change_case)
1147 {
1148 to_uppcase = false;
1149 to_lowcase = true;
1150 }
1151 #ifdef _NL_CURRENT
1152 cpy (ap_len, ampm);
1153 break;
1154 #else
1155 goto underlying_strftime;
1156 #endif
1157
1158 case L_('q'): /* GNU extension. */
1159 DO_SIGNED_NUMBER (1, false, ((tp->tm_mon * 11) >> 5) + 1);
1160
1161 case L_('R'):
1162 subfmt = L_("%H:%M");
1163 goto subformat;
1164
1165 case L_('r'):
1166 #ifdef _NL_CURRENT
1167 if (*(subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME,
1168 NLW(T_FMT_AMPM)))
1169 == L_('\0'))
1170 subfmt = L_("%I:%M:%S %p");
1171 goto subformat;
1172 #else
1173 goto underlying_strftime;
1174 #endif
1175
1176 case L_('S'):
1177 if (modifier == L_('E'))
1178 goto bad_format;
1179
1180 DO_NUMBER (2, tp->tm_sec);
1181
1182 case L_('s'): /* GNU extension. */
1183 {
1184 struct tm ltm;
1185 time_t t;
1186
1187 ltm = *tp;
1188 ltm.tm_yday = -1;
1189 t = mktime_z (tz, <m);
1190 if (ltm.tm_yday < 0)
1191 {
1192 errno = EOVERFLOW;
1193 return 0;
1194 }
1195
1196 /* Generate string value for T using time_t arithmetic;
1197 this works even if sizeof (long) < sizeof (time_t). */
1198
1199 bufp = buf + sizeof (buf) / sizeof (buf[0]);
1200 negative_number = t < 0;
1201
1202 do
1203 {
1204 int d = t % 10;
1205 t /= 10;
1206 *--bufp = (negative_number ? -d : d) + L_('0');
1207 }
1208 while (t != 0);
1209
1210 digits = 1;
1211 always_output_a_sign = false;
1212 goto do_number_sign_and_padding;
1213 }
1214
1215 case L_('X'):
1216 if (modifier == L_('O'))
1217 goto bad_format;
1218 #ifdef _NL_CURRENT
1219 if (! (modifier == L_('E')
1220 && (*(subfmt =
1221 (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(ERA_T_FMT)))
1222 != L_('\0'))))
1223 subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(T_FMT));
1224 goto subformat;
1225 #else
1226 goto underlying_strftime;
1227 #endif
1228 case L_('T'):
1229 subfmt = L_("%H:%M:%S");
1230 goto subformat;
1231
1232 case L_('t'):
1233 add1 (L_('\t'));
1234 break;
1235
1236 case L_('u'):
1237 DO_NUMBER (1, (tp->tm_wday - 1 + 7) % 7 + 1);
1238
1239 case L_('U'):
1240 if (modifier == L_('E'))
1241 goto bad_format;
1242
1243 DO_NUMBER (2, (tp->tm_yday - tp->tm_wday + 7) / 7);
1244
1245 case L_('V'):
1246 case L_('g'):
1247 case L_('G'):
1248 if (modifier == L_('E'))
1249 goto bad_format;
1250 {
1251 /* YEAR is a leap year if and only if (tp->tm_year + TM_YEAR_BASE)
1252 is a leap year, except that YEAR and YEAR - 1 both work
1253 correctly even when (tp->tm_year + TM_YEAR_BASE) would
1254 overflow. */
1255 int year = (tp->tm_year
1256 + (tp->tm_year < 0
1257 ? TM_YEAR_BASE % 400
1258 : TM_YEAR_BASE % 400 - 400));
1259 int year_adjust = 0;
1260 int days = iso_week_days (tp->tm_yday, tp->tm_wday);
1261
1262 if (days < 0)
1263 {
1264 /* This ISO week belongs to the previous year. */
1265 year_adjust = -1;
1266 days = iso_week_days (tp->tm_yday + (365 + __isleap (year - 1)),
1267 tp->tm_wday);
1268 }
1269 else
1270 {
1271 int d = iso_week_days (tp->tm_yday - (365 + __isleap (year)),
1272 tp->tm_wday);
1273 if (0 <= d)
1274 {
1275 /* This ISO week belongs to the next year. */
1276 year_adjust = 1;
1277 days = d;
1278 }
1279 }
1280
1281 switch (*f)
1282 {
1283 case L_('g'):
1284 {
1285 int yy = (tp->tm_year % 100 + year_adjust) % 100;
1286 DO_YEARISH (2, false,
1287 (0 <= yy
1288 ? yy
1289 : tp->tm_year < -TM_YEAR_BASE - year_adjust
1290 ? -yy
1291 : yy + 100));
1292 }
1293
1294 case L_('G'):
1295 DO_YEARISH (4, tp->tm_year < -TM_YEAR_BASE - year_adjust,
1296 (tp->tm_year + (unsigned int) TM_YEAR_BASE
1297 + year_adjust));
1298
1299 default:
1300 DO_NUMBER (2, days / 7 + 1);
1301 }
1302 }
1303
1304 case L_('W'):
1305 if (modifier == L_('E'))
1306 goto bad_format;
1307
1308 DO_NUMBER (2, (tp->tm_yday - (tp->tm_wday - 1 + 7) % 7 + 7) / 7);
1309
1310 case L_('w'):
1311 if (modifier == L_('E'))
1312 goto bad_format;
1313
1314 DO_NUMBER (1, tp->tm_wday);
1315
1316 case L_('Y'):
1317 if (modifier == L_('E'))
1318 {
1319 #if HAVE_STRUCT_ERA_ENTRY
1320 struct era_entry *era = _nl_get_era_entry (tp HELPER_LOCALE_ARG);
1321 if (era)
1322 {
1323 # ifdef COMPILE_WIDE
1324 subfmt = era->era_wformat;
1325 # else
1326 subfmt = era->era_format;
1327 # endif
1328 if (pad == 0)
1329 pad = yr_spec;
1330 goto subformat;
1331 }
1332 #else
1333 goto underlying_strftime;
1334 #endif
1335 }
1336 if (modifier == L_('O'))
1337 goto bad_format;
1338
1339 DO_YEARISH (4, tp->tm_year < -TM_YEAR_BASE,
1340 tp->tm_year + (unsigned int) TM_YEAR_BASE);
1341
1342 case L_('y'):
1343 if (modifier == L_('E'))
1344 {
1345 #if HAVE_STRUCT_ERA_ENTRY
1346 struct era_entry *era = _nl_get_era_entry (tp HELPER_LOCALE_ARG);
1347 if (era)
1348 {
1349 int delta = tp->tm_year - era->start_date[0];
1350 if (pad == 0)
1351 pad = yr_spec;
1352 DO_NUMBER (2, (era->offset
1353 + delta * era->absolute_direction));
1354 }
1355 #else
1356 goto underlying_strftime;
1357 #endif
1358 }
1359
1360 {
1361 int yy = tp->tm_year % 100;
1362 if (yy < 0)
1363 yy = tp->tm_year < - TM_YEAR_BASE ? -yy : yy + 100;
1364 DO_YEARISH (2, false, yy);
1365 }
1366
1367 case L_('Z'):
1368 if (change_case)
1369 {
1370 to_uppcase = false;
1371 to_lowcase = true;
1372 }
1373
1374 #ifdef COMPILE_WIDE
1375 {
1376 /* The zone string is always given in multibyte form. We have
1377 to convert it to wide character. */
1378 size_t w = pad == L_('-') || width < 0 ? 0 : width;
1379 char const *z = zone;
1380 mbstate_t st = {0};
1381 size_t len = __mbsrtowcs_l (p, &z, maxsize - i, &st, loc);
1382 if (len == (size_t) -1)
1383 return 0;
1384 size_t incr = len < w ? w : len;
1385 if (incr >= maxsize - i)
1386 {
1387 errno = ERANGE;
1388 return 0;
1389 }
1390 if (p)
1391 {
1392 if (len < w)
1393 {
1394 size_t delta = w - len;
1395 __wmemmove (p + delta, p, len);
1396 wchar_t wc = pad == L_('0') || pad == L_('+') ? L'0' : L' ';
1397 wmemset (p, wc, delta);
1398 }
1399 p += incr;
1400 }
1401 i += incr;
1402 }
1403 #else
1404 cpy (strlen (zone), zone);
1405 #endif
1406 break;
1407
1408 case L_(':'):
1409 /* :, ::, and ::: are valid only just before 'z'.
1410 :::: etc. are rejected later. */
1411 for (colons = 1; f[colons] == L_(':'); colons++)
1412 continue;
1413 if (f[colons] != L_('z'))
1414 goto bad_format;
1415 f += colons;
1416 goto do_z_conversion;
1417
1418 case L_('z'):
1419 colons = 0;
1420
1421 do_z_conversion:
1422 if (tp->tm_isdst < 0)
1423 break;
1424
1425 {
1426 int diff;
1427 int hour_diff;
1428 int min_diff;
1429 int sec_diff;
1430 #if HAVE_TM_GMTOFF
1431 diff = tp->tm_gmtoff;
1432 #else
1433 if (!tz)
1434 diff = 0;
1435 else
1436 {
1437 struct tm gtm;
1438 struct tm ltm;
1439 time_t lt;
1440
1441 /* POSIX.1 requires that local time zone information be used as
1442 though strftime called tzset. */
1443 # ifndef my_strftime
1444 if (!*tzset_called)
1445 {
1446 tzset ();
1447 *tzset_called = true;
1448 }
1449 # endif
1450
1451 ltm = *tp;
1452 ltm.tm_wday = -1;
1453 lt = mktime_z (tz, <m);
1454 if (ltm.tm_wday < 0 || ! localtime_rz (0, <, >m))
1455 break;
1456 diff = tm_diff (<m, >m);
1457 }
1458 #endif
1459
1460 negative_number = diff < 0 || (diff == 0 && *zone == '-');
1461 hour_diff = diff / 60 / 60;
1462 min_diff = diff / 60 % 60;
1463 sec_diff = diff % 60;
1464
1465 switch (colons)
1466 {
1467 case 0: /* +hhmm */
1468 DO_TZ_OFFSET (5, 0, hour_diff * 100 + min_diff);
1469
1470 case 1: tz_hh_mm: /* +hh:mm */
1471 DO_TZ_OFFSET (6, 04, hour_diff * 100 + min_diff);
1472
1473 case 2: tz_hh_mm_ss: /* +hh:mm:ss */
1474 DO_TZ_OFFSET (9, 024,
1475 hour_diff * 10000 + min_diff * 100 + sec_diff);
1476
1477 case 3: /* +hh if possible, else +hh:mm, else +hh:mm:ss */
1478 if (sec_diff != 0)
1479 goto tz_hh_mm_ss;
1480 if (min_diff != 0)
1481 goto tz_hh_mm;
1482 DO_TZ_OFFSET (3, 0, hour_diff);
1483
1484 default:
1485 goto bad_format;
1486 }
1487 }
1488
1489 case L_('\0'): /* GNU extension: % at end of format. */
1490 bad_percent:
1491 --f;
1492 FALLTHROUGH;
1493 default:
1494 /* Unknown format; output the format, including the '%',
1495 since this is most likely the right thing to do if a
1496 multibyte string has been misparsed. */
1497 bad_format:
1498 cpy (f - percent + 1, percent);
1499 break;
1500 }
1501 }
1502
1503 #if ! FPRINTFTIME
1504 if (p && maxsize != 0)
1505 *p = L_('\0');
1506 #endif
1507
1508 errno = saved_errno;
1509 return i;
1510 }