984ead520c1b8ade15f64f384258382e7bc60190
[platform/upstream/tzdata.git] / strftime.c
1 /* Convert a broken-down time stamp to a string.  */
2
3 /* Copyright 1989 The Regents of the University of California.
4    All rights reserved.
5
6    Redistribution and use in source and binary forms, with or without
7    modification, are permitted provided that the following conditions
8    are met:
9    1. Redistributions of source code must retain the above copyright
10       notice, this list of conditions and the following disclaimer.
11    2. Redistributions in binary form must reproduce the above copyright
12       notice, this list of conditions and the following disclaimer in the
13       documentation and/or other materials provided with the distribution.
14    3. Neither the name of the University nor the names of its contributors
15       may be used to endorse or promote products derived from this software
16       without specific prior written permission.
17
18    THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
19    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21    ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24    OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27    OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28    SUCH DAMAGE.  */
29
30 /*
31 ** Based on the UCB version with the copyright notice appearing above.
32 **
33 ** This is ANSIish only when "multibyte character == plain character".
34 */
35
36 #include "private.h"
37
38 #include "tzfile.h"
39 #include "fcntl.h"
40 #include "locale.h"
41
42 struct lc_time_T {
43         const char *    mon[MONSPERYEAR];
44         const char *    month[MONSPERYEAR];
45         const char *    wday[DAYSPERWEEK];
46         const char *    weekday[DAYSPERWEEK];
47         const char *    X_fmt;
48         const char *    x_fmt;
49         const char *    c_fmt;
50         const char *    am;
51         const char *    pm;
52         const char *    date_fmt;
53 };
54
55 #define Locale  (&C_time_locale)
56
57 static const struct lc_time_T   C_time_locale = {
58         {
59                 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
60                 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
61         }, {
62                 "January", "February", "March", "April", "May", "June",
63                 "July", "August", "September", "October", "November", "December"
64         }, {
65                 "Sun", "Mon", "Tue", "Wed",
66                 "Thu", "Fri", "Sat"
67         }, {
68                 "Sunday", "Monday", "Tuesday", "Wednesday",
69                 "Thursday", "Friday", "Saturday"
70         },
71
72         /* X_fmt */
73         "%H:%M:%S",
74
75         /*
76         ** x_fmt
77         ** C99 requires this format.
78         ** Using just numbers (as here) makes Quakers happier;
79         ** it's also compatible with SVR4.
80         */
81         "%m/%d/%y",
82
83         /*
84         ** c_fmt
85         ** C99 requires this format.
86         ** Previously this code used "%D %X", but we now conform to C99.
87         ** Note that
88         **      "%a %b %d %H:%M:%S %Y"
89         ** is used by Solaris 2.3.
90         */
91         "%a %b %e %T %Y",
92
93         /* am */
94         "AM",
95
96         /* pm */
97         "PM",
98
99         /* date_fmt */
100         "%a %b %e %H:%M:%S %Z %Y"
101 };
102
103 static char *   _add(const char *, char *, const char *);
104 static char *   _conv(int, const char *, char *, const char *);
105 static char *   _fmt(const char *, const struct tm *, char *, const char *,
106                         int *);
107 static char *   _yconv(int, int, bool, bool, char *, char const *);
108
109 #if !HAVE_POSIX_DECLS
110 extern char *   tzname[];
111 #endif
112
113 #ifndef YEAR_2000_NAME
114 #define YEAR_2000_NAME  "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS"
115 #endif /* !defined YEAR_2000_NAME */
116
117 #define IN_NONE 0
118 #define IN_SOME 1
119 #define IN_THIS 2
120 #define IN_ALL  3
121
122 #if HAVE_STRFTIME_L
123 size_t
124 strftime_l(char *s, size_t maxsize, char const *format, struct tm const *t,
125            locale_t locale)
126 {
127   /* Just call strftime, as only the C locale is supported.  */
128   return strftime(s, maxsize, format, t);
129 }
130 #endif
131
132 size_t
133 strftime(char *s, size_t maxsize, const char *format, const struct tm *t)
134 {
135         char *  p;
136         int     warn;
137
138         tzset();
139         warn = IN_NONE;
140         p = _fmt(format, t, s, s + maxsize, &warn);
141 #ifndef NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU
142         if (warn != IN_NONE && getenv(YEAR_2000_NAME) != NULL) {
143                 fprintf(stderr, "\n");
144                 fprintf(stderr, "strftime format \"%s\" ", format);
145                 fprintf(stderr, "yields only two digits of years in ");
146                 if (warn == IN_SOME)
147                         fprintf(stderr, "some locales");
148                 else if (warn == IN_THIS)
149                         fprintf(stderr, "the current locale");
150                 else    fprintf(stderr, "all locales");
151                 fprintf(stderr, "\n");
152         }
153 #endif /* !defined NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU */
154         if (p == s + maxsize)
155                 return 0;
156         *p = '\0';
157         return p - s;
158 }
159
160 static char *
161 _fmt(const char *format, const struct tm *t, char *pt,
162      const char *ptlim, int *warnp)
163 {
164         for ( ; *format; ++format) {
165                 if (*format == '%') {
166 label:
167                         switch (*++format) {
168                         case '\0':
169                                 --format;
170                                 break;
171                         case 'A':
172                                 pt = _add((t->tm_wday < 0 ||
173                                         t->tm_wday >= DAYSPERWEEK) ?
174                                         "?" : Locale->weekday[t->tm_wday],
175                                         pt, ptlim);
176                                 continue;
177                         case 'a':
178                                 pt = _add((t->tm_wday < 0 ||
179                                         t->tm_wday >= DAYSPERWEEK) ?
180                                         "?" : Locale->wday[t->tm_wday],
181                                         pt, ptlim);
182                                 continue;
183                         case 'B':
184                                 pt = _add((t->tm_mon < 0 ||
185                                         t->tm_mon >= MONSPERYEAR) ?
186                                         "?" : Locale->month[t->tm_mon],
187                                         pt, ptlim);
188                                 continue;
189                         case 'b':
190                         case 'h':
191                                 pt = _add((t->tm_mon < 0 ||
192                                         t->tm_mon >= MONSPERYEAR) ?
193                                         "?" : Locale->mon[t->tm_mon],
194                                         pt, ptlim);
195                                 continue;
196                         case 'C':
197                                 /*
198                                 ** %C used to do a...
199                                 **      _fmt("%a %b %e %X %Y", t);
200                                 ** ...whereas now POSIX 1003.2 calls for
201                                 ** something completely different.
202                                 ** (ado, 1993-05-24)
203                                 */
204                                 pt = _yconv(t->tm_year, TM_YEAR_BASE,
205                                             true, false, pt, ptlim);
206                                 continue;
207                         case 'c':
208                                 {
209                                 int warn2 = IN_SOME;
210
211                                 pt = _fmt(Locale->c_fmt, t, pt, ptlim, &warn2);
212                                 if (warn2 == IN_ALL)
213                                         warn2 = IN_THIS;
214                                 if (warn2 > *warnp)
215                                         *warnp = warn2;
216                                 }
217                                 continue;
218                         case 'D':
219                                 pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp);
220                                 continue;
221                         case 'd':
222                                 pt = _conv(t->tm_mday, "%02d", pt, ptlim);
223                                 continue;
224                         case 'E':
225                         case 'O':
226                                 /*
227                                 ** C99 locale modifiers.
228                                 ** The sequences
229                                 **      %Ec %EC %Ex %EX %Ey %EY
230                                 **      %Od %oe %OH %OI %Om %OM
231                                 **      %OS %Ou %OU %OV %Ow %OW %Oy
232                                 ** are supposed to provide alternate
233                                 ** representations.
234                                 */
235                                 goto label;
236                         case 'e':
237                                 pt = _conv(t->tm_mday, "%2d", pt, ptlim);
238                                 continue;
239                         case 'F':
240                                 pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp);
241                                 continue;
242                         case 'H':
243                                 pt = _conv(t->tm_hour, "%02d", pt, ptlim);
244                                 continue;
245                         case 'I':
246                                 pt = _conv((t->tm_hour % 12) ?
247                                         (t->tm_hour % 12) : 12,
248                                         "%02d", pt, ptlim);
249                                 continue;
250                         case 'j':
251                                 pt = _conv(t->tm_yday + 1, "%03d", pt, ptlim);
252                                 continue;
253                         case 'k':
254                                 /*
255                                 ** This used to be...
256                                 **      _conv(t->tm_hour % 12 ?
257                                 **              t->tm_hour % 12 : 12, 2, ' ');
258                                 ** ...and has been changed to the below to
259                                 ** match SunOS 4.1.1 and Arnold Robbins'
260                                 ** strftime version 3.0. That is, "%k" and
261                                 ** "%l" have been swapped.
262                                 ** (ado, 1993-05-24)
263                                 */
264                                 pt = _conv(t->tm_hour, "%2d", pt, ptlim);
265                                 continue;
266 #ifdef KITCHEN_SINK
267                         case 'K':
268                                 /*
269                                 ** After all this time, still unclaimed!
270                                 */
271                                 pt = _add("kitchen sink", pt, ptlim);
272                                 continue;
273 #endif /* defined KITCHEN_SINK */
274                         case 'l':
275                                 /*
276                                 ** This used to be...
277                                 **      _conv(t->tm_hour, 2, ' ');
278                                 ** ...and has been changed to the below to
279                                 ** match SunOS 4.1.1 and Arnold Robbin's
280                                 ** strftime version 3.0. That is, "%k" and
281                                 ** "%l" have been swapped.
282                                 ** (ado, 1993-05-24)
283                                 */
284                                 pt = _conv((t->tm_hour % 12) ?
285                                         (t->tm_hour % 12) : 12,
286                                         "%2d", pt, ptlim);
287                                 continue;
288                         case 'M':
289                                 pt = _conv(t->tm_min, "%02d", pt, ptlim);
290                                 continue;
291                         case 'm':
292                                 pt = _conv(t->tm_mon + 1, "%02d", pt, ptlim);
293                                 continue;
294                         case 'n':
295                                 pt = _add("\n", pt, ptlim);
296                                 continue;
297                         case 'p':
298                                 pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ?
299                                         Locale->pm :
300                                         Locale->am,
301                                         pt, ptlim);
302                                 continue;
303                         case 'R':
304                                 pt = _fmt("%H:%M", t, pt, ptlim, warnp);
305                                 continue;
306                         case 'r':
307                                 pt = _fmt("%I:%M:%S %p", t, pt, ptlim, warnp);
308                                 continue;
309                         case 'S':
310                                 pt = _conv(t->tm_sec, "%02d", pt, ptlim);
311                                 continue;
312                         case 's':
313                                 {
314                                         struct tm       tm;
315                                         char            buf[INT_STRLEN_MAXIMUM(
316                                                                 time_t) + 1];
317                                         time_t          mkt;
318
319                                         tm = *t;
320                                         mkt = mktime(&tm);
321                                         if (TYPE_SIGNED(time_t))
322                                                 sprintf(buf, "%"PRIdMAX,
323                                                         (intmax_t) mkt);
324                                         else    sprintf(buf, "%"PRIuMAX,
325                                                         (uintmax_t) mkt);
326                                         pt = _add(buf, pt, ptlim);
327                                 }
328                                 continue;
329                         case 'T':
330                                 pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp);
331                                 continue;
332                         case 't':
333                                 pt = _add("\t", pt, ptlim);
334                                 continue;
335                         case 'U':
336                                 pt = _conv((t->tm_yday + DAYSPERWEEK -
337                                         t->tm_wday) / DAYSPERWEEK,
338                                         "%02d", pt, ptlim);
339                                 continue;
340                         case 'u':
341                                 /*
342                                 ** From Arnold Robbins' strftime version 3.0:
343                                 ** "ISO 8601: Weekday as a decimal number
344                                 ** [1 (Monday) - 7]"
345                                 ** (ado, 1993-05-24)
346                                 */
347                                 pt = _conv((t->tm_wday == 0) ?
348                                         DAYSPERWEEK : t->tm_wday,
349                                         "%d", pt, ptlim);
350                                 continue;
351                         case 'V':       /* ISO 8601 week number */
352                         case 'G':       /* ISO 8601 year (four digits) */
353                         case 'g':       /* ISO 8601 year (two digits) */
354 /*
355 ** From Arnold Robbins' strftime version 3.0: "the week number of the
356 ** year (the first Monday as the first day of week 1) as a decimal number
357 ** (01-53)."
358 ** (ado, 1993-05-24)
359 **
360 ** From <http://www.ft.uni-erlangen.de/~mskuhn/iso-time.html> by Markus Kuhn:
361 ** "Week 01 of a year is per definition the first week which has the
362 ** Thursday in this year, which is equivalent to the week which contains
363 ** the fourth day of January. In other words, the first week of a new year
364 ** is the week which has the majority of its days in the new year. Week 01
365 ** might also contain days from the previous year and the week before week
366 ** 01 of a year is the last week (52 or 53) of the previous year even if
367 ** it contains days from the new year. A week starts with Monday (day 1)
368 ** and ends with Sunday (day 7). For example, the first week of the year
369 ** 1997 lasts from 1996-12-30 to 1997-01-05..."
370 ** (ado, 1996-01-02)
371 */
372                                 {
373                                         int     year;
374                                         int     base;
375                                         int     yday;
376                                         int     wday;
377                                         int     w;
378
379                                         year = t->tm_year;
380                                         base = TM_YEAR_BASE;
381                                         yday = t->tm_yday;
382                                         wday = t->tm_wday;
383                                         for ( ; ; ) {
384                                                 int     len;
385                                                 int     bot;
386                                                 int     top;
387
388                                                 len = isleap_sum(year, base) ?
389                                                         DAYSPERLYEAR :
390                                                         DAYSPERNYEAR;
391                                                 /*
392                                                 ** What yday (-3 ... 3) does
393                                                 ** the ISO year begin on?
394                                                 */
395                                                 bot = ((yday + 11 - wday) %
396                                                         DAYSPERWEEK) - 3;
397                                                 /*
398                                                 ** What yday does the NEXT
399                                                 ** ISO year begin on?
400                                                 */
401                                                 top = bot -
402                                                         (len % DAYSPERWEEK);
403                                                 if (top < -3)
404                                                         top += DAYSPERWEEK;
405                                                 top += len;
406                                                 if (yday >= top) {
407                                                         ++base;
408                                                         w = 1;
409                                                         break;
410                                                 }
411                                                 if (yday >= bot) {
412                                                         w = 1 + ((yday - bot) /
413                                                                 DAYSPERWEEK);
414                                                         break;
415                                                 }
416                                                 --base;
417                                                 yday += isleap_sum(year, base) ?
418                                                         DAYSPERLYEAR :
419                                                         DAYSPERNYEAR;
420                                         }
421 #ifdef XPG4_1994_04_09
422                                         if ((w == 52 &&
423                                                 t->tm_mon == TM_JANUARY) ||
424                                                 (w == 1 &&
425                                                 t->tm_mon == TM_DECEMBER))
426                                                         w = 53;
427 #endif /* defined XPG4_1994_04_09 */
428                                         if (*format == 'V')
429                                                 pt = _conv(w, "%02d",
430                                                         pt, ptlim);
431                                         else if (*format == 'g') {
432                                                 *warnp = IN_ALL;
433                                                 pt = _yconv(year, base,
434                                                         false, true,
435                                                         pt, ptlim);
436                                         } else  pt = _yconv(year, base,
437                                                         true, true,
438                                                         pt, ptlim);
439                                 }
440                                 continue;
441                         case 'v':
442                                 /*
443                                 ** From Arnold Robbins' strftime version 3.0:
444                                 ** "date as dd-bbb-YYYY"
445                                 ** (ado, 1993-05-24)
446                                 */
447                                 pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp);
448                                 continue;
449                         case 'W':
450                                 pt = _conv((t->tm_yday + DAYSPERWEEK -
451                                         (t->tm_wday ?
452                                         (t->tm_wday - 1) :
453                                         (DAYSPERWEEK - 1))) / DAYSPERWEEK,
454                                         "%02d", pt, ptlim);
455                                 continue;
456                         case 'w':
457                                 pt = _conv(t->tm_wday, "%d", pt, ptlim);
458                                 continue;
459                         case 'X':
460                                 pt = _fmt(Locale->X_fmt, t, pt, ptlim, warnp);
461                                 continue;
462                         case 'x':
463                                 {
464                                 int     warn2 = IN_SOME;
465
466                                 pt = _fmt(Locale->x_fmt, t, pt, ptlim, &warn2);
467                                 if (warn2 == IN_ALL)
468                                         warn2 = IN_THIS;
469                                 if (warn2 > *warnp)
470                                         *warnp = warn2;
471                                 }
472                                 continue;
473                         case 'y':
474                                 *warnp = IN_ALL;
475                                 pt = _yconv(t->tm_year, TM_YEAR_BASE,
476                                         false, true,
477                                         pt, ptlim);
478                                 continue;
479                         case 'Y':
480                                 pt = _yconv(t->tm_year, TM_YEAR_BASE,
481                                         true, true,
482                                         pt, ptlim);
483                                 continue;
484                         case 'Z':
485 #ifdef TM_ZONE
486                                 pt = _add(t->TM_ZONE, pt, ptlim);
487 #else
488                                 if (t->tm_isdst >= 0)
489                                         pt = _add(tzname[t->tm_isdst != 0],
490                                                 pt, ptlim);
491 #endif
492                                 /*
493                                 ** C99 says that %Z must be replaced by the
494                                 ** empty string if the time zone is not
495                                 ** determinable.
496                                 */
497                                 continue;
498                         case 'z':
499 #if defined TM_GMTOFF || defined USG_COMPAT || defined ALTZONE
500                                 {
501                                 long            diff;
502                                 char const *    sign;
503
504 # ifdef TM_GMTOFF
505                                 diff = t->TM_GMTOFF;
506 # else
507                                 /*
508                                 ** C99 says that the UT offset must
509                                 ** be computed by looking only at
510                                 ** tm_isdst. This requirement is
511                                 ** incorrect, since it means the code
512                                 ** must rely on magic (in this case
513                                 ** altzone and timezone), and the
514                                 ** magic might not have the correct
515                                 ** offset. Doing things correctly is
516                                 ** tricky and requires disobeying C99;
517                                 ** see GNU C strftime for details.
518                                 ** For now, punt and conform to the
519                                 ** standard, even though it's incorrect.
520                                 **
521                                 ** C99 says that %z must be replaced by the
522                                 ** empty string if the time zone is not
523                                 ** determinable, so output nothing if the
524                                 ** appropriate variables are not available.
525                                 */
526                                 if (t->tm_isdst < 0)
527                                         continue;
528                                 if (t->tm_isdst == 0)
529 #  ifdef USG_COMPAT
530                                         diff = -timezone;
531 #  else
532                                         continue;
533 #  endif
534                                 else
535 #  ifdef ALTZONE
536                                         diff = -altzone;
537 #  else
538                                         continue;
539 #  endif
540 # endif
541                                 if (diff < 0) {
542                                         sign = "-";
543                                         diff = -diff;
544                                 } else  sign = "+";
545                                 pt = _add(sign, pt, ptlim);
546                                 diff /= SECSPERMIN;
547                                 diff = (diff / MINSPERHOUR) * 100 +
548                                         (diff % MINSPERHOUR);
549                                 pt = _conv(diff, "%04d", pt, ptlim);
550                                 }
551 #endif
552                                 continue;
553                         case '+':
554                                 pt = _fmt(Locale->date_fmt, t, pt, ptlim,
555                                         warnp);
556                                 continue;
557                         case '%':
558                         /*
559                         ** X311J/88-090 (4.12.3.5): if conversion char is
560                         ** undefined, behavior is undefined. Print out the
561                         ** character itself as printf(3) also does.
562                         */
563                         default:
564                                 break;
565                         }
566                 }
567                 if (pt == ptlim)
568                         break;
569                 *pt++ = *format;
570         }
571         return pt;
572 }
573
574 static char *
575 _conv(int n, const char *format, char *pt, const char *ptlim)
576 {
577         char    buf[INT_STRLEN_MAXIMUM(int) + 1];
578
579         sprintf(buf, format, n);
580         return _add(buf, pt, ptlim);
581 }
582
583 static char *
584 _add(const char *str, char *pt, const char *ptlim)
585 {
586         while (pt < ptlim && (*pt = *str++) != '\0')
587                 ++pt;
588         return pt;
589 }
590
591 /*
592 ** POSIX and the C Standard are unclear or inconsistent about
593 ** what %C and %y do if the year is negative or exceeds 9999.
594 ** Use the convention that %C concatenated with %y yields the
595 ** same output as %Y, and that %Y contains at least 4 bytes,
596 ** with more only if necessary.
597 */
598
599 static char *
600 _yconv(int a, int b, bool convert_top, bool convert_yy,
601        char *pt, const char *ptlim)
602 {
603         register int    lead;
604         register int    trail;
605
606 #define DIVISOR 100
607         trail = a % DIVISOR + b % DIVISOR;
608         lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR;
609         trail %= DIVISOR;
610         if (trail < 0 && lead > 0) {
611                 trail += DIVISOR;
612                 --lead;
613         } else if (lead < 0 && trail > 0) {
614                 trail -= DIVISOR;
615                 ++lead;
616         }
617         if (convert_top) {
618                 if (lead == 0 && trail < 0)
619                         pt = _add("-0", pt, ptlim);
620                 else    pt = _conv(lead, "%02d", pt, ptlim);
621         }
622         if (convert_yy)
623                 pt = _conv(((trail < 0) ? -trail : trail), "%02d", pt, ptlim);
624         return pt;
625 }