update from main archive 961217
[glibc.git] / time / strftime.c
blobfea545cd38327af25f127250a28126a1f37a6f32
1 /* Copyright (C) 1991, 92, 93, 94, 95, 96 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public License as
6 published by the Free Software Foundation; either version 2 of the
7 License, or (at your option) any later version.
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
14 You should have received a copy of the GNU Library General Public
15 License along with the GNU C Library; see the file COPYING.LIB. If not,
16 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 Boston, MA 02111-1307, USA. */
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
23 #ifdef _LIBC
24 # define HAVE_LIMITS_H 1
25 # define HAVE_MBLEN 1
26 # define HAVE_MBRLEN 1
27 # define HAVE_STRUCT_ERA_ENTRY 1
28 # define HAVE_TM_GMTOFF 1
29 # define HAVE_TM_ZONE 1
30 # define HAVE_TZNAME 1
31 # define HAVE_TZSET 1
32 # define MULTIBYTE_IS_FORMAT_SAFE 1
33 # define STDC_HEADERS 1
34 # include <ansidecl.h>
35 # include "../locale/localeinfo.h"
36 #endif
38 #include <ctype.h>
39 #include <sys/types.h> /* Some systems define `time_t' here. */
41 #ifdef TIME_WITH_SYS_TIME
42 # include <sys/time.h>
43 # include <time.h>
44 #else
45 # ifdef HAVE_SYS_TIME_H
46 # include <sys/time.h>
47 # else
48 # include <time.h>
49 # endif
50 #endif
51 #if HAVE_TZNAME
52 extern char *tzname[];
53 #endif
55 /* Do multibyte processing if multibytes are supported, unless
56 multibyte sequences are safe in formats. Multibyte sequences are
57 safe if they cannot contain byte sequences that look like format
58 conversion specifications. The GNU C Library uses UTF8 multibyte
59 encoding, which is safe for formats, but strftime.c can be used
60 with other C libraries that use unsafe encodings. */
61 #define DO_MULTIBYTE (HAVE_MBLEN && ! MULTIBYTE_IS_FORMAT_SAFE)
63 #if DO_MULTIBYTE
64 # if HAVE_MBRLEN
65 # include <wchar.h>
66 # else
67 /* Simulate mbrlen with mblen as best we can. */
68 # define mbstate_t int
69 # define mbrlen(s, n, ps) mblen (s, n)
70 # define mbsinit(ps) (*(ps) == 0)
71 # endif
72 static const mbstate_t mbstate_zero;
73 #endif
75 #if HAVE_LIMITS_H
76 # include <limits.h>
77 #endif
79 #if STDC_HEADERS
80 # include <stddef.h>
81 # include <stdlib.h>
82 # include <string.h>
83 #else
84 # define memcpy(d, s, n) bcopy ((s), (d), (n))
85 #endif
87 #ifndef __P
88 #if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
89 #define __P(args) args
90 #else
91 #define __P(args) ()
92 #endif /* GCC. */
93 #endif /* Not __P. */
95 #ifndef PTR
96 #ifdef __STDC__
97 #define PTR void *
98 #else
99 #define PTR char *
100 #endif
101 #endif
103 #ifndef CHAR_BIT
104 #define CHAR_BIT 8
105 #endif
107 #define TYPE_SIGNED(t) ((t) -1 < 0)
109 /* Bound on length of the string representing an integer value of type t.
110 Subtract one for the sign bit if t is signed;
111 302 / 1000 is log10 (2) rounded up;
112 add one for integer division truncation;
113 add one more for a minus sign if t is signed. */
114 #define INT_STRLEN_BOUND(t) \
115 ((sizeof (t) * CHAR_BIT - TYPE_SIGNED (t)) * 302 / 100 + 1 + TYPE_SIGNED (t))
117 #define TM_YEAR_BASE 1900
119 #ifndef __isleap
120 /* Nonzero if YEAR is a leap year (every 4 years,
121 except every 100th isn't, and every 400th is). */
122 #define __isleap(year) \
123 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
124 #endif
127 #ifdef _LIBC
128 # define gmtime_r __gmtime_r
129 # define localtime_r __localtime_r
130 extern int __tz_compute __P ((time_t timer, const struct tm *tm));
131 # define tzname __tzname
132 # define tzset __tzset
133 #else
134 # if ! HAVE_LOCALTIME_R
135 # if ! HAVE_TM_GMTOFF
136 /* Approximate gmtime_r as best we can in its absence. */
137 #define gmtime_r my_gmtime_r
138 static struct tm *gmtime_r __P ((const time_t *, struct tm *));
139 static struct tm *
140 gmtime_r (t, tp)
141 const time_t *t;
142 struct tm *tp;
144 struct tm *l = gmtime (t);
145 if (! l)
146 return 0;
147 *tp = *l;
148 return tp;
150 # endif /* ! HAVE_TM_GMTOFF */
152 /* Approximate localtime_r as best we can in its absence. */
153 #define localtime_r my_localtime_r
154 static struct tm *localtime_r __P ((const time_t *, struct tm *));
155 static struct tm *
156 localtime_r (t, tp)
157 const time_t *t;
158 struct tm *tp;
160 struct tm *l = localtime (t);
161 if (! l)
162 return 0;
163 *tp = *l;
164 return tp;
166 # endif /* ! HAVE_LOCALTIME_R */
167 #endif /* ! defined (_LIBC) */
170 #if !defined (memset) && !defined (HAVE_MEMSET) && !defined (_LIBC)
171 /* Some systems lack the `memset' function and we don't want to
172 introduce additional dependencies. */
173 static const char spaces[16] = " ";
175 # define memset_space(P, Len) \
176 do { \
177 int _len = (Len); \
179 do \
181 int _this = _len > 16 ? 16 : _len; \
182 memcpy ((P), spaces, _this); \
183 (P) += _this; \
184 _len -= _this; \
186 while (_len > 0); \
187 } while (0)
188 #else
189 # define memset_space(P, Len) memset ((P), ' ', (Len))
190 #endif
192 #define add(n, f) \
193 do \
195 int _n = (n); \
196 int _delta = width - _n; \
197 i += _n + (_delta > 0 ? _delta : 0); \
198 if (i >= maxsize) \
199 return 0; \
200 else \
201 if (p) \
203 if (_delta > 0) \
204 memset_space (p, _delta); \
205 f; \
206 p += _n; \
208 } while (0)
210 #define cpy(n, s) \
211 add ((n), \
212 if (to_lowcase) \
213 memcpy_lowcase (p, (s), _n); \
214 else \
215 memcpy ((PTR) p, (PTR) (s), _n))
219 #ifdef _LIBC
220 # define TOUPPER(Ch) toupper (Ch)
221 # define TOLOWER(Ch) tolower (Ch)
222 #else
223 # define TOUPPER(Ch) (islower (Ch) ? toupper (Ch) : (Ch))
224 # define TOLOWER(Ch) (isupper (Ch) ? tolower (Ch) : (Ch))
225 #endif
227 static char *memcpy_lowcase __P ((char *dest, const char *src, size_t len));
229 static char *
230 memcpy_lowcase (dest, src, len)
231 char *dest;
232 const char *src;
233 size_t len;
235 while (len-- > 0)
236 dest[len] = TOLOWER (src[len]);
237 return dest;
240 #if ! HAVE_TM_GMTOFF
241 /* Yield the difference between *A and *B,
242 measured in seconds, ignoring leap seconds. */
243 static int tm_diff __P ((const struct tm *, const struct tm *));
244 static int
245 tm_diff (a, b)
246 const struct tm *a;
247 const struct tm *b;
249 /* Compute intervening leap days correctly even if year is negative.
250 Take care to avoid int overflow in leap day calculations,
251 but it's OK to assume that A and B are close to each other. */
252 int a4 = (a->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (a->tm_year & 3);
253 int b4 = (b->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (b->tm_year & 3);
254 int a100 = a4 / 25 - (a4 % 25 < 0);
255 int b100 = b4 / 25 - (b4 % 25 < 0);
256 int a400 = a100 >> 2;
257 int b400 = b100 >> 2;
258 int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
259 int years = a->tm_year - b->tm_year;
260 int days = (365 * years + intervening_leap_days
261 + (a->tm_yday - b->tm_yday));
262 return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
263 + (a->tm_min - b->tm_min))
264 + (a->tm_sec - b->tm_sec));
266 #endif /* ! HAVE_TM_GMTOFF */
270 /* The number of days from the first day of the first ISO week of this
271 year to the year day YDAY with week day WDAY. ISO weeks start on
272 Monday; the first ISO week has the year's first Thursday. YDAY may
273 be as small as YDAY_MINIMUM. */
274 #define ISO_WEEK_START_WDAY 1 /* Monday */
275 #define ISO_WEEK1_WDAY 4 /* Thursday */
276 #define YDAY_MINIMUM (-366)
277 static int iso_week_days __P ((int, int));
278 #ifdef __GNUC__
279 inline
280 #endif
281 static int
282 iso_week_days (yday, wday)
283 int yday;
284 int wday;
286 /* Add enough to the first operand of % to make it nonnegative. */
287 int big_enough_multiple_of_7 = (-YDAY_MINIMUM / 7 + 2) * 7;
288 return (yday
289 - (yday - wday + ISO_WEEK1_WDAY + big_enough_multiple_of_7) % 7
290 + ISO_WEEK1_WDAY - ISO_WEEK_START_WDAY);
294 #ifndef _NL_CURRENT
295 static char const weekday_name[][10] =
297 "Sunday", "Monday", "Tuesday", "Wednesday",
298 "Thursday", "Friday", "Saturday"
300 static char const month_name[][10] =
302 "January", "February", "March", "April", "May", "June",
303 "July", "August", "September", "October", "November", "December"
305 #endif
307 /* Write information from TP into S according to the format
308 string FORMAT, writing no more that MAXSIZE characters
309 (including the terminating '\0') and returning number of
310 characters written. If S is NULL, nothing will be written
311 anywhere, so to determine how many characters would be
312 written, use NULL for S and (size_t) UINT_MAX for MAXSIZE. */
313 size_t
314 strftime (s, maxsize, format, tp)
315 char *s;
316 size_t maxsize;
317 const char *format;
318 const struct tm *tp;
320 int hour12 = tp->tm_hour;
321 #ifdef _NL_CURRENT
322 const char *const a_wkday = _NL_CURRENT (LC_TIME, ABDAY_1 + tp->tm_wday);
323 const char *const f_wkday = _NL_CURRENT (LC_TIME, DAY_1 + tp->tm_wday);
324 const char *const a_month = _NL_CURRENT (LC_TIME, ABMON_1 + tp->tm_mon);
325 const char *const f_month = _NL_CURRENT (LC_TIME, MON_1 + tp->tm_mon);
326 const char *const ampm = _NL_CURRENT (LC_TIME,
327 hour12 > 11 ? PM_STR : AM_STR);
328 size_t aw_len = strlen (a_wkday);
329 size_t am_len = strlen (a_month);
330 size_t ap_len = strlen (ampm);
331 #else
332 const char *const f_wkday = weekday_name[tp->tm_wday];
333 const char *const f_month = month_name[tp->tm_mon];
334 const char *const a_wkday = f_wkday;
335 const char *const a_month = f_month;
336 const char *const ampm = "AMPM" + 2 * (hour12 > 11);
337 size_t aw_len = 3;
338 size_t am_len = 3;
339 size_t ap_len = 2;
340 #endif
341 size_t wkday_len = strlen (f_wkday);
342 size_t month_len = strlen (f_month);
343 const char *zone;
344 size_t zonelen;
345 size_t i = 0;
346 char *p = s;
347 const char *f;
349 zone = NULL;
350 #if !defined _LIBC && HAVE_TM_ZONE
351 /* XXX We have some problems here. First, the string pointed to by
352 tm_zone is dynamically allocated while loading the zone data. But
353 when another zone is loaded since the information in TP were
354 computed this would be a stale pointer.
355 The second problem is the POSIX test suite which assumes setting
356 the environment variable TZ to a new value before calling strftime()
357 will influence the result (the %Z format) even if the information in
358 TP is computed with a totally different time zone. --drepper@gnu */
359 zone = (const char *) tp->tm_zone;
360 #endif
361 #if HAVE_TZNAME
362 /* POSIX.1 8.1.1 requires that whenever strftime() is called, the
363 time zone names contained in the external variable `tzname' shall
364 be set as if the tzset() function had been called. */
365 # if HAVE_TZSET
366 tzset ();
367 # endif
369 if (!(zone && *zone) && tp->tm_isdst >= 0)
370 zone = tzname[tp->tm_isdst];
371 #endif
372 if (! zone)
373 zone = ""; /* POSIX.2 requires the empty string here. */
375 zonelen = strlen (zone);
377 if (hour12 > 12)
378 hour12 -= 12;
379 else
380 if (hour12 == 0) hour12 = 12;
382 for (f = format; *f != '\0'; ++f)
384 int pad; /* Padding for number ('-', '_', or 0). */
385 int modifier; /* Field modifier ('E', 'O', or 0). */
386 int digits; /* Max digits for numeric format. */
387 int number_value; /* Numeric value to be printed. */
388 int negative_number; /* 1 if the number is negative. */
389 const char *subfmt;
390 char *bufp;
391 char buf[1 + (sizeof (int) < sizeof (time_t)
392 ? INT_STRLEN_BOUND (time_t)
393 : INT_STRLEN_BOUND (int))];
394 int width = -1;
395 int to_lowcase = 0;
397 #if DO_MULTIBYTE
399 switch (*f)
401 case '%':
402 break;
404 case '\a': case '\b': case '\t': case '\n':
405 case '\v': case '\f': case '\r':
406 case ' ': case '!': case '"': case '#': case '&': case'\'':
407 case '(': case ')': case '*': case '+': case ',': case '-':
408 case '.': case '/': case '0': case '1': case '2': case '3':
409 case '4': case '5': case '6': case '7': case '8': case '9':
410 case ':': case ';': case '<': case '=': case '>': case '?':
411 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
412 case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
413 case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
414 case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
415 case 'Y': case 'Z': case '[': case'\\': case ']': case '^':
416 case '_': case 'a': case 'b': case 'c': case 'd': case 'e':
417 case 'f': case 'g': case 'h': case 'i': case 'j': case 'k':
418 case 'l': case 'm': case 'n': case 'o': case 'p': case 'q':
419 case 'r': case 's': case 't': case 'u': case 'v': case 'w':
420 case 'x': case 'y': case 'z': case '{': case '|': case '}':
421 case '~':
422 /* The C Standard requires these 98 characters (plus '%') to
423 be in the basic execution character set. None of these
424 characters can start a multibyte sequence, so they need
425 not be analyzed further. */
426 add (1, *p = *f);
427 continue;
429 default:
430 /* Copy this multibyte sequence until we reach its end, find
431 an error, or come back to the initial shift state. */
433 mbstate_t mbstate = mbstate_zero;
434 size_t len = 0;
438 size_t bytes = mbrlen (f + len, (size_t) -1, &mbstate);
440 if (bytes == 0)
441 break;
443 if (bytes == (size_t) -2 || bytes == (size_t) -1)
445 len++;
446 break;
449 len += bytes;
451 while (! mbsinit (&mbstate));
453 cpy (len, f);
454 continue;
458 #else /* ! DO_MULTIBYTE */
460 /* Either multibyte encodings are not supported, or they are
461 safe for formats, so any non-'%' byte can be copied through. */
462 if (*f != '%')
464 add (1, *p = *f);
465 continue;
468 #endif /* ! DO_MULTIBYTE */
470 /* Check for flags that can modify a number format. */
471 ++f;
472 while (1)
474 switch (*f)
476 case '_':
477 case '-':
478 case '0':
479 pad = *f++;
480 break;
482 default:
483 pad = 0;
484 break;
486 break;
489 /* As a GNU extension we allow to specify the field width. */
490 if (isdigit (*f))
492 width = 0;
495 width *= 10;
496 width += *f - '0';
498 while (isdigit (*++f));
501 /* Check for modifiers. */
502 switch (*f)
504 case 'E':
505 case 'O':
506 modifier = *f++;
507 break;
509 default:
510 modifier = 0;
511 break;
514 /* Now do the specified format. */
515 switch (*f)
517 #define DO_NUMBER(d, v) \
518 digits = d; number_value = v; goto do_number
519 #define DO_NUMBER_SPACEPAD(d, v) \
520 digits = d; number_value = v; goto do_number_spacepad
522 case '%':
523 if (modifier != 0)
524 goto bad_format;
525 add (1, *p = *f);
526 break;
528 case 'a':
529 if (modifier != 0)
530 goto bad_format;
531 cpy (aw_len, a_wkday);
532 break;
534 case 'A':
535 if (modifier != 0)
536 goto bad_format;
537 cpy (wkday_len, f_wkday);
538 break;
540 case 'b':
541 case 'h': /* POSIX.2 extension. */
542 if (modifier != 0)
543 goto bad_format;
544 cpy (am_len, a_month);
545 break;
547 case 'B':
548 if (modifier != 0)
549 goto bad_format;
550 cpy (month_len, f_month);
551 break;
553 case 'c':
554 if (modifier == 'O')
555 goto bad_format;
556 #ifdef _NL_CURRENT
557 if (! (modifier == 'E'
558 && *(subfmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT)) != '\0'))
559 subfmt = _NL_CURRENT (LC_TIME, D_T_FMT);
560 #else
561 subfmt = "%a %b %e %H:%M:%S %Y";
562 #endif
564 subformat:
566 size_t len = strftime (NULL, maxsize - i, subfmt, tp);
567 if (len == 0 && *subfmt)
568 return 0;
569 add (len, strftime (p, maxsize - i, subfmt, tp));
571 break;
573 case 'C': /* POSIX.2 extension. */
574 if (modifier == 'O')
575 goto bad_format;
576 #if HAVE_STRUCT_ERA_ENTRY
577 if (modifier == 'E')
579 struct era_entry *era = _nl_get_era_entry (tp);
580 if (era)
582 size_t len = strlen (era->name_fmt);
583 cpy (len, era->name_fmt);
584 break;
587 #endif
589 int year = tp->tm_year + TM_YEAR_BASE;
590 DO_NUMBER (1, year / 100 - (year % 100 < 0));
593 case 'x':
594 if (modifier == 'O')
595 goto bad_format;
596 #ifdef _NL_CURRENT
597 if (! (modifier == 'E'
598 && *(subfmt = _NL_CURRENT (LC_TIME, ERA_D_FMT)) != '\0'))
599 subfmt = _NL_CURRENT (LC_TIME, D_FMT);
600 goto subformat;
601 #endif
602 /* Fall through. */
603 case 'D': /* POSIX.2 extension. */
604 if (modifier != 0)
605 goto bad_format;
606 subfmt = "%m/%d/%y";
607 goto subformat;
609 case 'd':
610 if (modifier == 'E')
611 goto bad_format;
613 DO_NUMBER (2, tp->tm_mday);
615 case 'e': /* POSIX.2 extension. */
616 if (modifier == 'E')
617 goto bad_format;
619 DO_NUMBER_SPACEPAD (2, tp->tm_mday);
621 /* All numeric formats set DIGITS and NUMBER_VALUE and then
622 jump to one of these two labels. */
624 do_number_spacepad:
625 /* Force `_' flag unless overwritten by `0' flag. */
626 if (pad != '0')
627 pad = '_';
629 do_number:
630 /* Format the number according to the MODIFIER flag. */
632 #ifdef _NL_CURRENT
633 if (modifier == 'O' && 0 <= number_value)
635 /* Get the locale specific alternate representation of
636 the number NUMBER_VALUE. If none exist NULL is returned. */
637 const char *cp = _nl_get_alt_digit (number_value);
639 if (cp != NULL)
641 size_t digitlen = strlen (cp);
642 if (digitlen != 0)
644 cpy (digitlen, cp);
645 break;
649 #endif
651 unsigned int u = number_value;
653 bufp = buf + sizeof (buf);
654 negative_number = number_value < 0;
656 if (negative_number)
657 u = -u;
660 *--bufp = u % 10 + '0';
661 while ((u /= 10) != 0);
664 do_number_sign_and_padding:
665 if (negative_number)
666 *--bufp = '-';
668 if (pad != '-')
670 int padding = digits - (buf + sizeof (buf) - bufp);
672 if (pad == '_')
674 while (0 < padding--)
675 *--bufp = ' ';
677 else
679 bufp += negative_number;
680 while (0 < padding--)
681 *--bufp = '0';
682 if (negative_number)
683 *--bufp = '-';
687 cpy (buf + sizeof (buf) - bufp, bufp);
688 break;
691 case 'H':
692 if (modifier == 'E')
693 goto bad_format;
695 DO_NUMBER (2, tp->tm_hour);
697 case 'I':
698 if (modifier == 'E')
699 goto bad_format;
701 DO_NUMBER (2, hour12);
703 case 'k': /* GNU extension. */
704 if (modifier == 'E')
705 goto bad_format;
707 DO_NUMBER_SPACEPAD (2, tp->tm_hour);
709 case 'l': /* GNU extension. */
710 if (modifier == 'E')
711 goto bad_format;
713 DO_NUMBER_SPACEPAD (2, hour12);
715 case 'j':
716 if (modifier == 'E')
717 goto bad_format;
719 DO_NUMBER (3, 1 + tp->tm_yday);
721 case 'M':
722 if (modifier == 'E')
723 goto bad_format;
725 DO_NUMBER (2, tp->tm_min);
727 case 'm':
728 if (modifier == 'E')
729 goto bad_format;
731 DO_NUMBER (2, tp->tm_mon + 1);
733 case 'n': /* POSIX.2 extension. */
734 add (1, *p = '\n');
735 break;
737 case 'P':
738 to_lowcase = 1;
739 /* FALLTHROUGH */
741 case 'p':
742 cpy (ap_len, ampm);
743 break;
745 case 'R': /* GNU extension. */
746 subfmt = "%H:%M";
747 goto subformat;
749 case 'r': /* POSIX.2 extension. */
750 #ifdef _NL_CURRENT
751 if (*(subfmt = _NL_CURRENT (LC_TIME, T_FMT_AMPM)) == '\0')
752 #endif
753 subfmt = "%I:%M:%S %p";
754 goto subformat;
756 case 'S':
757 if (modifier == 'E')
758 goto bad_format;
760 DO_NUMBER (2, tp->tm_sec);
762 case 's': /* GNU extension. */
764 struct tm ltm;
765 time_t t;
767 ltm = *tp;
768 t = mktime (&ltm);
770 /* Generate string value for T using time_t arithmetic;
771 this works even if sizeof (long) < sizeof (time_t). */
773 bufp = buf + sizeof (buf);
774 negative_number = t < 0;
778 int d = t % 10;
779 t /= 10;
781 if (negative_number)
783 d = -d;
785 /* Adjust if division truncates to minus infinity. */
786 if (0 < -1 % 10 && d < 0)
788 t++;
789 d += 10;
793 *--bufp = d + '0';
795 while (t != 0);
797 digits = 1;
798 goto do_number_sign_and_padding;
801 case 'X':
802 if (modifier == 'O')
803 goto bad_format;
804 #ifdef _NL_CURRENT
805 if (! (modifier == 'E'
806 && *(subfmt = _NL_CURRENT (LC_TIME, ERA_T_FMT)) != '\0'))
807 subfmt = _NL_CURRENT (LC_TIME, T_FMT);
808 goto subformat;
809 #endif
810 /* Fall through. */
811 case 'T': /* POSIX.2 extension. */
812 subfmt = "%H:%M:%S";
813 goto subformat;
815 case 't': /* POSIX.2 extension. */
816 add (1, *p = '\t');
817 break;
819 case 'u': /* POSIX.2 extension. */
820 DO_NUMBER (1, (tp->tm_wday - 1 + 7) % 7 + 1);
822 case 'U':
823 if (modifier == 'E')
824 goto bad_format;
826 DO_NUMBER (2, (tp->tm_yday - tp->tm_wday + 7) / 7);
828 case 'V':
829 case 'g': /* GNU extension. */
830 case 'G': /* GNU extension. */
831 if (modifier == 'E')
832 goto bad_format;
834 int year = tp->tm_year + TM_YEAR_BASE;
835 int days = iso_week_days (tp->tm_yday, tp->tm_wday);
837 if (days < 0)
839 /* This ISO week belongs to the previous year. */
840 year--;
841 days = iso_week_days (tp->tm_yday + (365 + __isleap (year)),
842 tp->tm_wday);
844 else
846 int d = iso_week_days (tp->tm_yday - (365 + __isleap (year)),
847 tp->tm_wday);
848 if (0 <= d)
850 /* This ISO week belongs to the next year. */
851 year++;
852 days = d;
856 switch (*f)
858 case 'g':
859 DO_NUMBER (2, (year % 100 + 100) % 100);
861 case 'G':
862 DO_NUMBER (1, year);
864 default:
865 DO_NUMBER (2, days / 7 + 1);
869 case 'W':
870 if (modifier == 'E')
871 goto bad_format;
873 DO_NUMBER (2, (tp->tm_yday - (tp->tm_wday - 1 + 7) % 7 + 7) / 7);
875 case 'w':
876 if (modifier == 'E')
877 goto bad_format;
879 DO_NUMBER (1, tp->tm_wday);
881 case 'Y':
882 #if HAVE_STRUCT_ERA_ENTRY
883 if (modifier == 'E')
885 struct era_entry *era = _nl_get_era_entry (tp);
886 if (era)
888 subfmt = strchr (era->name_fmt, '\0') + 1;
889 goto subformat;
892 #endif
893 if (modifier == 'O')
894 goto bad_format;
895 else
896 DO_NUMBER (1, tp->tm_year + TM_YEAR_BASE);
898 case 'y':
899 #if HAVE_STRUCT_ERA_ENTRY
900 if (modifier == 'E')
902 struct era_entry *era = _nl_get_era_entry (tp);
903 if (era)
905 int delta = tp->tm_year - era->start_date[0];
906 DO_NUMBER (1, (era->offset
907 + (era->direction == '-' ? -delta : delta)));
910 #endif
911 DO_NUMBER (2, (tp->tm_year % 100 + 100) % 100);
913 case 'Z':
914 cpy (zonelen, zone);
915 break;
917 case 'z': /* GNU extension. */
918 if (tp->tm_isdst < 0)
919 break;
922 int diff;
923 #if HAVE_TM_GMTOFF
924 diff = tp->tm_gmtoff;
925 #else
926 struct tm gtm;
927 struct tm ltm;
928 time_t lt;
930 ltm = *tp;
931 lt = mktime (&ltm);
933 if (lt == (time_t) -1)
935 /* mktime returns -1 for errors, but -1 is also a
936 valid time_t value. Check whether an error really
937 occurred. */
938 struct tm tm;
939 localtime_r (&lt, &tm);
941 if ((ltm.tm_sec ^ tm.tm_sec)
942 | (ltm.tm_min ^ tm.tm_min)
943 | (ltm.tm_hour ^ tm.tm_hour)
944 | (ltm.tm_mday ^ tm.tm_mday)
945 | (ltm.tm_mon ^ tm.tm_mon)
946 | (ltm.tm_year ^ tm.tm_year))
947 break;
950 if (! gmtime_r (&lt, &gtm))
951 break;
953 diff = tm_diff (&ltm, &gtm);
954 #endif
956 if (diff < 0)
958 add (1, *p = '-');
959 diff = -diff;
961 else
962 add (1, *p = '+');
964 diff /= 60;
965 DO_NUMBER (4, (diff / 60) * 100 + diff % 60);
968 case '\0': /* GNU extension: % at end of format. */
969 --f;
970 /* Fall through. */
971 default:
972 /* Unknown format; output the format, including the '%',
973 since this is most likely the right thing to do if a
974 multibyte string has been misparsed. */
975 bad_format:
977 int flen;
978 for (flen = 1; f[1 - flen] != '%'; flen++)
979 continue;
980 cpy (flen, &f[1 - flen]);
982 break;
986 if (p)
987 *p = '\0';
988 return i;