update from main archive 960919
[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               /* ALT_DIGITS is the first entry in an array with
533                  alternative digit symbols.  We have to find string
534                  number NUMBER_VALUE, but must not look beyond
535                  END_ALT_DIGITS.  */
536               int run = number_value;
537               const char *cp = alt_digits;
538
539               while (run-- > 0 && cp < end_alt_digits)
540                 cp = strchr (cp, '\0') + 1;
541
542               if (cp < end_alt_digits)
543                 {
544                   size_t digitlen = strlen (cp);
545                   if (digitlen != 0)
546                     {
547                       cpy (digitlen, cp);
548                       break;
549                     }
550                 }
551             }
552 #endif
553           {
554             unsigned int u = number_value;
555
556             bufp = buf + sizeof (buf);
557             negative_number = number_value < 0;
558
559             if (negative_number)
560               u = -u;
561
562             do
563               *--bufp = u % 10 + '0';
564             while ((u /= 10) != 0);
565           }
566
567         do_number_sign_and_padding:
568           if (negative_number)
569             *--bufp = '-';
570
571           if (pad != '-')
572             {
573               int padding = digits - (buf + sizeof (buf) - bufp);
574
575               if (pad == '_')
576                 {
577                   while (0 < padding--)
578                     *--bufp = ' ';
579                 }
580               else
581                 {
582                   bufp += negative_number;
583                   while (0 < padding--)
584                     *--bufp = '0';
585                   if (negative_number)
586                     *--bufp = '-';
587                 }
588             }
589
590           cpy (buf + sizeof (buf) - bufp, bufp);
591           break;
592
593
594         case 'H':
595           if (modifier == 'E')
596             goto bad_format;
597
598           DO_NUMBER (2, tp->tm_hour);
599
600         case 'I':
601           if (modifier == 'E')
602             goto bad_format;
603
604           DO_NUMBER (2, hour12);
605
606         case 'k':               /* GNU extension.  */
607           if (modifier == 'E')
608             goto bad_format;
609
610           DO_NUMBER_SPACEPAD (2, tp->tm_hour);
611
612         case 'l':               /* GNU extension.  */
613           if (modifier == 'E')
614             goto bad_format;
615
616           DO_NUMBER_SPACEPAD (2, hour12);
617
618         case 'j':
619           if (modifier == 'E')
620             goto bad_format;
621
622           DO_NUMBER (3, 1 + tp->tm_yday);
623
624         case 'M':
625           if (modifier == 'E')
626             goto bad_format;
627
628           DO_NUMBER (2, tp->tm_min);
629
630         case 'm':
631           if (modifier == 'E')
632             goto bad_format;
633
634           DO_NUMBER (2, tp->tm_mon + 1);
635
636         case 'n':               /* POSIX.2 extension.  */
637           add (1, *p = '\n');
638           break;
639
640         case 'p':
641           cpy (ap_len, ampm);
642           break;
643
644         case 'R':               /* GNU extension.  */
645           subfmt = "%H:%M";
646           goto subformat;
647
648         case 'r':               /* POSIX.2 extension.  */
649 #ifdef _NL_CURRENT
650           if (*(subfmt = _NL_CURRENT (LC_TIME, T_FMT_AMPM)) == '\0')
651 #endif
652             subfmt = "%I:%M:%S %p";
653           goto subformat;
654
655         case 'S':
656           if (modifier == 'E')
657             goto bad_format;
658
659           DO_NUMBER (2, tp->tm_sec);
660
661         case 's':               /* GNU extension.  */
662           {
663             struct tm ltm = *tp;
664             time_t t = mktime (&ltm);
665
666             /* Generate string value for T using time_t arithmetic;
667                this works even if sizeof (long) < sizeof (time_t).  */
668
669             bufp = buf + sizeof (buf);
670             negative_number = t < 0;
671
672             do
673               {
674                 int d = t % 10;
675                 t /= 10;
676
677                 if (negative_number)
678                   {
679                     d = -d;
680
681                     /* Adjust if division truncates to minus infinity.  */
682                     if (0 < -1 % 10 && d < 0)
683                       {
684                         t++;
685                         d += 10;
686                       }
687                   }
688
689                 *--bufp = d + '0';
690               }
691             while (t != 0);
692
693             digits = 1;
694             goto do_number_sign_and_padding;
695           }
696
697         case 'X':
698           if (modifier == 'O')
699             goto bad_format;
700 #ifdef _NL_CURRENT
701           if (! (modifier == 'E'
702                  && *(subfmt = _NL_CURRENT (LC_TIME, ERA_T_FMT)) != '\0'))
703             subfmt = _NL_CURRENT (LC_TIME, T_FMT);
704           goto subformat;
705 #endif
706           /* Fall through.  */
707         case 'T':               /* POSIX.2 extension.  */
708           subfmt = "%H:%M:%S";
709           goto subformat;
710
711         case 't':               /* POSIX.2 extension.  */
712           add (1, *p = '\t');
713           break;
714
715         case 'u':               /* POSIX.2 extension.  */
716           DO_NUMBER (1, (tp->tm_wday - 1 + 7) % 7 + 1);
717
718         case 'U':
719           if (modifier == 'E')
720             goto bad_format;
721
722           DO_NUMBER (2, (tp->tm_yday - tp->tm_wday + 7) / 7);
723
724         case 'V':
725         case 'g':               /* GNU extension.  */
726         case 'G':               /* GNU extension.  */
727           if (modifier == 'E')
728             goto bad_format;
729           {
730             int year = tp->tm_year + TM_YEAR_BASE;
731             int days = iso_week_days (tp->tm_yday, tp->tm_wday);
732
733             if (days < 0)
734               {
735                 /* This ISO week belongs to the previous year.  */
736                 year--;
737                 days = iso_week_days (tp->tm_yday + (365 + __isleap (year)),
738                                       tp->tm_wday);
739               }
740             else
741               {
742                 int d = iso_week_days (tp->tm_yday - (365 + __isleap (year)),
743                                        tp->tm_wday);
744                 if (0 <= d)
745                   {
746                     /* This ISO week belongs to the next year.  */
747                     year++;
748                     days = d;
749                   }
750               }
751
752             switch (*f)
753               {
754               case 'g':
755                 DO_NUMBER (2, (year % 100 + 100) % 100);
756
757               case 'G':
758                 DO_NUMBER (1, year);
759
760               default:
761                 DO_NUMBER (2, days / 7 + 1);
762               }
763           }
764
765         case 'W':
766           if (modifier == 'E')
767             goto bad_format;
768
769           DO_NUMBER (2, (tp->tm_yday - (tp->tm_wday - 1 + 7) % 7 + 7) / 7);
770
771         case 'w':
772           if (modifier == 'E')
773             goto bad_format;
774
775           DO_NUMBER (1, tp->tm_wday);
776
777         case 'Y':
778 #ifdef _NL_CURRENT
779           if (modifier == 'E'
780               && *(subfmt = _NL_CURRENT (LC_TIME, ERA_YEAR)) != '\0')
781             goto subformat;
782 #endif
783           if (modifier == 'O')
784             goto bad_format;
785           else
786             DO_NUMBER (1, tp->tm_year + TM_YEAR_BASE);
787
788         case 'y':
789 #ifdef _NL_CURRENT
790           /* XXX %Ey is not implemented yet.  */
791 #endif
792           DO_NUMBER (2, (tp->tm_year % 100 + 100) % 100);
793
794         case 'Z':
795           cpy(zonelen, zone);
796           break;
797
798         case 'z':               /* GNU extension.  */
799           if (tp->tm_isdst < 0)
800             break;
801
802           {
803             int diff;
804 #if HAVE_TM_GMTOFF
805             diff = tp->tm_gmtoff;
806 #else
807             struct tm gtm;
808             struct tm ltm = *tp;
809             time_t lt = mktime (&ltm);
810
811             if (lt == (time_t) -1)
812               {
813                 /* mktime returns -1 for errors, but -1 is also a
814                    valid time_t value.  Check whether an error really
815                    occurred.  */
816                 struct tm tm;
817                 localtime_r (&lt, &tm);
818
819                 if ((ltm.tm_sec ^ tm.tm_sec)
820                     | (ltm.tm_min ^ tm.tm_min)
821                     | (ltm.tm_hour ^ tm.tm_hour)
822                     | (ltm.tm_mday ^ tm.tm_mday)
823                     | (ltm.tm_mon ^ tm.tm_mon)
824                     | (ltm.tm_year ^ tm.tm_year))
825                   break;
826               }
827
828             if (! gmtime_r (&lt, &gtm))
829               break;
830
831             diff = tm_diff (&ltm, &gtm);
832 #endif
833
834             if (diff < 0)
835               {
836                 add (1, *p = '-');
837                 diff = -diff;
838               }
839             else
840               add (1, *p = '+');
841
842             diff /= 60;
843             DO_NUMBER (4, (diff / 60) * 100 + diff % 60);
844           }
845
846         default:
847           /* Unknown format; output the format, including the '%',
848              since this is most likely the right thing to do if a
849              multibyte string has been misparsed.  */
850         bad_format:
851           {
852             int flen;
853             for (flen = 2; f[1 - flen] != '%'; flen++)
854               continue;
855             cpy (flen, &f[1 - flen]);
856           }
857           break;
858         }
859     }
860
861   if (p)
862     *p = '\0';
863   return i;
864 }