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