• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *   http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <langinfo.h>
20 #include <locale.h>
21 #include <time.h>
22 #include <limits.h>
23 #include "locale_impl.h"
24 #include "time_impl.h"
25 
26 #define __YEAR_BASE__ 1900
27 #define __WEEKS_IN_YEAR__ 52
28 #define __WEEKS_IN_YEAR2__ 53
29 #define __DAY_LAST_WEEK__ 4
30 #define __DAY_LAST_WEEK2__ 3
31 #define __YEARS_PER_CENTURY__ 100
32 #define __DAYS_IN_YEAR__ 360
33 #define __LEN_YEAR__ 4
34 #define __HALF_HOUR__ 12
35 #define __LEN_DAY__ 3
36 
is_leap(int y)37 static int is_leap(int y)
38 {
39     /* Avoid overflow */
40     if (y > INT_MAX - __YEAR_BASE__) {
41         y -= 2000;
42     }
43     y += __YEAR_BASE__;
44     return !(y%4) && ((y%100) || !(y%400));
45 }
46 
week_num(const struct tm * tm)47 static int week_num(const struct tm *tm)
48 {
49     int val = (tm->tm_yday + 7U - (tm->tm_wday+6U)%7) / 7;
50     /* If 1 Jan is just 1-3 days past Monday,
51      * the previous week is also in this year. */
52     if ((tm->tm_wday + 371U - tm->tm_yday - 2) % 7 <= 2)
53         val++;
54     if (!val) {
55         val = __WEEKS_IN_YEAR__;
56         /* If 31 December of prev year a Thursday,
57          * or Friday of a leap year, then the
58          * prev year has 53 weeks. */
59         int dec31 = (tm->tm_wday + 7U - tm->tm_yday - 1) % 7;
60         if (dec31 == 4 || (dec31 == 5 && is_leap(tm->tm_year%400-1)))
61             val++;
62     } else if (val == __WEEKS_IN_YEAR2__) {
63         /* If 1 January is not a Thursday, and not
64          * a Wednesday of a leap year, then this
65          * year has only 52 weeks. */
66         int jan1 = (tm->tm_wday + 371U - tm->tm_yday) % 7;
67         if (jan1 != __DAY_LAST_WEEK__ && (jan1 != 3 || !is_leap(tm->tm_year)))
68             val = 1;
69     }
70     return val;
71 }
72 
__strftime_fmt_1(char (* s)[100],size_t * l,int f,const struct tm * tm,locale_t loc,int pad)73 const char *__strftime_fmt_1(char (*s)[100], size_t *l, int f, const struct tm *tm, locale_t loc, int pad)
74 {
75     nl_item item;
76     long long val;
77     const char *fmt = "-";
78     int width = 2, def_pad = '0';
79 
80     switch (f) {
81         case 'a':
82             if (tm->tm_wday > 6U) goto string;
83             item = ABDAY_1 + tm->tm_wday;
84             goto nl_strcat;
85         case 'A':
86             if (tm->tm_wday > 6U) goto string;
87             item = DAY_1 + tm->tm_wday;
88             goto nl_strcat;
89         case 'h':
90         case 'b':
91             if (tm->tm_mon > 11U) {
92                 goto string;
93             }
94             item = ABMON_1 + tm->tm_mon;
95             goto nl_strcat;
96         case 'B':
97             if (tm->tm_mon > 11U) {
98                 goto string;
99             }
100             item = MON_1 + tm->tm_mon;
101             goto nl_strcat;
102         case 'c':
103             item = D_T_FMT;
104             goto nl_strftime;
105         case 'C':
106             val = (1900LL+tm->tm_year) / __YEARS_PER_CENTURY__;
107             goto number;
108         case 'e':
109             def_pad = '_';
110         case 'd':
111             val = tm->tm_mday;
112             goto number;
113         case 'D':
114             fmt = "%m/%d/%y";
115             goto recu_strftime;
116         case 'F':
117             fmt = "%Y-%m-%d";
118             goto recu_strftime;
119         case 'g':
120         case 'G':
121             val = tm->tm_year + 1900LL;
122             if (tm->tm_yday < __DAY_LAST_WEEK2__ && week_num(tm) != 1) {
123                 val--;
124             } else if (tm->tm_yday > __DAYS_IN_YEAR__ && week_num(tm) == 1) {
125                 val++;
126             }
127             if (f=='g') {
128                 val %= __YEARS_PER_CENTURY__;
129             } else {
130                 width = __LEN_YEAR__;
131             }
132             goto number;
133         case 'H':
134             val = tm->tm_hour;
135             goto number;
136         case 'I':
137             val = tm->tm_hour;
138             if (!val) {
139                 val = __HALF_HOUR__;
140             } else if (val > __HALF_HOUR__) {
141                 val -= __HALF_HOUR__;
142             }
143             goto number;
144         case 'j':
145             val = tm->tm_yday+1;
146             width = __LEN_DAY__;
147             goto number;
148         case 'k':
149             def_pad = '_';
150             val = tm->tm_hour;
151             goto number;
152         case 'l':
153             def_pad = '_';
154             val = tm->tm_hour;
155             if (!val) {
156                 val = __HALF_HOUR__;
157             } else if (val > __HALF_HOUR__) {
158                 val -= __HALF_HOUR__;
159             }
160             goto number;
161         case 'm':
162             val = tm->tm_mon+1;
163             goto number;
164         case 'M':
165             val = tm->tm_min;
166             goto number;
167         case 'n':
168             *l = 1;
169             return "\n";
170         case 'p':
171             item = tm->tm_hour >= __HALF_HOUR__ ? PM_STR : AM_STR;
172             goto nl_strcat;
173         case 'P':
174             item = tm->tm_hour >= __HALF_HOUR__ ? PM_STR_LOWER : AM_STR_LOWER;
175             goto nl_strcat;
176         case 'r':
177             item = T_FMT_AMPM;
178             goto nl_strftime;
179         case 'R':
180             fmt = "%H:%M";
181             goto recu_strftime;
182         case 's':
183             val = __tm_to_secs(tm) - tm->__tm_gmtoff;
184             width = 1;
185             goto number;
186         case 'S':
187             val = tm->tm_sec;
188             goto number;
189         case 't':
190             *l = 1;
191             return "\t";
192         case 'T':
193             fmt = "%H:%M:%S";
194             goto recu_strftime;
195         case 'u':
196             val = tm->tm_wday ? tm->tm_wday : 7;
197             width = 1;
198             goto number;
199         case 'U':
200             val = (tm->tm_yday + 7U - tm->tm_wday) / 7;
201             goto number;
202         case 'W':
203             val = (tm->tm_yday + 7U - (tm->tm_wday+6U)%7) / 7;
204             goto number;
205         case 'V':
206             val = week_num(tm);
207             goto number;
208         case 'v':
209             fmt = "%e-%b-%Y";
210             goto recu_strftime;
211         case 'w':
212             val = tm->tm_wday;
213             width = 1;
214             goto number;
215         case 'x':
216             item = D_FMT;
217             goto nl_strftime;
218         case 'X':
219             item = T_FMT;
220             goto nl_strftime;
221         case 'y':
222             val = (tm->tm_year + 1900LL) % 100;
223             if (val < 0) {
224                 val = -val;
225             }
226             goto number;
227         case 'Y':
228             val = tm->tm_year + 1900LL;
229             if (val >= 10000) {
230                 *l = snprintf(*s, sizeof *s, "+%lld", val);
231                 return *s;
232             }
233             width = __LEN_YEAR__;
234             goto number;
235         case 'z':
236             if (tm->tm_isdst < 0) {
237                 *l = 0;
238                 return "";
239             }
240             *l = snprintf(*s, sizeof *s, "%+.4ld",
241                 tm->__tm_gmtoff/3600*100 + tm->__tm_gmtoff%3600/60);
242             return *s;
243         case 'Z':
244             if (tm->tm_isdst < 0) {
245                 *l = 0;
246                 return "";
247             }
248             fmt = __tm_to_tzname(tm);
249             goto string;
250         case '%':
251             *l = 1;
252             return "%";
253         default:
254             return 0;
255     }
256 number:
257     switch (pad ? pad : def_pad) {
258         case '-':
259             *l = snprintf(*s, sizeof *s, "%lld", val);
260             break;
261         case '_':
262             *l = snprintf(*s, sizeof *s, "%*lld", width, val);
263             break;
264         case '0':
265         default:
266             *l = snprintf(*s, sizeof *s, "%0*lld", width, val);
267             break;
268     }
269     return *s;
270 nl_strcat:
271     fmt = __nl_langinfo_l(item, loc);
272 string:
273     *l = strlen(fmt);
274     return fmt;
275 nl_strftime:
276     fmt = __nl_langinfo_l(item, loc);
277 recu_strftime:
278     *l = __strftime_l(*s, sizeof *s, fmt, tm, loc);
279     if (!*l) {
280         return 0;
281     }
282     return *s;
283 }
284 
__strftime_l(char * restrict s,size_t n,const char * restrict f,const struct tm * restrict tm,locale_t loc)285 size_t __strftime_l(char *restrict s, size_t n, const char *restrict f, const struct tm *restrict tm, locale_t loc)
286 {
287     size_t l, k;
288     char buf[100];
289     char *p;
290     const char *t;
291     int pad, plus;
292     unsigned long width;
293     for (l=0; l<n; f++) {
294         if (!*f) {
295             s[l] = 0;
296             return l;
297         }
298         if (*f != '%') {
299             s[l++] = *f;
300             continue;
301         }
302         f++;
303         pad = 0;
304         if (*f == '-' || *f == '_' || *f == '0') {
305             pad = *f++;
306         }
307         if ((plus = (*f == '+'))) {
308             f++;
309         }
310         width = strtoul(f, &p, 10);
311         if (*p == 'C' || *p == 'F' || *p == 'G' || *p == 'Y') {
312             if (!width && p!=f) {
313                 width = 1;
314             }
315         } else {
316             width = 0;
317         }
318         f = p;
319         if (*f == 'E' || *f == 'O') {
320             f++;
321         }
322         t = __strftime_fmt_1(&buf, &k, *f, tm, loc, pad);
323         if (!t) {
324             break;
325         }
326         if (width) {
327             /* Trim off any sign and leading zeros, then
328              * count remaining digits to determine behavior
329              * for the + flag. */
330             if (*t=='+' || *t=='-') {
331                 t++, k--;
332             }
333             for (; *t=='0' && t[1]-'0'<10U; t++, k--) {}
334             if (width < k) {
335                 width = k;
336             }
337             size_t d;
338             for (d=0; t[d]-'0'<10U; d++) {}
339             if (tm->tm_year < -1900) {
340                 s[l++] = '-';
341                 width--;
342             } else if (plus && d+(width-k) >= (*p=='C'?3:5)) {
343                 s[l++] = '+';
344                 width--;
345             }
346             for (; width > k && l < n; width--) {
347                 s[l++] = '0';
348             }
349         }
350         if (k > n-l) {
351             k = n-l;
352         }
353         memcpy(s+l, t, k);
354         l += k;
355     }
356     if (n) {
357         if (l==n) {
358             l=n-1;
359         }
360         s[l] = 0;
361     }
362     return 0;
363 }
364 
strftime(char * restrict s,size_t n,const char * restrict f,const struct tm * restrict tm)365 size_t strftime(char *restrict s, size_t n, const char *restrict f, const struct tm *restrict tm)
366 {
367     return __strftime_l(s, n, f, tm, CURRENT_LOCALE);
368 }
369 
370 weak_alias(__strftime_l, strftime_l);