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