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