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