__time_to_tm: initialize tm_zone and tm_gmtoff
[musl] / src / time / strptime.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <langinfo.h>
4 #include <time.h>
5 #include <ctype.h>
6 #include <stddef.h>
7 #include <string.h>
8 #include <strings.h>
9
10 char *strptime(const char *restrict s, const char *restrict f, struct tm *restrict tm)
11 {
12         int i, w, neg, adj, min, range, *dest;
13         const char *ex;
14         size_t len;
15         while (*f) {
16                 if (*f != '%') {
17                         if (isspace(*f)) for (; *s && isspace(*s); s++);
18                         else if (*s != *f) return 0;
19                         else s++;
20                         f++;
21                         continue;
22                 }
23                 f++;
24                 if (*f == '+') f++;
25                 if (isdigit(*f)) w=strtoul(f, (void *)&f, 10);
26                 else w=-1;
27                 adj=0;
28                 switch (*f++) {
29                 case 'a': case 'A':
30                         dest = &tm->tm_wday;
31                         min = ABDAY_1;
32                         range = 7;
33                         goto symbolic_range;
34                 case 'b': case 'B': case 'h':
35                         dest = &tm->tm_mon;
36                         min = ABMON_1;
37                         range = 12;
38                         goto symbolic_range;
39                 case 'c':
40                         s = strptime(s, nl_langinfo(D_T_FMT), tm);
41                         if (!s) return 0;
42                         break;
43                 case 'C':
44                 case 'd': case 'e':
45                         dest = &tm->tm_mday;
46                         min = 1;
47                         range = 31;
48                         goto numeric_range;
49                 case 'D':
50                         s = strptime(s, "%m/%d/%y", tm);
51                         if (!s) return 0;
52                         break;
53                 case 'H':
54                         dest = &tm->tm_hour;
55                         min = 0;
56                         range = 24;
57                         goto numeric_range;
58                 case 'I':
59                         dest = &tm->tm_hour;
60                         min = 1;
61                         range = 12;
62                         goto numeric_range;
63                 case 'j':
64                         dest = &tm->tm_yday;
65                         min = 1;
66                         range = 366;
67                         goto numeric_range;
68                 case 'm':
69                         dest = &tm->tm_mon;
70                         min = 1;
71                         range = 12;
72                         adj = 1;
73                         goto numeric_range;
74                 case 'M':
75                         dest = &tm->tm_min;
76                         min = 0;
77                         range = 60;
78                         goto numeric_range;
79                 case 'n': case 't':
80                         for (; *s && isspace(*s); s++);
81                         break;
82                 case 'p':
83                         ex = nl_langinfo(AM_STR);
84                         len = strlen(ex);
85                         if (!strncasecmp(s, ex, len)) {
86                                 tm->tm_hour %= 12;
87                                 break;
88                         }
89                         ex = nl_langinfo(PM_STR);
90                         len = strlen(ex);
91                         if (!strncasecmp(s, ex, len)) {
92                                 tm->tm_hour %= 12;
93                                 tm->tm_hour += 12;
94                                 break;
95                         }
96                         return 0;
97                 case 'r':
98                         s = strptime(s, nl_langinfo(T_FMT_AMPM), tm);
99                         if (!s) return 0;
100                         break;
101                 case 'R':
102                         s = strptime(s, "%H:%M", tm);
103                         if (!s) return 0;
104                         break;
105                 case 'S':
106                         dest = &tm->tm_sec;
107                         min = 0;
108                         range = 61;
109                         goto numeric_range;
110                 case 'T':
111                         s = strptime(s, "%H:%M:%S", tm);
112                         if (!s) return 0;
113                         break;
114                 case 'U':
115                 case 'W':
116                         //FIXME
117                         return 0;
118                 case 'w':
119                         dest = &tm->tm_wday;
120                         min = 0;
121                         range = 7;
122                         goto numeric_range;
123                 case 'x':
124                         s = strptime(s, nl_langinfo(D_FMT), tm);
125                         if (!s) return 0;
126                         break;
127                 case 'X':
128                         s = strptime(s, nl_langinfo(T_FMT), tm);
129                         if (!s) return 0;
130                         break;
131                 case 'y':
132                         //FIXME
133                         return 0;
134                 case 'Y':
135                         dest = &tm->tm_year;
136                         if (w<0) w=4;
137                         adj = 1900;
138                         goto numeric_digits;
139                 case '%':
140                         if (*s++ != '%') return 0;
141                         break;
142                 numeric_range:
143                         if (!isdigit(*s)) return 0;
144                         *dest = 0;
145                         for (i=1; i<=min+range && isdigit(*s); i*=10)
146                                 *dest = *dest * 10 + *s++ - '0';
147                         if (*dest - min >= (unsigned)range) return 0;
148                         *dest -= adj;
149                         switch((char *)dest - (char *)tm) {
150                         case offsetof(struct tm, tm_yday):
151                                 ;
152                         }
153                         goto update;
154                 numeric_digits:
155                         neg = 0;
156                         if (*s == '+') s++;
157                         else if (*s == '-') neg=1, s++;
158                         if (!isdigit(*s)) return 0;
159                         for (*dest=i=0; i<w && isdigit(*s); i++)
160                                 *dest = *dest * 10 + *s++ - '0';
161                         if (neg) *dest = -*dest;
162                         *dest -= adj;
163                         goto update;
164                 symbolic_range:
165                         for (i=2*range-1; i>=0; i--) {
166                                 ex = nl_langinfo(min+i);
167                                 len = strlen(ex);
168                                 if (strncasecmp(s, ex, len)) continue;
169                                 s += len;
170                                 *dest = i % range;
171                                 break;
172                         }
173                         if (i<0) return 0;
174                         goto update;
175                 update:
176                         //FIXME
177                         ;
178                 }
179         }
180         return (char *)s;
181 }