1 /* $OpenBSD: strptime.c,v 1.11 2005/08/08 08:05:38 espie Exp $ */
2 /* $NetBSD: strptime.c,v 1.12 1998/01/20 21:39:40 mycroft Exp $ */
3
4 /*-
5 * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc.
6 * All rights reserved.
7 *
8 * This code was contributed to The NetBSD Foundation by Klaus Klein.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 //#include <sys/localedef.h>
40 #include <ctype.h>
41 #include <errno.h>
42 #include <locale.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <time.h>
46 #include "tzfile.h"
47
48 static const struct {
49 const char *abday[7];
50 const char *day[7];
51 const char *abmon[12];
52 const char *mon[12];
53 const char *am_pm[2];
54 const char *d_t_fmt;
55 const char *d_fmt;
56 const char *t_fmt;
57 const char *t_fmt_ampm;
58 } _DefaultTimeLocale = {
59 {
60 "Sun","Mon","Tue","Wed","Thu","Fri","Sat",
61 },
62 {
63 "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
64 "Friday", "Saturday"
65 },
66 {
67 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
68 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
69 },
70 {
71 "January", "February", "March", "April", "May", "June", "July",
72 "August", "September", "October", "November", "December"
73 },
74 {
75 "AM", "PM"
76 },
77 "%a %b %d %H:%M:%S %Y",
78 "%m/%d/%y",
79 "%H:%M:%S",
80 "%I:%M:%S %p"
81 };
82
83 #define _ctloc(x) (_DefaultTimeLocale.x)
84
85 /*
86 * We do not implement alternate representations. However, we always
87 * check whether a given modifier is allowed for a certain conversion.
88 */
89 #define _ALT_E 0x01
90 #define _ALT_O 0x02
91 #define _LEGAL_ALT(x) { if (alt_format & ~(x)) return (0); }
92
93
94 struct century_relyear {
95 int century;
96 int relyear;
97 };
98 static int _conv_num(const unsigned char **, int *, int, int);
99 static unsigned char *_strptime(const unsigned char *, const char *, struct tm *,
100 struct century_relyear *);
101
102
103 char *
strptime(const char * buf,const char * fmt,struct tm * tm)104 strptime(const char *buf, const char *fmt, struct tm *tm)
105 {
106 struct century_relyear cr;
107 cr.century = TM_YEAR_BASE;
108 cr.relyear = -1;
109 return (char*)(_strptime((const unsigned char*)buf, fmt, tm, &cr));
110 }
111
112 static unsigned char *
_strptime(const unsigned char * buf,const char * fmt,struct tm * tm,struct century_relyear * cr)113 _strptime(const unsigned char *buf, const char *fmt, struct tm *tm, struct century_relyear *cr)
114 {
115 unsigned char c;
116 const unsigned char *bp;
117 size_t len = 0;
118 int alt_format, i;
119
120 bp = (unsigned char *)buf;
121 while ((c = *fmt) != '\0') {
122 /* Clear `alternate' modifier prior to new conversion. */
123 alt_format = 0;
124
125 /* Eat up white-space. */
126 if (isspace(c)) {
127 while (isspace(*bp))
128 bp++;
129
130 fmt++;
131 continue;
132 }
133
134 if ((c = *fmt++) != '%')
135 goto literal;
136
137
138 again: switch (c = *fmt++) {
139 case '%': /* "%%" is converted to "%". */
140 literal:
141 if (c != *bp++)
142 return (NULL);
143
144 break;
145
146 /*
147 * "Alternative" modifiers. Just set the appropriate flag
148 * and start over again.
149 */
150 case 'E': /* "%E?" alternative conversion modifier. */
151 _LEGAL_ALT(0);
152 alt_format |= _ALT_E;
153 goto again;
154
155 case 'O': /* "%O?" alternative conversion modifier. */
156 _LEGAL_ALT(0);
157 alt_format |= _ALT_O;
158 goto again;
159
160 /*
161 * "Complex" conversion rules, implemented through recursion.
162 */
163 case 'c': /* Date and time, using the locale's format. */
164 _LEGAL_ALT(_ALT_E);
165 if (!(bp = _strptime(bp, _ctloc(d_t_fmt), tm, cr)))
166 return (NULL);
167 break;
168
169 case 'D': /* The date as "%m/%d/%y". */
170 _LEGAL_ALT(0);
171 if (!(bp = _strptime(bp, "%m/%d/%y", tm, cr)))
172 return (NULL);
173 break;
174
175 case 'F': /* The date as "%Y-%m-%d". */
176 _LEGAL_ALT(0);
177 if (!(bp = _strptime(bp, "%Y-%m-%d", tm, cr)))
178 return (NULL);
179 continue;
180
181 case 'R': /* The time as "%H:%M". */
182 _LEGAL_ALT(0);
183 if (!(bp = _strptime(bp, "%H:%M", tm, cr)))
184 return (NULL);
185 break;
186
187 case 'r': /* The time as "%I:%M:%S %p". */
188 _LEGAL_ALT(0);
189 if (!(bp = _strptime(bp, "%I:%M:%S %p", tm, cr)))
190 return (NULL);
191 break;
192
193 case 'T': /* The time as "%H:%M:%S". */
194 _LEGAL_ALT(0);
195 if (!(bp = _strptime(bp, "%H:%M:%S", tm, cr)))
196 return (NULL);
197 break;
198
199 case 'v': /* The date as "%e-%b-%Y". */
200 _LEGAL_ALT(0);
201 if (!(bp = _strptime(bp, "%e-%b-%Y", tm, cr)))
202 return (NULL);
203 break;
204
205 case 'X': /* The time, using the locale's format. */
206 _LEGAL_ALT(_ALT_E);
207 if (!(bp = _strptime(bp, _ctloc(t_fmt), tm, cr)))
208 return (NULL);
209 break;
210
211 case 'x': /* The date, using the locale's format. */
212 _LEGAL_ALT(_ALT_E);
213 if (!(bp = _strptime(bp, _ctloc(d_fmt), tm, cr)))
214 return (NULL);
215 break;
216
217 /*
218 * "Elementary" conversion rules.
219 */
220 case 'A': /* The day of week, using the locale's form. */
221 case 'a':
222 _LEGAL_ALT(0);
223 for (i = 0; i < 7; i++) {
224 /* Full name. */
225 len = strlen(_ctloc(day[i]));
226 if (strncasecmp(_ctloc(day[i]), (const char*)bp, len) == 0)
227 break;
228
229 /* Abbreviated name. */
230 len = strlen(_ctloc(abday[i]));
231 if (strncasecmp(_ctloc(abday[i]), (const char*)bp, len) == 0)
232 break;
233 }
234
235 /* Nothing matched. */
236 if (i == 7)
237 return (NULL);
238
239 tm->tm_wday = i;
240 bp += len;
241 break;
242
243 case 'B': /* The month, using the locale's form. */
244 case 'b':
245 case 'h':
246 _LEGAL_ALT(0);
247 for (i = 0; i < 12; i++) {
248 /* Full name. */
249 len = strlen(_ctloc(mon[i]));
250 if (strncasecmp(_ctloc(mon[i]), (const char*)bp, len) == 0)
251 break;
252
253 /* Abbreviated name. */
254 len = strlen(_ctloc(abmon[i]));
255 if (strncasecmp(_ctloc(abmon[i]), (const char*)bp, len) == 0)
256 break;
257 }
258
259 /* Nothing matched. */
260 if (i == 12)
261 return (NULL);
262
263 tm->tm_mon = i;
264 bp += len;
265 break;
266
267 case 'C': /* The century number. */
268 _LEGAL_ALT(_ALT_E);
269 if (!(_conv_num(&bp, &i, 0, 99)))
270 return (NULL);
271
272 cr->century = i * 100;
273 break;
274
275 case 'd': /* The day of month. */
276 case 'e':
277 _LEGAL_ALT(_ALT_O);
278 if (!(_conv_num(&bp, &tm->tm_mday, 1, 31)))
279 return (NULL);
280 break;
281
282 case 'k': /* The hour (24-hour clock representation). */
283 _LEGAL_ALT(0);
284 /* FALLTHROUGH */
285 case 'H':
286 _LEGAL_ALT(_ALT_O);
287 if (!(_conv_num(&bp, &tm->tm_hour, 0, 23)))
288 return (NULL);
289 break;
290
291 case 'l': /* The hour (12-hour clock representation). */
292 _LEGAL_ALT(0);
293 /* FALLTHROUGH */
294 case 'I':
295 _LEGAL_ALT(_ALT_O);
296 if (!(_conv_num(&bp, &tm->tm_hour, 1, 12)))
297 return (NULL);
298 break;
299
300 case 'j': /* The day of year. */
301 _LEGAL_ALT(0);
302 if (!(_conv_num(&bp, &tm->tm_yday, 1, 366)))
303 return (NULL);
304 tm->tm_yday--;
305 break;
306
307 case 'M': /* The minute. */
308 _LEGAL_ALT(_ALT_O);
309 if (!(_conv_num(&bp, &tm->tm_min, 0, 59)))
310 return (NULL);
311 break;
312
313 case 'm': /* The month. */
314 _LEGAL_ALT(_ALT_O);
315 if (!(_conv_num(&bp, &tm->tm_mon, 1, 12)))
316 return (NULL);
317 tm->tm_mon--;
318 break;
319
320 case 'P':
321 case 'p': /* The locale's equivalent of AM/PM. */
322 _LEGAL_ALT(0);
323 /* AM? */
324 len = strlen(_ctloc(am_pm[0]));
325 if (strncasecmp(_ctloc(am_pm[0]), (const char*)bp, len) == 0) {
326 if (tm->tm_hour > 12) /* i.e., 13:00 AM ?! */
327 return (NULL);
328 else if (tm->tm_hour == 12)
329 tm->tm_hour = 0;
330
331 bp += len;
332 break;
333 }
334 /* PM? */
335 len = strlen(_ctloc(am_pm[1]));
336 if (strncasecmp(_ctloc(am_pm[1]), (const char*)bp, len) == 0) {
337 if (tm->tm_hour > 12) /* i.e., 13:00 PM ?! */
338 return (NULL);
339 else if (tm->tm_hour < 12)
340 tm->tm_hour += 12;
341
342 bp += len;
343 break;
344 }
345
346 /* Nothing matched. */
347 return (NULL);
348
349 case 'S': /* The seconds. */
350 _LEGAL_ALT(_ALT_O);
351 if (!(_conv_num(&bp, &tm->tm_sec, 0, 61)))
352 return (NULL);
353 break;
354
355 case 's':
356 {
357 // Android addition, based on FreeBSD's implementation.
358 int saved_errno = errno;
359 errno = 0;
360 const unsigned char* old_bp = bp;
361 long n = strtol((const char*) bp, (char**) &bp, 10);
362 time_t t = n;
363 if (bp == old_bp || errno == ERANGE || ((long) t) != n) {
364 errno = saved_errno;
365 return NULL;
366 }
367 errno = saved_errno;
368
369 if (localtime_r(&t, tm) == NULL) return NULL;
370 }
371 break;
372
373
374 case 'U': /* The week of year, beginning on sunday. */
375 case 'W': /* The week of year, beginning on monday. */
376 _LEGAL_ALT(_ALT_O);
377 /*
378 * XXX This is bogus, as we can not assume any valid
379 * information present in the tm structure at this
380 * point to calculate a real value, so just check the
381 * range for now.
382 */
383 if (!(_conv_num(&bp, &i, 0, 53)))
384 return (NULL);
385 break;
386
387 case 'w': /* The day of week, beginning on sunday. */
388 _LEGAL_ALT(_ALT_O);
389 if (!(_conv_num(&bp, &tm->tm_wday, 0, 6)))
390 return (NULL);
391 break;
392
393 case 'u': /* The day of week, monday = 1. */
394 _LEGAL_ALT(_ALT_O);
395 if (!(_conv_num(&bp, &i, 1, 7)))
396 return (NULL);
397 tm->tm_wday = i % 7;
398 continue;
399
400 case 'g': /* The year corresponding to the ISO week
401 * number but without the century.
402 */
403 if (!(_conv_num(&bp, &i, 0, 99)))
404 return (NULL);
405 continue;
406
407 case 'G': /* The year corresponding to the ISO week
408 * number with century.
409 */
410 do
411 bp++;
412 while (isdigit(*bp));
413 continue;
414
415 case 'V': /* The ISO 8601:1988 week number as decimal */
416 if (!(_conv_num(&bp, &i, 0, 53)))
417 return (NULL);
418 continue;
419
420 case 'Y': /* The year. */
421 _LEGAL_ALT(_ALT_E);
422 if (!(_conv_num(&bp, &i, 0, 9999)))
423 return (NULL);
424
425 cr->relyear = -1;
426 tm->tm_year = i - TM_YEAR_BASE;
427 break;
428
429 case 'y': /* The year within the century (2 digits). */
430 _LEGAL_ALT(_ALT_E | _ALT_O);
431 if (!(_conv_num(&bp, &cr->relyear, 0, 99)))
432 return (NULL);
433 break;
434
435 /*
436 * Miscellaneous conversions.
437 */
438 case 'n': /* Any kind of white-space. */
439 case 't':
440 _LEGAL_ALT(0);
441 while (isspace(*bp))
442 bp++;
443 break;
444
445
446 default: /* Unknown/unsupported conversion. */
447 return (NULL);
448 }
449
450
451 }
452
453 /*
454 * We need to evaluate the two digit year spec (%y)
455 * last as we can get a century spec (%C) at any time.
456 */
457 if (cr->relyear != -1) {
458 if (cr->century == TM_YEAR_BASE) {
459 if (cr->relyear <= 68)
460 tm->tm_year = cr->relyear + 2000 - TM_YEAR_BASE;
461 else
462 tm->tm_year = cr->relyear + 1900 - TM_YEAR_BASE;
463 } else {
464 tm->tm_year = cr->relyear + cr->century - TM_YEAR_BASE;
465 }
466 }
467
468 return (unsigned char*)bp;
469 }
470
471
472 static int
_conv_num(const unsigned char ** buf,int * dest,int llim,int ulim)473 _conv_num(const unsigned char **buf, int *dest, int llim, int ulim)
474 {
475 int result = 0;
476 int rulim = ulim;
477
478 if (**buf < '0' || **buf > '9')
479 return (0);
480
481 /* we use rulim to break out of the loop when we run out of digits */
482 do {
483 result *= 10;
484 result += *(*buf)++ - '0';
485 rulim /= 10;
486 } while ((result * 10 <= ulim) && rulim && **buf >= '0' && **buf <= '9');
487
488 if (result < llim || result > ulim)
489 return (0);
490
491 *dest = result;
492 return (1);
493 }
494
strptime_l(const char * buf,const char * fmt,struct tm * tm,locale_t l)495 char* strptime_l(const char* buf, const char* fmt, struct tm* tm, locale_t l) {
496 return strptime(buf, fmt, tm);
497 }
498