1570af302Sopenharmony_ci/**
2570af302Sopenharmony_ci * Copyright (c) 2022 Huawei Device Co., Ltd.
3570af302Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
4570af302Sopenharmony_ci * you may not use this file except in compliance with the License.
5570af302Sopenharmony_ci * You may obtain a copy of the License at
6570af302Sopenharmony_ci *
7570af302Sopenharmony_ci *   http://www.apache.org/licenses/LICENSE-2.0
8570af302Sopenharmony_ci *
9570af302Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
10570af302Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
11570af302Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12570af302Sopenharmony_ci * See the License for the specific language governing permissions and
13570af302Sopenharmony_ci * limitations under the License.
14570af302Sopenharmony_ci */
15570af302Sopenharmony_ci
16570af302Sopenharmony_ci#include <stdio.h>
17570af302Sopenharmony_ci#include <stdlib.h>
18570af302Sopenharmony_ci#include <string.h>
19570af302Sopenharmony_ci#include <langinfo.h>
20570af302Sopenharmony_ci#include <locale.h>
21570af302Sopenharmony_ci#include <time.h>
22570af302Sopenharmony_ci#include <limits.h>
23570af302Sopenharmony_ci#include "locale_impl.h"
24570af302Sopenharmony_ci#include "time_impl.h"
25570af302Sopenharmony_ci
26570af302Sopenharmony_ci#define __YEAR_BASE__ 1900
27570af302Sopenharmony_ci#define __WEEKS_IN_YEAR__ 52
28570af302Sopenharmony_ci#define __WEEKS_IN_YEAR2__ 53
29570af302Sopenharmony_ci#define __DAY_LAST_WEEK__ 4
30570af302Sopenharmony_ci#define __DAY_LAST_WEEK2__ 3
31570af302Sopenharmony_ci#define __YEARS_PER_CENTURY__ 100
32570af302Sopenharmony_ci#define __DAYS_IN_YEAR__ 360
33570af302Sopenharmony_ci#define __LEN_YEAR__ 4
34570af302Sopenharmony_ci#define __HALF_HOUR__ 12
35570af302Sopenharmony_ci#define __LEN_DAY__ 3
36570af302Sopenharmony_ci
37570af302Sopenharmony_cistatic int is_leap(int y)
38570af302Sopenharmony_ci{
39570af302Sopenharmony_ci    /* Avoid overflow */
40570af302Sopenharmony_ci    if (y > INT_MAX - __YEAR_BASE__) {
41570af302Sopenharmony_ci        y -= 2000;
42570af302Sopenharmony_ci    }
43570af302Sopenharmony_ci    y += __YEAR_BASE__;
44570af302Sopenharmony_ci    return !(y%4) && ((y%100) || !(y%400));
45570af302Sopenharmony_ci}
46570af302Sopenharmony_ci
47570af302Sopenharmony_cistatic int week_num(const struct tm *tm)
48570af302Sopenharmony_ci{
49570af302Sopenharmony_ci    int val = (tm->tm_yday + 7U - (tm->tm_wday+6U)%7) / 7;
50570af302Sopenharmony_ci    /* If 1 Jan is just 1-3 days past Monday,
51570af302Sopenharmony_ci     * the previous week is also in this year. */
52570af302Sopenharmony_ci    if ((tm->tm_wday + 371U - tm->tm_yday - 2) % 7 <= 2)
53570af302Sopenharmony_ci        val++;
54570af302Sopenharmony_ci    if (!val) {
55570af302Sopenharmony_ci        val = __WEEKS_IN_YEAR__;
56570af302Sopenharmony_ci        /* If 31 December of prev year a Thursday,
57570af302Sopenharmony_ci         * or Friday of a leap year, then the
58570af302Sopenharmony_ci         * prev year has 53 weeks. */
59570af302Sopenharmony_ci        int dec31 = (tm->tm_wday + 7U - tm->tm_yday - 1) % 7;
60570af302Sopenharmony_ci        if (dec31 == 4 || (dec31 == 5 && is_leap(tm->tm_year%400-1)))
61570af302Sopenharmony_ci            val++;
62570af302Sopenharmony_ci    } else if (val == __WEEKS_IN_YEAR2__) {
63570af302Sopenharmony_ci        /* If 1 January is not a Thursday, and not
64570af302Sopenharmony_ci         * a Wednesday of a leap year, then this
65570af302Sopenharmony_ci         * year has only 52 weeks. */
66570af302Sopenharmony_ci        int jan1 = (tm->tm_wday + 371U - tm->tm_yday) % 7;
67570af302Sopenharmony_ci        if (jan1 != __DAY_LAST_WEEK__ && (jan1 != 3 || !is_leap(tm->tm_year)))
68570af302Sopenharmony_ci            val = 1;
69570af302Sopenharmony_ci    }
70570af302Sopenharmony_ci    return val;
71570af302Sopenharmony_ci}
72570af302Sopenharmony_ci
73570af302Sopenharmony_ciconst char *__strftime_fmt_1(char (*s)[100], size_t *l, int f, const struct tm *tm, locale_t loc, int pad)
74570af302Sopenharmony_ci{
75570af302Sopenharmony_ci    nl_item item;
76570af302Sopenharmony_ci    long long val;
77570af302Sopenharmony_ci    const char *fmt = "-";
78570af302Sopenharmony_ci    int width = 2, def_pad = '0';
79570af302Sopenharmony_ci
80570af302Sopenharmony_ci    switch (f) {
81570af302Sopenharmony_ci        case 'a':
82570af302Sopenharmony_ci            if (tm->tm_wday > 6U) goto string;
83570af302Sopenharmony_ci            item = ABDAY_1 + tm->tm_wday;
84570af302Sopenharmony_ci            goto nl_strcat;
85570af302Sopenharmony_ci        case 'A':
86570af302Sopenharmony_ci            if (tm->tm_wday > 6U) goto string;
87570af302Sopenharmony_ci            item = DAY_1 + tm->tm_wday;
88570af302Sopenharmony_ci            goto nl_strcat;
89570af302Sopenharmony_ci        case 'h':
90570af302Sopenharmony_ci        case 'b':
91570af302Sopenharmony_ci            if (tm->tm_mon > 11U) {
92570af302Sopenharmony_ci                goto string;
93570af302Sopenharmony_ci            }
94570af302Sopenharmony_ci            item = ABMON_1 + tm->tm_mon;
95570af302Sopenharmony_ci            goto nl_strcat;
96570af302Sopenharmony_ci        case 'B':
97570af302Sopenharmony_ci            if (tm->tm_mon > 11U) {
98570af302Sopenharmony_ci                goto string;
99570af302Sopenharmony_ci            }
100570af302Sopenharmony_ci            item = MON_1 + tm->tm_mon;
101570af302Sopenharmony_ci            goto nl_strcat;
102570af302Sopenharmony_ci        case 'c':
103570af302Sopenharmony_ci            item = D_T_FMT;
104570af302Sopenharmony_ci            goto nl_strftime;
105570af302Sopenharmony_ci        case 'C':
106570af302Sopenharmony_ci            val = (1900LL+tm->tm_year) / __YEARS_PER_CENTURY__;
107570af302Sopenharmony_ci            goto number;
108570af302Sopenharmony_ci        case 'e':
109570af302Sopenharmony_ci            def_pad = '_';
110570af302Sopenharmony_ci        case 'd':
111570af302Sopenharmony_ci            val = tm->tm_mday;
112570af302Sopenharmony_ci            goto number;
113570af302Sopenharmony_ci        case 'D':
114570af302Sopenharmony_ci            fmt = "%m/%d/%y";
115570af302Sopenharmony_ci            goto recu_strftime;
116570af302Sopenharmony_ci        case 'F':
117570af302Sopenharmony_ci            fmt = "%Y-%m-%d";
118570af302Sopenharmony_ci            goto recu_strftime;
119570af302Sopenharmony_ci        case 'g':
120570af302Sopenharmony_ci        case 'G':
121570af302Sopenharmony_ci            val = tm->tm_year + 1900LL;
122570af302Sopenharmony_ci            if (tm->tm_yday < __DAY_LAST_WEEK2__ && week_num(tm) != 1) {
123570af302Sopenharmony_ci                val--;
124570af302Sopenharmony_ci            } else if (tm->tm_yday > __DAYS_IN_YEAR__ && week_num(tm) == 1) {
125570af302Sopenharmony_ci                val++;
126570af302Sopenharmony_ci            }
127570af302Sopenharmony_ci            if (f=='g') {
128570af302Sopenharmony_ci                val %= __YEARS_PER_CENTURY__;
129570af302Sopenharmony_ci            } else {
130570af302Sopenharmony_ci                width = __LEN_YEAR__;
131570af302Sopenharmony_ci            }
132570af302Sopenharmony_ci            goto number;
133570af302Sopenharmony_ci        case 'H':
134570af302Sopenharmony_ci            val = tm->tm_hour;
135570af302Sopenharmony_ci            goto number;
136570af302Sopenharmony_ci        case 'I':
137570af302Sopenharmony_ci            val = tm->tm_hour;
138570af302Sopenharmony_ci            if (!val) {
139570af302Sopenharmony_ci                val = __HALF_HOUR__;
140570af302Sopenharmony_ci            } else if (val > __HALF_HOUR__) {
141570af302Sopenharmony_ci                val -= __HALF_HOUR__;
142570af302Sopenharmony_ci            }
143570af302Sopenharmony_ci            goto number;
144570af302Sopenharmony_ci        case 'j':
145570af302Sopenharmony_ci            val = tm->tm_yday+1;
146570af302Sopenharmony_ci            width = __LEN_DAY__;
147570af302Sopenharmony_ci            goto number;
148570af302Sopenharmony_ci        case 'k':
149570af302Sopenharmony_ci            def_pad = '_';
150570af302Sopenharmony_ci            val = tm->tm_hour;
151570af302Sopenharmony_ci            goto number;
152570af302Sopenharmony_ci        case 'l':
153570af302Sopenharmony_ci            def_pad = '_';
154570af302Sopenharmony_ci            val = tm->tm_hour;
155570af302Sopenharmony_ci            if (!val) {
156570af302Sopenharmony_ci                val = __HALF_HOUR__;
157570af302Sopenharmony_ci            } else if (val > __HALF_HOUR__) {
158570af302Sopenharmony_ci                val -= __HALF_HOUR__;
159570af302Sopenharmony_ci            }
160570af302Sopenharmony_ci            goto number;
161570af302Sopenharmony_ci        case 'm':
162570af302Sopenharmony_ci            val = tm->tm_mon+1;
163570af302Sopenharmony_ci            goto number;
164570af302Sopenharmony_ci        case 'M':
165570af302Sopenharmony_ci            val = tm->tm_min;
166570af302Sopenharmony_ci            goto number;
167570af302Sopenharmony_ci        case 'n':
168570af302Sopenharmony_ci            *l = 1;
169570af302Sopenharmony_ci            return "\n";
170570af302Sopenharmony_ci        case 'p':
171570af302Sopenharmony_ci            item = tm->tm_hour >= __HALF_HOUR__ ? PM_STR : AM_STR;
172570af302Sopenharmony_ci            goto nl_strcat;
173570af302Sopenharmony_ci        case 'P':
174570af302Sopenharmony_ci            item = tm->tm_hour >= __HALF_HOUR__ ? PM_STR_LOWER : AM_STR_LOWER;
175570af302Sopenharmony_ci            goto nl_strcat;
176570af302Sopenharmony_ci        case 'r':
177570af302Sopenharmony_ci            item = T_FMT_AMPM;
178570af302Sopenharmony_ci            goto nl_strftime;
179570af302Sopenharmony_ci        case 'R':
180570af302Sopenharmony_ci            fmt = "%H:%M";
181570af302Sopenharmony_ci            goto recu_strftime;
182570af302Sopenharmony_ci        case 's':
183570af302Sopenharmony_ci            val = __tm_to_secs(tm) - tm->__tm_gmtoff;
184570af302Sopenharmony_ci            width = 1;
185570af302Sopenharmony_ci            goto number;
186570af302Sopenharmony_ci        case 'S':
187570af302Sopenharmony_ci            val = tm->tm_sec;
188570af302Sopenharmony_ci            goto number;
189570af302Sopenharmony_ci        case 't':
190570af302Sopenharmony_ci            *l = 1;
191570af302Sopenharmony_ci            return "\t";
192570af302Sopenharmony_ci        case 'T':
193570af302Sopenharmony_ci            fmt = "%H:%M:%S";
194570af302Sopenharmony_ci            goto recu_strftime;
195570af302Sopenharmony_ci        case 'u':
196570af302Sopenharmony_ci            val = tm->tm_wday ? tm->tm_wday : 7;
197570af302Sopenharmony_ci            width = 1;
198570af302Sopenharmony_ci            goto number;
199570af302Sopenharmony_ci        case 'U':
200570af302Sopenharmony_ci            val = (tm->tm_yday + 7U - tm->tm_wday) / 7;
201570af302Sopenharmony_ci            goto number;
202570af302Sopenharmony_ci        case 'W':
203570af302Sopenharmony_ci            val = (tm->tm_yday + 7U - (tm->tm_wday+6U)%7) / 7;
204570af302Sopenharmony_ci            goto number;
205570af302Sopenharmony_ci        case 'V':
206570af302Sopenharmony_ci            val = week_num(tm);
207570af302Sopenharmony_ci            goto number;
208570af302Sopenharmony_ci        case 'v':
209570af302Sopenharmony_ci            fmt = "%e-%b-%Y";
210570af302Sopenharmony_ci            goto recu_strftime;
211570af302Sopenharmony_ci        case 'w':
212570af302Sopenharmony_ci            val = tm->tm_wday;
213570af302Sopenharmony_ci            width = 1;
214570af302Sopenharmony_ci            goto number;
215570af302Sopenharmony_ci        case 'x':
216570af302Sopenharmony_ci            item = D_FMT;
217570af302Sopenharmony_ci            goto nl_strftime;
218570af302Sopenharmony_ci        case 'X':
219570af302Sopenharmony_ci            item = T_FMT;
220570af302Sopenharmony_ci            goto nl_strftime;
221570af302Sopenharmony_ci        case 'y':
222570af302Sopenharmony_ci            val = (tm->tm_year + 1900LL) % 100;
223570af302Sopenharmony_ci            if (val < 0) {
224570af302Sopenharmony_ci                val = -val;
225570af302Sopenharmony_ci            }
226570af302Sopenharmony_ci            goto number;
227570af302Sopenharmony_ci        case 'Y':
228570af302Sopenharmony_ci            val = tm->tm_year + 1900LL;
229570af302Sopenharmony_ci            if (val >= 10000) {
230570af302Sopenharmony_ci                *l = snprintf(*s, sizeof *s, "+%lld", val);
231570af302Sopenharmony_ci                return *s;
232570af302Sopenharmony_ci            }
233570af302Sopenharmony_ci            width = __LEN_YEAR__;
234570af302Sopenharmony_ci            goto number;
235570af302Sopenharmony_ci        case 'z':
236570af302Sopenharmony_ci            if (tm->tm_isdst < 0) {
237570af302Sopenharmony_ci                *l = 0;
238570af302Sopenharmony_ci                return "";
239570af302Sopenharmony_ci            }
240570af302Sopenharmony_ci            *l = snprintf(*s, sizeof *s, "%+.4ld",
241570af302Sopenharmony_ci                tm->__tm_gmtoff/3600*100 + tm->__tm_gmtoff%3600/60);
242570af302Sopenharmony_ci            return *s;
243570af302Sopenharmony_ci        case 'Z':
244570af302Sopenharmony_ci            if (tm->tm_isdst < 0) {
245570af302Sopenharmony_ci                *l = 0;
246570af302Sopenharmony_ci                return "";
247570af302Sopenharmony_ci            }
248570af302Sopenharmony_ci            fmt = __tm_to_tzname(tm);
249570af302Sopenharmony_ci            goto string;
250570af302Sopenharmony_ci        case '%':
251570af302Sopenharmony_ci            *l = 1;
252570af302Sopenharmony_ci            return "%";
253570af302Sopenharmony_ci        default:
254570af302Sopenharmony_ci            return 0;
255570af302Sopenharmony_ci    }
256570af302Sopenharmony_cinumber:
257570af302Sopenharmony_ci    switch (pad ? pad : def_pad) {
258570af302Sopenharmony_ci        case '-':
259570af302Sopenharmony_ci            *l = snprintf(*s, sizeof *s, "%lld", val);
260570af302Sopenharmony_ci            break;
261570af302Sopenharmony_ci        case '_':
262570af302Sopenharmony_ci            *l = snprintf(*s, sizeof *s, "%*lld", width, val);
263570af302Sopenharmony_ci            break;
264570af302Sopenharmony_ci        case '0':
265570af302Sopenharmony_ci        default:
266570af302Sopenharmony_ci            *l = snprintf(*s, sizeof *s, "%0*lld", width, val);
267570af302Sopenharmony_ci            break;
268570af302Sopenharmony_ci    }
269570af302Sopenharmony_ci    return *s;
270570af302Sopenharmony_cinl_strcat:
271570af302Sopenharmony_ci    fmt = __nl_langinfo_l(item, loc);
272570af302Sopenharmony_cistring:
273570af302Sopenharmony_ci    *l = strlen(fmt);
274570af302Sopenharmony_ci    return fmt;
275570af302Sopenharmony_cinl_strftime:
276570af302Sopenharmony_ci    fmt = __nl_langinfo_l(item, loc);
277570af302Sopenharmony_cirecu_strftime:
278570af302Sopenharmony_ci    *l = __strftime_l(*s, sizeof *s, fmt, tm, loc);
279570af302Sopenharmony_ci    if (!*l) {
280570af302Sopenharmony_ci        return 0;
281570af302Sopenharmony_ci    }
282570af302Sopenharmony_ci    return *s;
283570af302Sopenharmony_ci}
284570af302Sopenharmony_ci
285570af302Sopenharmony_cisize_t __strftime_l(char *restrict s, size_t n, const char *restrict f, const struct tm *restrict tm, locale_t loc)
286570af302Sopenharmony_ci{
287570af302Sopenharmony_ci    size_t l, k;
288570af302Sopenharmony_ci    char buf[100];
289570af302Sopenharmony_ci    char *p;
290570af302Sopenharmony_ci    const char *t;
291570af302Sopenharmony_ci    int pad, plus;
292570af302Sopenharmony_ci    unsigned long width;
293570af302Sopenharmony_ci    for (l=0; l<n; f++) {
294570af302Sopenharmony_ci        if (!*f) {
295570af302Sopenharmony_ci            s[l] = 0;
296570af302Sopenharmony_ci            return l;
297570af302Sopenharmony_ci        }
298570af302Sopenharmony_ci        if (*f != '%') {
299570af302Sopenharmony_ci            s[l++] = *f;
300570af302Sopenharmony_ci            continue;
301570af302Sopenharmony_ci        }
302570af302Sopenharmony_ci        f++;
303570af302Sopenharmony_ci        pad = 0;
304570af302Sopenharmony_ci        if (*f == '-' || *f == '_' || *f == '0') {
305570af302Sopenharmony_ci            pad = *f++;
306570af302Sopenharmony_ci        }
307570af302Sopenharmony_ci        if ((plus = (*f == '+'))) {
308570af302Sopenharmony_ci            f++;
309570af302Sopenharmony_ci        }
310570af302Sopenharmony_ci        width = strtoul(f, &p, 10);
311570af302Sopenharmony_ci        if (*p == 'C' || *p == 'F' || *p == 'G' || *p == 'Y') {
312570af302Sopenharmony_ci            if (!width && p!=f) {
313570af302Sopenharmony_ci                width = 1;
314570af302Sopenharmony_ci            }
315570af302Sopenharmony_ci        } else {
316570af302Sopenharmony_ci            width = 0;
317570af302Sopenharmony_ci        }
318570af302Sopenharmony_ci        f = p;
319570af302Sopenharmony_ci        if (*f == 'E' || *f == 'O') {
320570af302Sopenharmony_ci            f++;
321570af302Sopenharmony_ci        }
322570af302Sopenharmony_ci        t = __strftime_fmt_1(&buf, &k, *f, tm, loc, pad);
323570af302Sopenharmony_ci        if (!t) {
324570af302Sopenharmony_ci            break;
325570af302Sopenharmony_ci        }
326570af302Sopenharmony_ci        if (width) {
327570af302Sopenharmony_ci            /* Trim off any sign and leading zeros, then
328570af302Sopenharmony_ci             * count remaining digits to determine behavior
329570af302Sopenharmony_ci             * for the + flag. */
330570af302Sopenharmony_ci            if (*t=='+' || *t=='-') {
331570af302Sopenharmony_ci                t++, k--;
332570af302Sopenharmony_ci            }
333570af302Sopenharmony_ci            for (; *t=='0' && t[1]-'0'<10U; t++, k--) {}
334570af302Sopenharmony_ci            if (width < k) {
335570af302Sopenharmony_ci                width = k;
336570af302Sopenharmony_ci            }
337570af302Sopenharmony_ci            size_t d;
338570af302Sopenharmony_ci            for (d=0; t[d]-'0'<10U; d++) {}
339570af302Sopenharmony_ci            if (tm->tm_year < -1900) {
340570af302Sopenharmony_ci                s[l++] = '-';
341570af302Sopenharmony_ci                width--;
342570af302Sopenharmony_ci            } else if (plus && d+(width-k) >= (*p=='C'?3:5)) {
343570af302Sopenharmony_ci                s[l++] = '+';
344570af302Sopenharmony_ci                width--;
345570af302Sopenharmony_ci            }
346570af302Sopenharmony_ci            for (; width > k && l < n; width--) {
347570af302Sopenharmony_ci                s[l++] = '0';
348570af302Sopenharmony_ci            }
349570af302Sopenharmony_ci        }
350570af302Sopenharmony_ci        if (k > n-l) {
351570af302Sopenharmony_ci            k = n-l;
352570af302Sopenharmony_ci        }
353570af302Sopenharmony_ci        memcpy(s+l, t, k);
354570af302Sopenharmony_ci        l += k;
355570af302Sopenharmony_ci    }
356570af302Sopenharmony_ci    if (n) {
357570af302Sopenharmony_ci        if (l==n) {
358570af302Sopenharmony_ci            l=n-1;
359570af302Sopenharmony_ci        }
360570af302Sopenharmony_ci        s[l] = 0;
361570af302Sopenharmony_ci    }
362570af302Sopenharmony_ci    return 0;
363570af302Sopenharmony_ci}
364570af302Sopenharmony_ci
365570af302Sopenharmony_cisize_t strftime(char *restrict s, size_t n, const char *restrict f, const struct tm *restrict tm)
366570af302Sopenharmony_ci{
367570af302Sopenharmony_ci    return __strftime_l(s, n, f, tm, CURRENT_LOCALE);
368570af302Sopenharmony_ci}
369570af302Sopenharmony_ci
370570af302Sopenharmony_ciweak_alias(__strftime_l, strftime_l);