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