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