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