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