• 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 <stdlib.h>
17 #include <langinfo.h>
18 #include <time.h>
19 #include <ctype.h>
20 #include <stddef.h>
21 #include <string.h>
22 #include <strings.h>
23 /* seconds per hour */
24 #define __STRPTIME_SECOND_IN_HOUR 3600
25 /* character-to-number base */
26 #define __STRPTIME_NUMBER_BASE 10
27 /* epoch time */
28 #define __STRPTIME_EPOCH 1900
29 /* receive data buffer size */
30 #define __STRPTIME_BUFFER_SIZE 16
31 /* Width of hours and minutes when formatting %z */
32 #define __STRPTIME_ZONE_WIDTH 2
33 /* time base */
34 #define __STRPTIME_TIME_BASE 60
35 /* number of weeks per year */
36 #define __STRPTIME_WEEKS_IN_YEAR 53
37 /* days of the week */
38 #define __STRPTIME_DAYS_IN_WEEK 7
39 /* 12 hour clock */
40 #define __STRPTIME_HOUR_CLOCK_12 12
41 /* 24 hour clock */
42 #define __STRPTIME_HOUR_CLOCK_24 24
43 /* days per year */
44 #define __STRPTIME_DAYS_PER_YEAR 366
45 /* Years in each century */
46 #define __STRPTIME_YEARS_PER_CENTURY 100
47 
__getzonename(const char * restrict s,struct tm * restrict tm)48 int __getzonename(const char *restrict s, struct tm *restrict tm)
49 {
50     const char *p = s;
51     struct tm old;
52     memcpy(&old, tm, sizeof(struct tm));
53     /* Possible time zone names like +XXX or -XXX */
54     if (*p == '+' || *p == '-') {
55         p++;
56     }
57 
58     /* The time zone name is adjacent to the offset second data,
59      * and the following symbol belongs to the offset second */
60     while (*p && (*p != '+' && *p != '-' && *p != ' ')) {
61         p++;
62     }
63 
64     /* In the structure struct tm, tm_zone is declared as const char * type, so use static */
65     static char buf[__STRPTIME_BUFFER_SIZE] = {0};
66     memset(buf, 0x0, sizeof(buf));
67     int len = p - s;
68     memcpy(buf, s, len);
69     tm->__tm_zone = buf;
70 
71     /* Re-fetch local data, extract tm_isdst flag. */
72     time_t t = mktime(&old);
73     struct tm *tmp = localtime(&t);
74     if (tmp) {
75         tm->tm_isdst = tmp->tm_isdst;
76     }
77     return len;
78 }
79 
__getgmtoff(const char * restrict s,struct tm * restrict tm)80 int __getgmtoff(const char *restrict s, struct tm *restrict tm)
81 {
82     const char *p = s;
83     int sign = 1;
84     int i;
85     int isexit = 0;
86     long m = 0;
87     long h = 0;
88 
89     /* The possible formats for time offset are HHMM(-HHMM) or HH:MM(-HH:MM) */
90     if (*p == '-') {
91         sign = -1;
92     }
93     p++;
94     tm->__tm_gmtoff = 0;
95 
96     /* get hours */
97     for (i=0; i<__STRPTIME_ZONE_WIDTH && *p; i++, p++) {
98         if (isdigit(*p)) {
99             h = h * __STRPTIME_NUMBER_BASE + (*p - 0x30);
100         } else {
101             p--;
102             isexit = 1;
103             break;
104         }
105     }
106 
107     if (!isexit) {
108         /* Possible time zone formats are HH:MM. */
109         if (*p == ':') {
110             *p++;
111         }
112 
113         /* get minutes */
114         for (i=0; i<__STRPTIME_ZONE_WIDTH && *p; i++, p++) {
115             if (isdigit(*p)) {
116                 m = m * __STRPTIME_NUMBER_BASE + (*p - 0x30);
117             } else {
118                 p--;
119                 isexit = 1;
120                 break;
121             }
122         }
123     }
124 
125     /* Convert hours and minutes to seconds */
126     tm->__tm_gmtoff = sign * (h * __STRPTIME_SECOND_IN_HOUR + m * __STRPTIME_TIME_BASE);
127 
128     return p - s;
129 }
130 
strptime(const char * restrict s,const char * restrict f,struct tm * restrict tm)131 char *strptime(const char *restrict s, const char *restrict f, struct tm *restrict tm)
132 {
133     int i, w, neg, adj, min, range, *dest, dummy;
134     const char *ex;
135     size_t len;
136     int want_century = 0, century = 0, relyear = 0;
137     while (*f) {
138         if (*f != '%') {
139             if (isspace(*f)) {
140                 for (; *s && isspace(*s); s++);
141             } else if (*s != *f) {
142                 return 0;
143             } else {
144                 s++;
145             }
146             f++;
147             continue;
148         }
149         f++;
150         if (*f == '+') {
151             f++;
152         }
153         if (isdigit(*f)) {
154             char *new_f;
155             w=strtoul(f, &new_f, __STRPTIME_NUMBER_BASE);
156             f = new_f;
157         } else {
158             w=-1;
159         }
160         adj=0;
161         switch (*f++) {
162             case 'a': case 'A':
163                 dest = &tm->tm_wday;
164                 min = ABDAY_1;
165                 range = __STRPTIME_DAYS_IN_WEEK;
166                 goto symbolic_range;
167             case 'b': case 'B': case 'h':
168                 dest = &tm->tm_mon;
169                 min = ABMON_1;
170                 range = __STRPTIME_HOUR_CLOCK_12;
171                 goto symbolic_range;
172             case 'c':
173                 s = strptime(s, nl_langinfo(D_T_FMT), tm);
174                 if (!s) {
175                     return 0;
176                 }
177                 break;
178             case 'C':
179                 dest = &century;
180                 if (w<0) {
181                     w=__STRPTIME_ZONE_WIDTH;
182                 }
183                 want_century |= __STRPTIME_ZONE_WIDTH;
184                 goto numeric_digits;
185             case 'd': case 'e':
186                 dest = &tm->tm_mday;
187                 min = 1;
188                 range = 31;
189                 goto numeric_range;
190             case 'D':
191                 s = strptime(s, "%m/%d/%y", tm);
192                 if (!s) {
193                     return 0;
194                 }
195                 break;
196             case 'F':
197                 s = strptime(s, "%Y-%m-%d", tm);
198                 if (!s) {
199                     return 0;
200                 }
201                 break;
202             case 'g':
203                 dest = &tm->tm_year;
204                 min = 0;
205                 range = 99;
206                 w = __STRPTIME_ZONE_WIDTH;
207                 want_century = 0;
208                 goto numeric_digits;
209             case 'G':
210                 do {
211                     ++s;
212                 } while (isdigit(*s));
213                 continue;
214             case 'k':
215             case 'H':
216                 dest = &tm->tm_hour;
217                 min = 0;
218                 range = __STRPTIME_HOUR_CLOCK_24;
219                 goto numeric_range;
220             case 'l':
221             case 'I':
222                 dest = &tm->tm_hour;
223                 min = 1;
224                 range = __STRPTIME_HOUR_CLOCK_12;
225                 goto numeric_range;
226             case 'j':
227                 dest = &tm->tm_yday;
228                 min = 1;
229                 range = __STRPTIME_DAYS_PER_YEAR;
230                 adj = 1;
231                 goto numeric_range;
232             case 'm':
233                 dest = &tm->tm_mon;
234                 min = 1;
235                 range = __STRPTIME_HOUR_CLOCK_12;
236                 adj = 1;
237                 goto numeric_range;
238             case 'M':
239                 dest = &tm->tm_min;
240                 min = 0;
241                 range = __STRPTIME_TIME_BASE;
242                 goto numeric_range;
243             case 'n': case 't':
244                 for (; *s && isspace(*s); s++) {}
245                 break;
246             case 'p':
247             case 'P':
248                 ex = nl_langinfo(AM_STR);
249                 len = strlen(ex);
250                 if (!strncasecmp(s, ex, len)) {
251                     tm->tm_hour %= __STRPTIME_HOUR_CLOCK_12;
252                     s += len;
253                     break;
254                 }
255                 ex = nl_langinfo(PM_STR);
256                 len = strlen(ex);
257                 if (!strncasecmp(s, ex, len)) {
258                     tm->tm_hour %= __STRPTIME_HOUR_CLOCK_12;
259                     tm->tm_hour += __STRPTIME_HOUR_CLOCK_12;
260                     s += len;
261                     break;
262                 }
263                 return 0;
264             case 'r':
265                 s = strptime(s, nl_langinfo(T_FMT_AMPM), tm);
266                 if (!s) {
267                     return 0;
268                 }
269                 break;
270             case 'R':
271                 s = strptime(s, "%H:%M", tm);
272                 if (!s) {
273                     return 0;
274                 }
275                 break;
276             case 's': {
277                 time_t secs = 0;
278                 if (!isdigit(*s)) {
279                     return 0;
280                 }
281                 do {
282                     secs *= __STRPTIME_NUMBER_BASE;
283                     secs += *s - '0';
284                     s++;
285                 } while (isdigit(*s));
286                 if (localtime_r(&secs, tm) == NULL) {
287                     return 0;
288                 }
289                 break;
290             }
291             case 'S':
292                 dest = &tm->tm_sec;
293                 min = 0;
294                 range = 61;
295                 goto numeric_range;
296             case 'T':
297                 s = strptime(s, "%H:%M:%S", tm);
298                 if (!s) {
299                     return 0;
300                 }
301                 break;
302             case 'u': {
303                 if (!isdigit(*s)) {
304                     return 0;
305                 }
306                 int wday = 0;
307                 int rulim = __STRPTIME_DAYS_IN_WEEK;
308                 do {
309                     wday *= __STRPTIME_NUMBER_BASE;
310                     wday += *s++ - '0';
311                     rulim /= __STRPTIME_NUMBER_BASE;
312                 } while ((wday * __STRPTIME_NUMBER_BASE < __STRPTIME_DAYS_IN_WEEK) && rulim && isdigit(*s));
313                 if (wday < 1 || wday > __STRPTIME_DAYS_IN_WEEK) {
314                     return 0;
315                 }
316                 tm->tm_wday = wday % __STRPTIME_DAYS_IN_WEEK;
317                 continue;
318             }
319             case 'U':
320             case 'W':
321                 dest = &dummy;
322                 min = 0;
323                 range = __STRPTIME_WEEKS_IN_YEAR + 1;
324                 goto numeric_range;
325             case 'w':
326                 dest = &tm->tm_wday;
327                 min = 0;
328                 range = __STRPTIME_DAYS_IN_WEEK;
329                 goto numeric_range;
330             case 'v':
331                 if (!(s = strptime(s, "%e-%b-%Y", tm))) {
332                     return 0;
333                 }
334                 break;
335             case 'V': {
336                 int r = 0;
337                 int rulim = __STRPTIME_WEEKS_IN_YEAR;
338                 if (!isdigit(*s)) {
339                     return 0;
340                 }
341                 do {
342                     r *= __STRPTIME_NUMBER_BASE;
343                     r += *s++ - '0';
344                     rulim /= __STRPTIME_NUMBER_BASE;
345                 } while ((r * __STRPTIME_NUMBER_BASE < __STRPTIME_WEEKS_IN_YEAR) && rulim && isdigit(*s));
346                 if (r < 0 || r > __STRPTIME_WEEKS_IN_YEAR) {
347                     return 0;
348                 }
349                 continue;
350             }
351             case 'x':
352                 s = strptime(s, nl_langinfo(D_FMT), tm);
353                 if (!s) {
354                     return 0;
355                 }
356                 break;
357             case 'X':
358                 s = strptime(s, nl_langinfo(T_FMT), tm);
359                 if (!s) {
360                     return 0;
361                 }
362                 break;
363             case 'y':
364                 dest = &relyear;
365                 w = __STRPTIME_ZONE_WIDTH;
366                 want_century |= 1;
367                 goto numeric_digits;
368             case 'Y':
369                 dest = &tm->tm_year;
370                 if (w<0) {
371                     w=4;
372                 }
373                 adj = __STRPTIME_EPOCH;
374                 want_century = 0;
375                 goto numeric_digits;
376             case 'Z':
377                 tzset();
378                 s += __getzonename((const char *)s, tm);
379                 continue;
380             case 'z':
381                 s += __getgmtoff((const char *)s, tm);
382                 continue;
383             case '%':
384                 if (*s++ != '%') {
385                     return 0;
386                 }
387                 break;
388             default:
389                 return 0;
390                 numeric_range:
391                 if (!isdigit(*s)) {
392                     return 0;
393                 }
394                 *dest = 0;
395                 for (i=1; i<=min+range && isdigit(*s); i*=__STRPTIME_NUMBER_BASE) {
396                     *dest = *dest * __STRPTIME_NUMBER_BASE + *s++ - '0';
397                 }
398                 if (*dest - min >= (unsigned)range) {
399                     return 0;
400                 }
401                 *dest -= adj;
402                 switch ((char *)dest - (char *)tm) {
403                     case offsetof(struct tm, tm_yday):
404                         ;
405                 }
406                 goto update;
407                 numeric_digits:
408                 neg = 0;
409                 if (*s == '+') {
410                     s++;
411                 } else if (*s == '-') {
412                     neg=1, s++;
413                 }
414                 if (!isdigit(*s)) {
415                     return 0;
416                 }
417                 for (*dest=i=0; i<w && isdigit(*s); i++) {
418                     *dest = *dest * __STRPTIME_NUMBER_BASE + *s++ - '0';
419                 }
420                 if (neg) {
421                     *dest = -*dest;
422                 }
423                 *dest -= adj;
424                 goto update;
425                 symbolic_range:
426                 for (i=__STRPTIME_ZONE_WIDTH*range-1; i>=0; i--) {
427                     ex = nl_langinfo(min+i);
428                     len = strlen(ex);
429                     if (strncasecmp(s, ex, len)) continue;
430                     s += len;
431                     *dest = i % range;
432                     break;
433                 }
434                 if (i<0) {
435                     return 0;
436                 }
437                 goto update;
438             update:
439                 ;
440             }
441         }
442     if (want_century) {
443         tm->tm_year = relyear;
444         if (want_century & __STRPTIME_ZONE_WIDTH) {
445             tm->tm_year += century * __STRPTIME_YEARS_PER_CENTURY - __STRPTIME_EPOCH;
446         }
447         else if (tm->tm_year <= 68) tm->tm_year += __STRPTIME_YEARS_PER_CENTURY;
448     }
449     return (char *)s;
450 }