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