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);