update from main archive 961207
[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 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 %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;
662             time_t t;
663
664             ltm = *tp;
665             t = mktime (&ltm);
666
667             /* Generate string value for T using time_t arithmetic;
668                this works even if sizeof (long) < sizeof (time_t).  */
669
670             bufp = buf + sizeof (buf);
671             negative_number = t < 0;
672
673             do
674               {
675                 int d = t % 10;
676                 t /= 10;
677
678                 if (negative_number)
679                   {
680                     d = -d;
681
682                     /* Adjust if division truncates to minus infinity.  */
683                     if (0 < -1 % 10 && d < 0)
684                       {
685                         t++;
686                         d += 10;
687                       }
688                   }
689
690                 *--bufp = d + '0';
691               }
692             while (t != 0);
693
694             digits = 1;
695             goto do_number_sign_and_padding;
696           }
697
698         case 'X':
699           if (modifier == 'O')
700             goto bad_format;
701 #ifdef _NL_CURRENT
702           if (! (modifier == 'E'
703                  && *(subfmt = _NL_CURRENT (LC_TIME, ERA_T_FMT)) != '\0'))
704             subfmt = _NL_CURRENT (LC_TIME, T_FMT);
705           goto subformat;
706 #endif
707           /* Fall through.  */
708         case 'T':               /* POSIX.2 extension.  */
709           subfmt = "%H:%M:%S";
710           goto subformat;
711
712         case 't':               /* POSIX.2 extension.  */
713           add (1, *p = '\t');
714           break;
715
716         case 'u':               /* POSIX.2 extension.  */
717           DO_NUMBER (1, (tp->tm_wday - 1 + 7) % 7 + 1);
718
719         case 'U':
720           if (modifier == 'E')
721             goto bad_format;
722
723           DO_NUMBER (2, (tp->tm_yday - tp->tm_wday + 7) / 7);
724
725         case 'V':
726         case 'g':               /* GNU extension.  */
727         case 'G':               /* GNU extension.  */
728           if (modifier == 'E')
729             goto bad_format;
730           {
731             int year = tp->tm_year + TM_YEAR_BASE;
732             int days = iso_week_days (tp->tm_yday, tp->tm_wday);
733
734             if (days < 0)
735               {
736                 /* This ISO week belongs to the previous year.  */
737                 year--;
738                 days = iso_week_days (tp->tm_yday + (365 + __isleap (year)),
739                                       tp->tm_wday);
740               }
741             else
742               {
743                 int d = iso_week_days (tp->tm_yday - (365 + __isleap (year)),
744                                        tp->tm_wday);
745                 if (0 <= d)
746                   {
747                     /* This ISO week belongs to the next year.  */
748                     year++;
749                     days = d;
750                   }
751               }
752
753             switch (*f)
754               {
755               case 'g':
756                 DO_NUMBER (2, (year % 100 + 100) % 100);
757
758               case 'G':
759                 DO_NUMBER (1, year);
760
761               default:
762                 DO_NUMBER (2, days / 7 + 1);
763               }
764           }
765
766         case 'W':
767           if (modifier == 'E')
768             goto bad_format;
769
770           DO_NUMBER (2, (tp->tm_yday - (tp->tm_wday - 1 + 7) % 7 + 7) / 7);
771
772         case 'w':
773           if (modifier == 'E')
774             goto bad_format;
775
776           DO_NUMBER (1, tp->tm_wday);
777
778         case 'Y':
779 #if HAVE_STRUCT_ERA_ENTRY
780           if (modifier == 'E')
781             {
782               struct era_entry *era = _nl_get_era_entry (tp);
783               if (era)
784                 {
785                   subfmt = strchr (era->name_fmt, '\0') + 1;
786                   goto subformat;
787                 }
788             }
789 #endif
790           if (modifier == 'O')
791             goto bad_format;
792           else
793             DO_NUMBER (1, tp->tm_year + TM_YEAR_BASE);
794
795         case 'y':
796 #if HAVE_STRUCT_ERA_ENTRY
797           if (modifier == 'E')
798             {
799               struct era_entry *era = _nl_get_era_entry (tp);
800               if (era)
801                 {
802                   int delta = tp->tm_year - era->start_date[0];
803                   DO_NUMBER (1, (era->offset
804                                  + (era->direction == '-' ? -delta : delta)));
805                 }
806             }
807 #endif
808           DO_NUMBER (2, (tp->tm_year % 100 + 100) % 100);
809
810         case 'Z':
811           cpy (zonelen, zone);
812           break;
813
814         case 'z':               /* GNU extension.  */
815           if (tp->tm_isdst < 0)
816             break;
817
818           {
819             int diff;
820 #if HAVE_TM_GMTOFF
821             diff = tp->tm_gmtoff;
822 #else
823             struct tm gtm;
824             struct tm ltm;
825             time_t lt;
826
827             ltm = *tp;
828             lt = mktime (&ltm);
829
830             if (lt == (time_t) -1)
831               {
832                 /* mktime returns -1 for errors, but -1 is also a
833                    valid time_t value.  Check whether an error really
834                    occurred.  */
835                 struct tm tm;
836                 localtime_r (&lt, &tm);
837
838                 if ((ltm.tm_sec ^ tm.tm_sec)
839                     | (ltm.tm_min ^ tm.tm_min)
840                     | (ltm.tm_hour ^ tm.tm_hour)
841                     | (ltm.tm_mday ^ tm.tm_mday)
842                     | (ltm.tm_mon ^ tm.tm_mon)
843                     | (ltm.tm_year ^ tm.tm_year))
844                   break;
845               }
846
847             if (! gmtime_r (&lt, &gtm))
848               break;
849
850             diff = tm_diff (&ltm, &gtm);
851 #endif
852
853             if (diff < 0)
854               {
855                 add (1, *p = '-');
856                 diff = -diff;
857               }
858             else
859               add (1, *p = '+');
860
861             diff /= 60;
862             DO_NUMBER (4, (diff / 60) * 100 + diff % 60);
863           }
864
865         case '\0':              /* GNU extension: % at end of format.  */
866             --f;
867             /* Fall through.  */
868         default:
869           /* Unknown format; output the format, including the '%',
870              since this is most likely the right thing to do if a
871              multibyte string has been misparsed.  */
872         bad_format:
873           {
874             int flen;
875             for (flen = 1; f[1 - flen] != '%'; flen++)
876               continue;
877             cpy (flen, &f[1 - flen]);
878           }
879           break;
880         }
881     }
882
883   if (p)
884     *p = '\0';
885   return i;
886 }