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