update from main archive 960912
[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_TM_GMTOFF 1
27 # define HAVE_TM_ZONE 1
28 # define STDC_HEADERS 1
29 # include <ansidecl.h>
30 # include "../locale/localeinfo.h"
31 #endif
32
33 #include <sys/types.h>          /* Some systems define `time_t' here.  */
34
35 #ifdef TIME_WITH_SYS_TIME
36 # include <sys/time.h>
37 # include <time.h>
38 #else
39 # ifdef HAVE_SYS_TIME_H
40 #  include <sys/time.h>
41 # else
42 #  include <time.h>
43 # endif
44 #endif
45
46 #if HAVE_MBLEN
47 # include <ctype.h>
48 #endif
49
50 #if HAVE_LIMITS_H
51 # include <limits.h>
52 #endif
53
54 #if STDC_HEADERS
55 # include <stddef.h>
56 # include <stdlib.h>
57 # include <string.h>
58 #else
59 # define memcpy(d, s, n) bcopy (s, d, n)
60 #endif
61
62 #ifndef __P
63 #if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
64 #define __P(args) args
65 #else
66 #define __P(args) ()
67 #endif  /* GCC.  */
68 #endif  /* Not __P.  */
69
70 #ifndef PTR
71 #ifdef __STDC__
72 #define PTR void *
73 #else
74 #define PTR char *
75 #endif
76 #endif
77
78 #ifndef CHAR_BIT
79 #define CHAR_BIT 8
80 #endif
81
82 #define TYPE_SIGNED(t) ((t) -1 < 0)
83
84 /* Bound on length of the string representing an integer value of type t.
85    Subtract one for the sign bit if t is signed;
86    302 / 1000 is log10 (2) rounded up;
87    add one for integer division truncation;
88    add one more for a minus sign if t is signed.  */
89 #define INT_STRLEN_BOUND(t) \
90   ((sizeof (t) * CHAR_BIT - TYPE_SIGNED (t)) * 302 / 100 + 1 + TYPE_SIGNED (t))
91
92 #define TM_YEAR_BASE 1900
93
94
95 #ifdef _LIBC
96 # define gmtime_r __gmtime_r
97 # define localtime_r __localtime_r
98 #else
99 # if ! HAVE_LOCALTIME_R
100 #  if ! HAVE_TM_GMTOFF
101 /* Approximate gmtime_r as best we can in its absence.  */
102 #define gmtime_r my_gmtime_r
103 static struct tm *gmtime_r __P ((const time_t *, struct tm *));
104 static struct tm *
105 gmtime_r (t, tp)
106      const time_t *t;
107      struct tm *tp;
108 {
109   struct tm *l = gmtime (t);
110   if (! l)
111     return 0;
112   *tp = *l;
113   return tp;
114 }
115 #  endif /* ! HAVE_TM_GMTOFF */
116
117 /* Approximate localtime_r as best we can in its absence.  */
118 #define localtime_r my_localtime_r
119 static struct tm *localtime_r __P ((const time_t *, struct tm *));
120 static struct tm *
121 localtime_r (t, tp)
122      const time_t *t;
123      struct tm *tp;
124 {
125   struct tm *l = localtime (t);
126   if (! l)
127     return 0;
128   *tp = *l;
129   return tp;
130 }
131 # endif /* ! HAVE_LOCALTIME_R */
132 #endif /* ! defined (_LIBC) */
133
134
135 static unsigned int week __P ((const struct tm *const, int, int));
136
137
138 #define add(n, f)                                                             \
139   do                                                                          \
140     {                                                                         \
141       i += (n);                                                               \
142       if (i >= maxsize)                                                       \
143         return 0;                                                             \
144       else                                                                    \
145         if (p)                                                                \
146           {                                                                   \
147             f;                                                                \
148             p += (n);                                                         \
149           }                                                                   \
150     } while (0)
151 #define cpy(n, s)       add ((n), memcpy((PTR) p, (PTR) (s), (n)))
152
153 #if ! HAVE_TM_GMTOFF
154 /* Yield the difference between *A and *B,
155    measured in seconds, ignoring leap seconds.  */
156 static int tm_diff __P ((const struct tm *, const struct tm *));
157 static int
158 tm_diff (a, b)
159      const struct tm *a;
160      const struct tm *b;
161 {
162   int ay = a->tm_year + TM_YEAR_BASE - 1;
163   int by = b->tm_year + TM_YEAR_BASE - 1;
164   /* Divide years by 100, rounding towards minus infinity.  */
165   int ac = ay / 100 - (ay % 100 < 0);
166   int bc = by / 100 - (by % 100 < 0);
167   int intervening_leap_days =
168     ((ay >> 2) - (by >> 2)) - (ac - bc) + ((ac >> 2) - (bc >> 2));
169   int years = ay - by;
170   int days = (365 * years + intervening_leap_days
171               + (a->tm_yday - b->tm_yday));
172   return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
173                 + (a->tm_min - b->tm_min))
174           + (a->tm_sec - b->tm_sec));
175 }
176 #endif /* ! HAVE_TM_GMTOFF */
177
178
179
180 /* Return the week in the year specified by TP,
181    with weeks starting on STARTING_DAY.  */
182 #ifdef  __GNUC__
183 inline
184 #endif
185 static unsigned int
186 week (tp, starting_day, max_preceding)
187       const struct tm *const tp;
188       int starting_day;
189       int max_preceding;
190 {
191   int wday, dl, base;
192
193   wday = tp->tm_wday - starting_day;
194   if (wday < 0)
195     wday += 7;
196
197   /* Set DL to the day in the year of the first day of the week
198      containing the day specified in TP.  */
199   dl = tp->tm_yday - wday;
200
201   /* For the computation following ISO 8601:1988 we set the number of
202      the week containing January 1st to 1 if this week has more than
203      MAX_PRECEDING days in the new year.  For ISO 8601 this number is
204      3, for the other representation it is 7 (i.e., not to be
205      fulfilled).  */
206   base = ((dl + 7) % 7) > max_preceding ? 1 : 0;
207
208   /* If DL is negative we compute the result as 0 unless we have to
209      compute it according ISO 8601.  In this case we have to return 53
210      or 1 if the week containing January 1st has less than 4 days in
211      the new year or not.  If DL is not negative we calculate the
212      number of complete weeks for our week (DL / 7) plus 1 (because
213      only for DL < 0 we are in week 0/53 and plus the number of the
214      first week computed in the last step.  */
215   return dl < 0 ? (dl < -max_preceding ? 53 : base)
216                 : base + 1 + dl / 7;
217 }
218
219 #ifndef _NL_CURRENT
220 static char const weekday_name[][10] =
221   {
222     "Sunday", "Monday", "Tuesday", "Wednesday",
223     "Thursday", "Friday", "Saturday"
224   };
225 static char const month_name[][10] =
226   {
227     "January", "February", "March", "April", "May", "June",
228     "July", "August", "September", "October", "November", "December"
229   };
230 #endif
231
232 /* Write information from TP into S according to the format
233    string FORMAT, writing no more that MAXSIZE characters
234    (including the terminating '\0') and returning number of
235    characters written.  If S is NULL, nothing will be written
236    anywhere, so to determine how many characters would be
237    written, use NULL for S and (size_t) UINT_MAX for MAXSIZE.  */
238 size_t
239 strftime (s, maxsize, format, tp)
240       char *s;
241       size_t maxsize;
242       const char *format;
243       register const struct tm *tp;
244 {
245   int hour12 = tp->tm_hour;
246 #ifdef _NL_CURRENT
247   const char *const a_wkday = _NL_CURRENT (LC_TIME, ABDAY_1 + tp->tm_wday);
248   const char *const f_wkday = _NL_CURRENT (LC_TIME, DAY_1 + tp->tm_wday);
249   const char *const a_month = _NL_CURRENT (LC_TIME, ABMON_1 + tp->tm_mon);
250   const char *const f_month = _NL_CURRENT (LC_TIME, MON_1 + tp->tm_mon);
251   const char *const ampm = _NL_CURRENT (LC_TIME,
252                                         hour12 > 11 ? PM_STR : AM_STR);
253   size_t aw_len = strlen(a_wkday);
254   size_t am_len = strlen(a_month);
255   size_t ap_len = strlen (ampm);
256
257   const char * const*alt_digits = &_NL_CURRENT (LC_TIME, ALT_DIGITS);
258   int nr_alt_digits = (_NL_CURRENT (LC_TIME, ALT_DIGITS + 1) - *alt_digits);
259 #else
260   const char *const f_wkday = weekday_name[tp->tm_wday];
261   const char *const f_month = month_name[tp->tm_mon];
262   const char *const a_wkday = f_wkday;
263   const char *const a_month = f_month;
264   const char *const ampm = "AMPM" + 2 * (hour12 > 11);
265   size_t aw_len = 3;
266   size_t am_len = 3;
267   size_t ap_len = 2;
268 #endif
269   size_t wkday_len = strlen (f_wkday);
270   size_t month_len = strlen (f_month);
271   const unsigned int y_week0 = week (tp, 0, 7);
272   const unsigned int y_week1 = week (tp, 1, 7);
273   const unsigned int y_week2 = week (tp, 1, 3);
274   const char *zone;
275   size_t zonelen;
276   register size_t i = 0;
277   register char *p = s;
278   register const char *f;
279
280   zone = 0;
281 #if HAVE_TM_ZONE
282   zone = (const char *) tp->tm_zone;
283 #endif
284 #if HAVE_TZNAME
285   if (!(zone && *zone) && tp->tm_isdst >= 0)
286     zone = tzname[tp->tm_isdst];
287 #endif
288   if (!(zone && *zone))
289     zone = "???";
290
291   zonelen = strlen (zone);
292
293   if (hour12 > 12)
294     hour12 -= 12;
295   else
296     if (hour12 == 0) hour12 = 12;
297
298   for (f = format; *f != '\0'; ++f)
299     {
300       enum { pad_zero, pad_space, pad_none } pad; /* Padding for number.  */
301       unsigned int digits;      /* Max digits for numeric format.  */
302       unsigned int number_value; /* Numeric value to be printed.  */
303       int negative_number;      /* 1 if the number is negative.  */
304       const char *subfmt = "";
305       enum { none, alternate, era } modifier;
306       char *bufp;
307       char buf[1 + (sizeof (int) < sizeof (time_t)
308                     ? INT_STRLEN_BOUND (time_t)
309                     : INT_STRLEN_BOUND (int))];
310
311 #if HAVE_MBLEN
312       if (!isascii (*f))
313         {
314           /* Non-ASCII, may be a multibyte.  */
315           int len = mblen (f, strlen (f));
316           if (len > 0)
317             {
318               cpy(len, f);
319               continue;
320             }
321         }
322 #endif
323
324       if (*f != '%')
325         {
326           add (1, *p = *f);
327           continue;
328         }
329
330       /* Check for flags that can modify a number format.  */
331       ++f;
332       switch (*f)
333         {
334         case '_':
335           pad = pad_space;
336           ++f;
337           break;
338         case '-':
339           pad = pad_none;
340           ++f;
341           break;
342         default:
343           pad = pad_zero;
344           break;
345         }
346
347       /* Check for modifiers.  */
348       switch (*f)
349         {
350         case 'E':
351           ++f;
352           modifier = era;
353           break;
354         case 'O':
355           ++f;
356           modifier = alternate;
357           break;
358         default:
359           modifier = none;
360           break;
361         }
362
363       /* Now do the specified format.  */
364       switch (*f)
365         {
366 #define DO_NUMBER(d, v) \
367           digits = d; number_value = v; goto do_number
368 #define DO_NUMBER_SPACEPAD(d, v) \
369           digits = d; number_value = v; goto do_number_spacepad
370
371         case '\0':              /* GNU extension: % at end of format.  */
372             --f;
373             /* Fall through.  */
374         case '%':
375           if (modifier != none)
376             goto bad_format;
377           add (1, *p = *f);
378           break;
379
380         case 'a':
381           if (modifier != none)
382             goto bad_format;
383           cpy (aw_len, a_wkday);
384           break;
385
386         case 'A':
387           if (modifier != none)
388             goto bad_format;
389           cpy (wkday_len, f_wkday);
390           break;
391
392         case 'b':
393         case 'h':               /* GNU extension.  */
394           if (modifier != none)
395             goto bad_format;
396           cpy (am_len, a_month);
397           break;
398
399         case 'B':
400           if (modifier != none)
401             goto bad_format;
402           cpy (month_len, f_month);
403           break;
404
405         case 'c':
406           if (modifier == alternate)
407             goto bad_format;
408 #ifdef _NL_CURRENT
409           if (modifier == era)
410             subfmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
411           if (*subfmt == '\0')
412             subfmt = _NL_CURRENT (LC_TIME, D_T_FMT);
413 #else
414           subfmt = "%a %b %e %H:%M:%S %Z %Y";
415 #endif
416
417         subformat:
418           {
419             size_t len = strftime (p, maxsize - i, subfmt, tp);
420             if (len == 0 && *subfmt)
421               return 0;
422             add (len, ;);
423           }
424           break;
425
426         case 'C':
427           if (modifier == alternate)
428             goto bad_format;
429 #ifdef _NL_CURRENT
430           /* XXX I'm not sure about this.  --drepper@gnu */
431           if (modifier == era &&
432               *(subfmt = _NL_CURRENT (LC_TIME, ERA)) != '\0')
433             goto subformat;
434 #endif
435           DO_NUMBER (2, (1900 + tp->tm_year) / 100);
436
437         case 'x':
438           if (modifier == alternate)
439             goto bad_format;
440 #ifdef _NL_CURRENT
441           if (modifier == era)
442             subfmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
443           if (*subfmt == '\0')
444             subfmt = _NL_CURRENT (LC_TIME, D_FMT);
445           goto subformat;
446 #endif
447           /* Fall through.  */
448         case 'D':               /* GNU extension.  */
449           subfmt = "%m/%d/%y";
450           goto subformat;
451
452         case 'd':
453           if (modifier == era)
454             goto bad_format;
455
456           DO_NUMBER (2, tp->tm_mday);
457
458         case 'e':               /* GNU extension: %d, but blank-padded.  */
459           if (modifier == era)
460             goto bad_format;
461
462           DO_NUMBER_SPACEPAD (2, tp->tm_mday);
463
464           /* All numeric formats set DIGITS and NUMBER_VALUE and then
465              jump to one of these two labels.  */
466
467         do_number_spacepad:
468           /* Force `_' flag.  */
469           pad = pad_space;
470
471         do_number:
472           /* Format the number according to the MODIFIER flag.  */
473
474 #ifdef _NL_CURRENT
475           if (modifier == alternate && 0 <= number_value
476               && number_value < (unsigned int) nr_alt_digits)
477             {
478               /* ALT_DIGITS is the first entry in an array with
479                  alternative digit symbols.  */
480               size_t digitlen = strlen (*(alt_digits + number_value));
481               if (digitlen == 0)
482                 break;
483               cpy (digitlen, *(alt_digits + number_value));
484               goto done_with_number;
485             }
486 #endif
487           {
488             unsigned int u = number_value;
489
490             bufp = buf + sizeof (buf);
491             negative_number = number_value < 0;
492
493             if (negative_number)
494               u = -u;
495
496             do
497               *--bufp = u % 10 + '0';
498             while ((u /= 10) != 0);
499           }
500
501         do_number_sign_and_padding:
502           if (negative_number)
503             *--bufp = '-';
504
505           if (pad != pad_none)
506             {
507               int padding = digits - (buf + sizeof (buf) - bufp);
508
509               if (pad == pad_space)
510                 {
511                   while (0 < padding--)
512                     *--bufp = ' ';
513                 }
514               else
515                 {
516                   bufp += negative_number;
517                   while (0 < padding--)
518                     *--bufp = '0';
519                   if (negative_number)
520                     *--bufp = '-';
521                 }
522             }
523
524           cpy (buf + sizeof (buf) - bufp, bufp);
525
526 #ifdef _NL_CURRENT
527         done_with_number:
528 #endif
529           break;
530
531
532         case 'H':
533           if (modifier == era)
534             goto bad_format;
535
536           DO_NUMBER (2, tp->tm_hour);
537
538         case 'I':
539           if (modifier == era)
540             goto bad_format;
541
542           DO_NUMBER (2, hour12);
543
544         case 'k':               /* GNU extension.  */
545           if (modifier == era)
546             goto bad_format;
547
548           DO_NUMBER_SPACEPAD (2, tp->tm_hour);
549
550         case 'l':               /* GNU extension.  */
551           if (modifier == era)
552             goto bad_format;
553
554           DO_NUMBER_SPACEPAD (2, hour12);
555
556         case 'j':
557           if (modifier == era)
558             goto bad_format;
559
560           DO_NUMBER (3, 1 + tp->tm_yday);
561
562         case 'M':
563           if (modifier == era)
564             goto bad_format;
565
566           DO_NUMBER (2, tp->tm_min);
567
568         case 'm':
569           if (modifier == era)
570             goto bad_format;
571
572           DO_NUMBER (2, tp->tm_mon + 1);
573
574         case 'n':               /* GNU extension.  */
575           add (1, *p = '\n');
576           break;
577
578         case 'p':
579           cpy (ap_len, ampm);
580           break;
581
582         case 'R':               /* GNU extension.  */
583           subfmt = "%H:%M";
584           goto subformat;
585
586         case 'r':               /* GNU extension.  */
587           subfmt = "%I:%M:%S %p";
588           goto subformat;
589
590         case 'S':
591           if (modifier == era)
592             return 0;
593
594           DO_NUMBER (2, tp->tm_sec);
595
596         case 's':               /* GNU extension.  */
597           {
598             struct tm ltm = *tp;
599             time_t t = mktime (&ltm);
600
601             /* Generate string value for T using time_t arithmetic;
602                this works even if sizeof (long) < sizeof (time_t).  */
603
604             bufp = buf + sizeof (buf);
605             negative_number = t < 0;
606
607             do
608               {
609                 int d = t % 10;
610                 t /= 10;
611
612                 if (negative_number)
613                   {
614                     d = -d;
615
616                     /* Adjust if division truncates to minus infinity.  */
617                     if (0 < -1 % 10 && d < 0)
618                       {
619                         t++;
620                         d += 10;
621                       }
622                   }
623
624                 *--bufp = d + '0';
625               }
626             while (t != 0);
627
628             digits = 1;
629             goto do_number_sign_and_padding;
630           }
631
632         case 'X':
633           if (modifier == alternate)
634             goto bad_format;
635 #ifdef _NL_CURRENT
636           if (modifier == era)
637             subfmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
638           if (*subfmt == '\0')
639             subfmt = _NL_CURRENT (LC_TIME, T_FMT);
640           goto subformat;
641 #endif
642           /* Fall through.  */
643         case 'T':               /* GNU extension.  */
644           subfmt = "%H:%M:%S";
645           goto subformat;
646
647         case 't':               /* GNU extension.  */
648           add (1, *p = '\t');
649           break;
650
651         case 'U':
652           if (modifier == era)
653             goto bad_format;
654
655           DO_NUMBER (2, y_week0);
656
657         case 'V':
658           if (modifier == era)
659             goto bad_format;
660
661           DO_NUMBER (2, y_week2);
662
663         case 'W':
664           if (modifier == era)
665             goto bad_format;
666
667           DO_NUMBER (2, y_week1);
668
669         case 'w':
670           if (modifier == era)
671             goto bad_format;
672
673           DO_NUMBER (2, tp->tm_wday);
674
675         case 'Y':
676 #ifdef _NL_CURRENT
677           if (modifier == era
678               && *(subfmt = _NL_CURRENT (LC_TIME, ERA_YEAR)) != '\0')
679             goto subformat;
680           else
681 #endif
682             if (modifier == alternate)
683               goto bad_format;
684             else
685               DO_NUMBER (4, 1900 + tp->tm_year);
686
687         case 'y':
688 #ifdef _NL_CURRENT
689           if (modifier == era
690               && *(subfmt = _NL_CURRENT (LC_TIME, ERA_YEAR)) != '\0')
691             goto subformat;
692 #endif
693           DO_NUMBER (2, tp->tm_year % 100);
694
695         case 'Z':
696           cpy(zonelen, zone);
697           break;
698
699         case 'z':               /* GNU extension.  */
700           if (tp->tm_isdst < 0)
701             break;
702
703           {
704             int diff;
705 #if HAVE_TM_GMTOFF
706             diff = tp->tm_gmtoff;
707 #else
708             struct tm gtm;
709             struct tm ltm = *tp;
710             time_t lt = mktime (&ltm);
711
712             if (lt == (time_t) -1)
713               {
714                 /* mktime returns -1 for errors, but -1 is also a
715                    valid time_t value.  Check whether an error really
716                    occurred.  */
717                 struct tm tm;
718                 localtime_r (&lt, &tm);
719
720                 if ((ltm.tm_sec ^ tm.tm_sec)
721                     | (ltm.tm_min ^ tm.tm_min)
722                     | (ltm.tm_hour ^ tm.tm_hour)
723                     | (ltm.tm_mday ^ tm.tm_mday)
724                     | (ltm.tm_mon ^ tm.tm_mon)
725                     | (ltm.tm_year ^ tm.tm_year))
726                   break;
727               }
728
729             if (! gmtime_r (&lt, &gtm))
730               break;
731
732             diff = tm_diff (&ltm, &gtm);
733 #endif
734
735             if (diff < 0)
736               {
737                 add (1, *p = '-');
738                 diff = -diff;
739               }
740             else
741               add (1, *p = '+');
742
743             pad = pad_zero;
744
745             diff /= 60;
746             DO_NUMBER (4, (diff / 60) * 100 + diff % 60);
747           }
748
749         default:
750           /* Bad format.  */
751         bad_format:
752           if (pad == pad_space)
753             add (1, *p = '_');
754           else if (pad == pad_zero)
755             add (1, *p = '0');
756
757           if (modifier == era)
758             add (1, *p = 'E');
759           else if (modifier == alternate)
760             add (1, *p = 'O');
761
762           add (1, *p = *f);
763           break;
764         }
765     }
766
767   if (p)
768     *p = '\0';
769   return i;
770 }