update from main archive 961220
[kopensolaris-gnu/glibc.git] / time / strftime.c
1 /* Copyright (C) 1991, 92, 93, 94, 95, 96 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3
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.
8
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.
13
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.  */
18
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22
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
37
38 #include <ctype.h>
39 #include <sys/types.h>          /* Some systems define `time_t' here.  */
40
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
54
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)
62
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
74
75 #if HAVE_LIMITS_H
76 # include <limits.h>
77 #endif
78
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
86
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.  */
94
95 #ifndef PTR
96 # ifdef __STDC__
97 #  define PTR void *
98 # else
99 #  define PTR char *
100 # endif
101 #endif
102
103 #ifndef CHAR_BIT
104 # define CHAR_BIT 8
105 #endif
106
107 #ifndef NULL
108 # define NULL 0
109 #endif
110
111 #define TYPE_SIGNED(t) ((t) -1 < 0)
112
113 /* Bound on length of the string representing an integer value of type t.
114    Subtract one for the sign bit if t is signed;
115    302 / 1000 is log10 (2) rounded up;
116    add one for integer division truncation;
117    add one more for a minus sign if t is signed.  */
118 #define INT_STRLEN_BOUND(t) \
119   ((sizeof (t) * CHAR_BIT - TYPE_SIGNED (t)) * 302 / 100 + 1 + TYPE_SIGNED (t))
120
121 #define TM_YEAR_BASE 1900
122
123 #ifndef __isleap
124 /* Nonzero if YEAR is a leap year (every 4 years,
125    except every 100th isn't, and every 400th is).  */
126 # define __isleap(year) \
127   ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
128 #endif
129
130
131 #ifdef _LIBC
132 # define gmtime_r __gmtime_r
133 # define localtime_r __localtime_r
134 extern int __tz_compute __P ((time_t timer, const struct tm *tm));
135 # define tzname __tzname
136 # define tzset __tzset
137 #else
138 # if ! HAVE_LOCALTIME_R
139 #  if ! HAVE_TM_GMTOFF
140 /* Approximate gmtime_r as best we can in its absence.  */
141 #  define gmtime_r my_gmtime_r
142 static struct tm *gmtime_r __P ((const time_t *, struct tm *));
143 static struct tm *
144 gmtime_r (t, tp)
145      const time_t *t;
146      struct tm *tp;
147 {
148   struct tm *l = gmtime (t);
149   if (! l)
150     return 0;
151   *tp = *l;
152   return tp;
153 }
154 #  endif /* ! HAVE_TM_GMTOFF */
155
156 /* Approximate localtime_r as best we can in its absence.  */
157 #  define localtime_r my_localtime_r
158 static struct tm *localtime_r __P ((const time_t *, struct tm *));
159 static struct tm *
160 localtime_r (t, tp)
161      const time_t *t;
162      struct tm *tp;
163 {
164   struct tm *l = localtime (t);
165   if (! l)
166     return 0;
167   *tp = *l;
168   return tp;
169 }
170 # endif /* ! HAVE_LOCALTIME_R */
171 #endif /* ! defined (_LIBC) */
172
173
174 #if !defined (memset) && !defined (HAVE_MEMSET) && !defined (_LIBC)
175 /* Some systems lack the `memset' function and we don't want to
176    introduce additional dependencies.  */
177 static const char spaces[16] = "                ";
178
179 # define memset_space(P, Len) \
180   do {                                                                        \
181     int _len = (Len);                                                         \
182                                                                               \
183     do                                                                        \
184       {                                                                       \
185         int _this = _len > 16 ? 16 : _len;                                    \
186         memcpy ((P), spaces, _this);                                          \
187         (P) += _this;                                                         \
188         _len -= _this;                                                        \
189       }                                                                       \
190     while (_len > 0);                                                         \
191   } while (0)
192 #else
193 # define memset_space(P, Len) memset ((P), ' ', (Len))
194 #endif
195
196 #define add(n, f) \
197   do                                                                          \
198     {                                                                         \
199       int _n = (n);                                                           \
200       int _delta = width - _n;                                                \
201       int _incr = _n + (_delta > 0 ? _delta : 0);                             \
202       if (i + _incr >= maxsize)                                               \
203         return 0;                                                             \
204       if (p)                                                                  \
205         {                                                                     \
206           if (_delta > 0)                                                     \
207             memset_space (p, _delta);                                         \
208           f;                                                                  \
209           p += _n;                                                            \
210         }                                                                     \
211       i += _incr;                                                             \
212     } while (0)
213
214 #define cpy(n, s) \
215     add ((n),                                                                 \
216          if (to_lowcase)                                                      \
217            memcpy_lowcase (p, (s), _n);                                       \
218          else if (to_uppcase)                                                 \
219            memcpy_uppcase (p, (s), _n);                                       \
220          else                                                                 \
221            memcpy ((PTR) p, (PTR) (s), _n))
222
223
224
225 #ifdef _LIBC
226 # define TOUPPER(Ch) toupper (Ch)
227 # define TOLOWER(Ch) tolower (Ch)
228 #else
229 # define TOUPPER(Ch) (islower (Ch) ? toupper (Ch) : (Ch))
230 # define TOLOWER(Ch) (isupper (Ch) ? tolower (Ch) : (Ch))
231 #endif
232 /* We don't use `isdigit' here since the locale dependent
233    interpretation is not what we want here.  We only need to accept
234    the arabic digits in the ASCII range.  One day there is perhaps a
235    more reliable way to accept other sets of digits.  */
236 #define ISDIGIT(Ch) ((unsigned int) (Ch) - '0' <= 9)
237
238 static char *memcpy_lowcase __P ((char *dest, const char *src, size_t len));
239
240 static char *
241 memcpy_lowcase (dest, src, len)
242      char *dest;
243      const char *src;
244      size_t len;
245 {
246   while (len-- > 0)
247     dest[len] = TOLOWER (src[len]);
248   return dest;
249 }
250
251 static char *memcpy_uppcase __P ((char *dest, const char *src, size_t len));
252
253 static char *
254 memcpy_uppcase (dest, src, len)
255      char *dest;
256      const char *src;
257      size_t len;
258 {
259   while (len-- > 0)
260     dest[len] = TOUPPER (src[len]);
261   return dest;
262 }
263
264 #if ! HAVE_TM_GMTOFF
265 /* Yield the difference between *A and *B,
266    measured in seconds, ignoring leap seconds.  */
267 static int tm_diff __P ((const struct tm *, const struct tm *));
268 static int
269 tm_diff (a, b)
270      const struct tm *a;
271      const struct tm *b;
272 {
273   /* Compute intervening leap days correctly even if year is negative.
274      Take care to avoid int overflow in leap day calculations,
275      but it's OK to assume that A and B are close to each other.  */
276   int a4 = (a->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (a->tm_year & 3);
277   int b4 = (b->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (b->tm_year & 3);
278   int a100 = a4 / 25 - (a4 % 25 < 0);
279   int b100 = b4 / 25 - (b4 % 25 < 0);
280   int a400 = a100 >> 2;
281   int b400 = b100 >> 2;
282   int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
283   int years = a->tm_year - b->tm_year;
284   int days = (365 * years + intervening_leap_days
285               + (a->tm_yday - b->tm_yday));
286   return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
287                 + (a->tm_min - b->tm_min))
288           + (a->tm_sec - b->tm_sec));
289 }
290 #endif /* ! HAVE_TM_GMTOFF */
291
292
293
294 /* The number of days from the first day of the first ISO week of this
295    year to the year day YDAY with week day WDAY.  ISO weeks start on
296    Monday; the first ISO week has the year's first Thursday.  YDAY may
297    be as small as YDAY_MINIMUM.  */
298 #define ISO_WEEK_START_WDAY 1 /* Monday */
299 #define ISO_WEEK1_WDAY 4 /* Thursday */
300 #define YDAY_MINIMUM (-366)
301 static int iso_week_days __P ((int, int));
302 #ifdef __GNUC__
303 inline
304 #endif
305 static int
306 iso_week_days (yday, wday)
307      int yday;
308      int wday;
309 {
310   /* Add enough to the first operand of % to make it nonnegative.  */
311   int big_enough_multiple_of_7 = (-YDAY_MINIMUM / 7 + 2) * 7;
312   return (yday
313           - (yday - wday + ISO_WEEK1_WDAY + big_enough_multiple_of_7) % 7
314           + ISO_WEEK1_WDAY - ISO_WEEK_START_WDAY);
315 }
316
317
318 #ifndef _NL_CURRENT
319 static char const weekday_name[][10] =
320   {
321     "Sunday", "Monday", "Tuesday", "Wednesday",
322     "Thursday", "Friday", "Saturday"
323   };
324 static char const month_name[][10] =
325   {
326     "January", "February", "March", "April", "May", "June",
327     "July", "August", "September", "October", "November", "December"
328   };
329 #endif
330
331 /* Write information from TP into S according to the format
332    string FORMAT, writing no more that MAXSIZE characters
333    (including the terminating '\0') and returning number of
334    characters written.  If S is NULL, nothing will be written
335    anywhere, so to determine how many characters would be
336    written, use NULL for S and (size_t) UINT_MAX for MAXSIZE.  */
337 size_t
338 strftime (s, maxsize, format, tp)
339       char *s;
340       size_t maxsize;
341       const char *format;
342       const struct tm *tp;
343 {
344   int hour12 = tp->tm_hour;
345 #ifdef _NL_CURRENT
346   const char *const a_wkday = _NL_CURRENT (LC_TIME, ABDAY_1 + tp->tm_wday);
347   const char *const f_wkday = _NL_CURRENT (LC_TIME, DAY_1 + tp->tm_wday);
348   const char *const a_month = _NL_CURRENT (LC_TIME, ABMON_1 + tp->tm_mon);
349   const char *const f_month = _NL_CURRENT (LC_TIME, MON_1 + tp->tm_mon);
350   const char *const ampm = _NL_CURRENT (LC_TIME,
351                                         hour12 > 11 ? PM_STR : AM_STR);
352   size_t aw_len = strlen (a_wkday);
353   size_t am_len = strlen (a_month);
354   size_t ap_len = strlen (ampm);
355 #else
356   const char *const f_wkday = weekday_name[tp->tm_wday];
357   const char *const f_month = month_name[tp->tm_mon];
358   const char *const a_wkday = f_wkday;
359   const char *const a_month = f_month;
360   const char *const ampm = "AMPM" + 2 * (hour12 > 11);
361   size_t aw_len = 3;
362   size_t am_len = 3;
363   size_t ap_len = 2;
364 #endif
365   size_t wkday_len = strlen (f_wkday);
366   size_t month_len = strlen (f_month);
367   const char *zone;
368   size_t zonelen;
369   size_t i = 0;
370   char *p = s;
371   const char *f;
372
373   zone = NULL;
374 #if !defined _LIBC && HAVE_TM_ZONE
375   /* XXX We have some problems here.  First, the string pointed to by
376      tm_zone is dynamically allocated while loading the zone data.  But
377      when another zone is loaded since the information in TP were
378      computed this would be a stale pointer.
379      The second problem is the POSIX test suite which assumes setting
380      the environment variable TZ to a new value before calling strftime()
381      will influence the result (the %Z format) even if the information in
382      TP is computed with a totally different time zone.  --drepper@gnu  */
383   zone = (const char *) tp->tm_zone;
384 #endif
385 #if HAVE_TZNAME
386   /* POSIX.1 8.1.1 requires that whenever strftime() is called, the
387      time zone names contained in the external variable `tzname' shall
388      be set as if the tzset() function had been called.  */
389 # if HAVE_TZSET
390   tzset ();
391 # endif
392
393   if (!(zone && *zone) && tp->tm_isdst >= 0)
394     zone = tzname[tp->tm_isdst];
395 #endif
396   if (! zone)
397     zone = "";          /* POSIX.2 requires the empty string here.  */
398
399   zonelen = strlen (zone);
400
401   if (hour12 > 12)
402     hour12 -= 12;
403   else
404     if (hour12 == 0) hour12 = 12;
405
406   for (f = format; *f != '\0'; ++f)
407     {
408       int pad;                  /* Padding for number ('-', '_', or 0).  */
409       int modifier;             /* Field modifier ('E', 'O', or 0).  */
410       int digits;               /* Max digits for numeric format.  */
411       int number_value;         /* Numeric value to be printed.  */
412       int negative_number;      /* 1 if the number is negative.  */
413       const char *subfmt;
414       char *bufp;
415       char buf[1 + (sizeof (int) < sizeof (time_t)
416                     ? INT_STRLEN_BOUND (time_t)
417                     : INT_STRLEN_BOUND (int))];
418       int width = -1;
419       int to_lowcase = 0;
420       int to_uppcase = 0;
421
422 #if DO_MULTIBYTE
423
424        switch (*f)
425         {
426         case '%':
427           break;
428
429         case '\a': case '\b': case '\t': case '\n':
430         case '\v': case '\f': case '\r':
431         case ' ': case '!': case '"': case '#': case '&': case'\'':
432         case '(': case ')': case '*': case '+': case ',': case '-':
433         case '.': case '/': case '0': case '1': case '2': case '3':
434         case '4': case '5': case '6': case '7': case '8': case '9':
435         case ':': case ';': case '<': case '=': case '>': case '?':
436         case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
437         case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
438         case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
439         case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
440         case 'Y': case 'Z': case '[': case'\\': case ']': case '^':
441         case '_': case 'a': case 'b': case 'c': case 'd': case 'e':
442         case 'f': case 'g': case 'h': case 'i': case 'j': case 'k':
443         case 'l': case 'm': case 'n': case 'o': case 'p': case 'q':
444         case 'r': case 's': case 't': case 'u': case 'v': case 'w':
445         case 'x': case 'y': case 'z': case '{': case '|': case '}':
446         case '~':
447           /* The C Standard requires these 98 characters (plus '%') to
448              be in the basic execution character set.  None of these
449              characters can start a multibyte sequence, so they need
450              not be analyzed further.  */
451           add (1, *p = *f);
452           continue;
453
454         default:
455           /* Copy this multibyte sequence until we reach its end, find
456              an error, or come back to the initial shift state.  */
457           {
458             mbstate_t mbstate = mbstate_zero;
459             size_t len = 0;
460
461             do
462               {
463                 size_t bytes = mbrlen (f + len, (size_t) -1, &mbstate);
464
465                 if (bytes == 0)
466                   break;
467
468                 if (bytes == (size_t) -2 || bytes == (size_t) -1)
469                   {
470                     len++;
471                     break;
472                   }
473
474                 len += bytes;
475               }
476             while (! mbsinit (&mbstate));
477
478             cpy (len, f);
479             continue;
480           }
481         }
482
483 #else /* ! DO_MULTIBYTE */
484
485       /* Either multibyte encodings are not supported, or they are
486          safe for formats, so any non-'%' byte can be copied through.  */
487       if (*f != '%')
488         {
489           add (1, *p = *f);
490           continue;
491         }
492
493 #endif /* ! DO_MULTIBYTE */
494
495       /* Check for flags that can modify a format.  */
496       while (1)
497         {
498           switch (*++f)
499             {
500               /* This influences the number formats.  */
501             case '_':
502             case '-':
503             case '0':
504               pad = *f;
505               continue;
506
507               /* This changes textual output.  */
508             case '^':
509               to_uppcase = 1;
510               continue;
511
512             default:
513               pad = 0;
514               break;
515             }
516           break;
517         }
518
519       /* As a GNU extension we allow to specify the field width.  */
520       if (ISDIGIT (*f))
521         {
522           width = 0;
523           do
524             {
525               width *= 10;
526               width += *f - '0';
527               ++f;
528             }
529           while (ISDIGIT (*f));
530         }
531
532       /* Check for modifiers.  */
533       switch (*f)
534         {
535         case 'E':
536         case 'O':
537           modifier = *f++;
538           break;
539
540         default:
541           modifier = 0;
542           break;
543         }
544
545       /* Now do the specified format.  */
546       switch (*f)
547         {
548 #define DO_NUMBER(d, v) \
549           digits = d; number_value = v; goto do_number
550 #define DO_NUMBER_SPACEPAD(d, v) \
551           digits = d; number_value = v; goto do_number_spacepad
552
553         case '%':
554           if (modifier != 0)
555             goto bad_format;
556           add (1, *p = *f);
557           break;
558
559         case 'a':
560           if (modifier != 0)
561             goto bad_format;
562           cpy (aw_len, a_wkday);
563           break;
564
565         case 'A':
566           if (modifier != 0)
567             goto bad_format;
568           cpy (wkday_len, f_wkday);
569           break;
570
571         case 'b':
572         case 'h':               /* POSIX.2 extension.  */
573           if (modifier != 0)
574             goto bad_format;
575           cpy (am_len, a_month);
576           break;
577
578         case 'B':
579           if (modifier != 0)
580             goto bad_format;
581           cpy (month_len, f_month);
582           break;
583
584         case 'c':
585           if (modifier == 'O')
586             goto bad_format;
587 #ifdef _NL_CURRENT
588           if (! (modifier == 'E'
589                  && *(subfmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT)) != '\0'))
590             subfmt = _NL_CURRENT (LC_TIME, D_T_FMT);
591 #else
592           subfmt = "%a %b %e %H:%M:%S %Y";
593 #endif
594
595         subformat:
596           {
597             char *old_start = p;
598             size_t len = strftime (NULL, maxsize - i, subfmt, tp);
599             if (len == 0 && *subfmt)
600               return 0;
601             add (len, strftime (p, maxsize - i, subfmt, tp));
602
603             if (to_uppcase)
604               while (old_start < p)
605                 {
606                   *old_start = TOUPPER (*old_start);
607                   ++old_start;
608                 }
609           }
610           break;
611
612         case 'C':               /* POSIX.2 extension.  */
613           if (modifier == 'O')
614             goto bad_format;
615 #if HAVE_STRUCT_ERA_ENTRY
616           if (modifier == 'E')
617             {
618               struct era_entry *era = _nl_get_era_entry (tp);
619               if (era)
620                 {
621                   size_t len = strlen (era->name_fmt);
622                   cpy (len, era->name_fmt);
623                   break;
624                 }
625             }
626 #endif
627           {
628             int year = tp->tm_year + TM_YEAR_BASE;
629             DO_NUMBER (1, year / 100 - (year % 100 < 0));
630           }
631
632         case 'x':
633           if (modifier == 'O')
634             goto bad_format;
635 #ifdef _NL_CURRENT
636           if (! (modifier == 'E'
637                  && *(subfmt = _NL_CURRENT (LC_TIME, ERA_D_FMT)) != '\0'))
638             subfmt = _NL_CURRENT (LC_TIME, D_FMT);
639           goto subformat;
640 #endif
641           /* Fall through.  */
642         case 'D':               /* POSIX.2 extension.  */
643           if (modifier != 0)
644             goto bad_format;
645           subfmt = "%m/%d/%y";
646           goto subformat;
647
648         case 'd':
649           if (modifier == 'E')
650             goto bad_format;
651
652           DO_NUMBER (2, tp->tm_mday);
653
654         case 'e':               /* POSIX.2 extension.  */
655           if (modifier == 'E')
656             goto bad_format;
657
658           DO_NUMBER_SPACEPAD (2, tp->tm_mday);
659
660           /* All numeric formats set DIGITS and NUMBER_VALUE and then
661              jump to one of these two labels.  */
662
663         do_number_spacepad:
664           /* Force `_' flag unless overwritten by `0' flag.  */
665           if (pad != '0')
666             pad = '_';
667
668         do_number:
669           /* Format the number according to the MODIFIER flag.  */
670
671 #ifdef _NL_CURRENT
672           if (modifier == 'O' && 0 <= number_value)
673             {
674               /* Get the locale specific alternate representation of
675                  the number NUMBER_VALUE.  If none exist NULL is returned.  */
676               const char *cp = _nl_get_alt_digit (number_value);
677
678               if (cp != NULL)
679                 {
680                   size_t digitlen = strlen (cp);
681                   if (digitlen != 0)
682                     {
683                       cpy (digitlen, cp);
684                       break;
685                     }
686                 }
687             }
688 #endif
689           {
690             unsigned int u = number_value;
691
692             bufp = buf + sizeof (buf);
693             negative_number = number_value < 0;
694
695             if (negative_number)
696               u = -u;
697
698             do
699               *--bufp = u % 10 + '0';
700             while ((u /= 10) != 0);
701           }
702
703         do_number_sign_and_padding:
704           if (negative_number)
705             *--bufp = '-';
706
707           if (pad != '-')
708             {
709               int padding = digits - (buf + sizeof (buf) - bufp);
710
711               if (pad == '_')
712                 {
713                   while (0 < padding--)
714                     *--bufp = ' ';
715                 }
716               else
717                 {
718                   bufp += negative_number;
719                   while (0 < padding--)
720                     *--bufp = '0';
721                   if (negative_number)
722                     *--bufp = '-';
723                 }
724             }
725
726           cpy (buf + sizeof (buf) - bufp, bufp);
727           break;
728
729
730         case 'H':
731           if (modifier == 'E')
732             goto bad_format;
733
734           DO_NUMBER (2, tp->tm_hour);
735
736         case 'I':
737           if (modifier == 'E')
738             goto bad_format;
739
740           DO_NUMBER (2, hour12);
741
742         case 'k':               /* GNU extension.  */
743           if (modifier == 'E')
744             goto bad_format;
745
746           DO_NUMBER_SPACEPAD (2, tp->tm_hour);
747
748         case 'l':               /* GNU extension.  */
749           if (modifier == 'E')
750             goto bad_format;
751
752           DO_NUMBER_SPACEPAD (2, hour12);
753
754         case 'j':
755           if (modifier == 'E')
756             goto bad_format;
757
758           DO_NUMBER (3, 1 + tp->tm_yday);
759
760         case 'M':
761           if (modifier == 'E')
762             goto bad_format;
763
764           DO_NUMBER (2, tp->tm_min);
765
766         case 'm':
767           if (modifier == 'E')
768             goto bad_format;
769
770           DO_NUMBER (2, tp->tm_mon + 1);
771
772         case 'n':               /* POSIX.2 extension.  */
773           add (1, *p = '\n');
774           break;
775
776         case 'P':
777           to_lowcase = 1;
778           /* FALLTHROUGH */
779
780         case 'p':
781           cpy (ap_len, ampm);
782           break;
783
784         case 'R':               /* GNU extension.  */
785           subfmt = "%H:%M";
786           goto subformat;
787
788         case 'r':               /* POSIX.2 extension.  */
789 #ifdef _NL_CURRENT
790           if (*(subfmt = _NL_CURRENT (LC_TIME, T_FMT_AMPM)) == '\0')
791 #endif
792             subfmt = "%I:%M:%S %p";
793           goto subformat;
794
795         case 'S':
796           if (modifier == 'E')
797             goto bad_format;
798
799           DO_NUMBER (2, tp->tm_sec);
800
801         case 's':               /* GNU extension.  */
802           {
803             struct tm ltm;
804             time_t t;
805
806             ltm = *tp;
807             t = mktime (&ltm);
808
809             /* Generate string value for T using time_t arithmetic;
810                this works even if sizeof (long) < sizeof (time_t).  */
811
812             bufp = buf + sizeof (buf);
813             negative_number = t < 0;
814
815             do
816               {
817                 int d = t % 10;
818                 t /= 10;
819
820                 if (negative_number)
821                   {
822                     d = -d;
823
824                     /* Adjust if division truncates to minus infinity.  */
825                     if (0 < -1 % 10 && d < 0)
826                       {
827                         t++;
828                         d += 10;
829                       }
830                   }
831
832                 *--bufp = d + '0';
833               }
834             while (t != 0);
835
836             digits = 1;
837             goto do_number_sign_and_padding;
838           }
839
840         case 'X':
841           if (modifier == 'O')
842             goto bad_format;
843 #ifdef _NL_CURRENT
844           if (! (modifier == 'E'
845                  && *(subfmt = _NL_CURRENT (LC_TIME, ERA_T_FMT)) != '\0'))
846             subfmt = _NL_CURRENT (LC_TIME, T_FMT);
847           goto subformat;
848 #endif
849           /* Fall through.  */
850         case 'T':               /* POSIX.2 extension.  */
851           subfmt = "%H:%M:%S";
852           goto subformat;
853
854         case 't':               /* POSIX.2 extension.  */
855           add (1, *p = '\t');
856           break;
857
858         case 'u':               /* POSIX.2 extension.  */
859           DO_NUMBER (1, (tp->tm_wday - 1 + 7) % 7 + 1);
860
861         case 'U':
862           if (modifier == 'E')
863             goto bad_format;
864
865           DO_NUMBER (2, (tp->tm_yday - tp->tm_wday + 7) / 7);
866
867         case 'V':
868         case 'g':               /* GNU extension.  */
869         case 'G':               /* GNU extension.  */
870           if (modifier == 'E')
871             goto bad_format;
872           {
873             int year = tp->tm_year + TM_YEAR_BASE;
874             int days = iso_week_days (tp->tm_yday, tp->tm_wday);
875
876             if (days < 0)
877               {
878                 /* This ISO week belongs to the previous year.  */
879                 year--;
880                 days = iso_week_days (tp->tm_yday + (365 + __isleap (year)),
881                                       tp->tm_wday);
882               }
883             else
884               {
885                 int d = iso_week_days (tp->tm_yday - (365 + __isleap (year)),
886                                        tp->tm_wday);
887                 if (0 <= d)
888                   {
889                     /* This ISO week belongs to the next year.  */
890                     year++;
891                     days = d;
892                   }
893               }
894
895             switch (*f)
896               {
897               case 'g':
898                 DO_NUMBER (2, (year % 100 + 100) % 100);
899
900               case 'G':
901                 DO_NUMBER (1, year);
902
903               default:
904                 DO_NUMBER (2, days / 7 + 1);
905               }
906           }
907
908         case 'W':
909           if (modifier == 'E')
910             goto bad_format;
911
912           DO_NUMBER (2, (tp->tm_yday - (tp->tm_wday - 1 + 7) % 7 + 7) / 7);
913
914         case 'w':
915           if (modifier == 'E')
916             goto bad_format;
917
918           DO_NUMBER (1, tp->tm_wday);
919
920         case 'Y':
921 #if HAVE_STRUCT_ERA_ENTRY
922           if (modifier == 'E')
923             {
924               struct era_entry *era = _nl_get_era_entry (tp);
925               if (era)
926                 {
927                   subfmt = strchr (era->name_fmt, '\0') + 1;
928                   goto subformat;
929                 }
930             }
931 #endif
932           if (modifier == 'O')
933             goto bad_format;
934           else
935             DO_NUMBER (1, tp->tm_year + TM_YEAR_BASE);
936
937         case 'y':
938 #if HAVE_STRUCT_ERA_ENTRY
939           if (modifier == 'E')
940             {
941               struct era_entry *era = _nl_get_era_entry (tp);
942               if (era)
943                 {
944                   int delta = tp->tm_year - era->start_date[0];
945                   DO_NUMBER (1, (era->offset
946                                  + (era->direction == '-' ? -delta : delta)));
947                 }
948             }
949 #endif
950           DO_NUMBER (2, (tp->tm_year % 100 + 100) % 100);
951
952         case 'Z':
953           cpy (zonelen, zone);
954           break;
955
956         case 'z':               /* GNU extension.  */
957           if (tp->tm_isdst < 0)
958             break;
959
960           {
961             int diff;
962 #if HAVE_TM_GMTOFF
963             diff = tp->tm_gmtoff;
964 #else
965             struct tm gtm;
966             struct tm ltm;
967             time_t lt;
968
969             ltm = *tp;
970             lt = mktime (&ltm);
971
972             if (lt == (time_t) -1)
973               {
974                 /* mktime returns -1 for errors, but -1 is also a
975                    valid time_t value.  Check whether an error really
976                    occurred.  */
977                 struct tm tm;
978                 localtime_r (&lt, &tm);
979
980                 if ((ltm.tm_sec ^ tm.tm_sec)
981                     | (ltm.tm_min ^ tm.tm_min)
982                     | (ltm.tm_hour ^ tm.tm_hour)
983                     | (ltm.tm_mday ^ tm.tm_mday)
984                     | (ltm.tm_mon ^ tm.tm_mon)
985                     | (ltm.tm_year ^ tm.tm_year))
986                   break;
987               }
988
989             if (! gmtime_r (&lt, &gtm))
990               break;
991
992             diff = tm_diff (&ltm, &gtm);
993 #endif
994
995             if (diff < 0)
996               {
997                 add (1, *p = '-');
998                 diff = -diff;
999               }
1000             else
1001               add (1, *p = '+');
1002
1003             diff /= 60;
1004             DO_NUMBER (4, (diff / 60) * 100 + diff % 60);
1005           }
1006
1007         case '\0':              /* GNU extension: % at end of format.  */
1008             --f;
1009             /* Fall through.  */
1010         default:
1011           /* Unknown format; output the format, including the '%',
1012              since this is most likely the right thing to do if a
1013              multibyte string has been misparsed.  */
1014         bad_format:
1015           {
1016             int flen;
1017             for (flen = 1; f[1 - flen] != '%'; flen++)
1018               continue;
1019             cpy (flen, &f[1 - flen]);
1020           }
1021           break;
1022         }
1023     }
1024
1025   if (p)
1026     *p = '\0';
1027   return i;
1028 }