blob: d31a501567b6d318f4399e19eaf85497b65a50ee [file] [log] [blame]
Elliott Hughes53b6dfb2023-03-06 23:32:48 +00001/* $OpenBSD: strptime.c,v 1.31 2023/03/02 16:21:51 millert Exp $ */
Elliott Hughesd3627a42022-12-06 22:24:27 +00002/* $NetBSD: strptime.c,v 1.12 1998/01/20 21:39:40 mycroft Exp $ */
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08003/*-
Elliott Hughesd3627a42022-12-06 22:24:27 +00004 * Copyright (c) 1997, 1998, 2005, 2008 The NetBSD Foundation, Inc.
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08005 * All rights reserved.
6 *
7 * This code was contributed to The NetBSD Foundation by Klaus Klein.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080017 *
18 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
19 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
22 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080031#include <ctype.h>
Elliott Hughes53b6dfb2023-03-06 23:32:48 +000032#include <errno.h>
33#include <limits.h>
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080034#include <locale.h>
Elliott Hughes53b6dfb2023-03-06 23:32:48 +000035#include <stdlib.h>
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080036#include <string.h>
37#include <time.h>
Elliott Hughesd3627a42022-12-06 22:24:27 +000038
39#include "localedef.h"
40#include "private.h"
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080041#include "tzfile.h"
42
Elliott Hughesd3627a42022-12-06 22:24:27 +000043// Android: ignore OpenBSD's DEF_WEAK() stuff.
44#define DEF_WEAK(sym) /* */
45// Android: this code is not pointer-sign clean.
46#pragma clang diagnostic ignored "-Wpointer-sign"
47#pragma clang diagnostic ignored "-Wunused-function"
Elliott Hughes53b6dfb2023-03-06 23:32:48 +000048// Android: clang thinks people don't know && has higher precedence than ||.
49#pragma clang diagnostic ignored "-Wlogical-op-parentheses"
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080050
Elliott Hughesd3627a42022-12-06 22:24:27 +000051#define _ctloc(x) (_CurrentTimeLocale->x)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080052
53/*
54 * We do not implement alternate representations. However, we always
55 * check whether a given modifier is allowed for a certain conversion.
56 */
Elliott Hughesd3627a42022-12-06 22:24:27 +000057#define _ALT_E 0x01
58#define _ALT_O 0x02
59#define _LEGAL_ALT(x) { if (alt_format & ~(x)) return (0); }
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080060
Elliott Hughesd3627a42022-12-06 22:24:27 +000061/*
62 * We keep track of some of the fields we set in order to compute missing ones.
63 */
64#define FIELD_TM_MON (1 << 0)
65#define FIELD_TM_MDAY (1 << 1)
66#define FIELD_TM_WDAY (1 << 2)
67#define FIELD_TM_YDAY (1 << 3)
68#define FIELD_TM_YEAR (1 << 4)
Elliott Hughesd065c042020-09-01 19:02:44 -070069
70static char gmt[] = { "GMT" };
71static char utc[] = { "UTC" };
72/* RFC-822/RFC-2822 */
73static const char * const nast[5] = {
74 "EST", "CST", "MST", "PST", "\0\0\0"
75};
76static const char * const nadt[5] = {
77 "EDT", "CDT", "MDT", "PDT", "\0\0\0"
78};
79
Elliott Hughesd3627a42022-12-06 22:24:27 +000080static const int mon_lengths[2][MONSPERYEAR] = {
81 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
82 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
83};
84
Elliott Hughesd3627a42022-12-06 22:24:27 +000085static int _conv_num(const unsigned char **, int *, int, int);
Elliott Hughes53b6dfb2023-03-06 23:32:48 +000086static int epoch_to_tm(const unsigned char **, struct tm *);
Elliott Hughesd3627a42022-12-06 22:24:27 +000087static int leaps_thru_end_of(const int y);
88static char *_strptime(const char *, const char *, struct tm *, int);
Elliott Hughesd065c042020-09-01 19:02:44 -070089static const u_char *_find_string(const u_char *, int *, const char * const *,
90 const char * const *, int);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080091
92
93char *
94strptime(const char *buf, const char *fmt, struct tm *tm)
95{
Elliott Hughesd3627a42022-12-06 22:24:27 +000096 return(_strptime(buf, fmt, tm, 1));
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080097}
Elliott Hughesd3627a42022-12-06 22:24:27 +000098DEF_WEAK(strptime);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080099
Elliott Hughesd3627a42022-12-06 22:24:27 +0000100static char *
101_strptime(const char *buf, const char *fmt, struct tm *tm, int initialize)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800102{
Elliott Hughesd3627a42022-12-06 22:24:27 +0000103 unsigned char c;
104 const unsigned char *bp, *ep;
105 size_t len;
106 int alt_format, i, offs;
107 int neg = 0;
108 static int century, relyear, fields;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800109
Elliott Hughesd3627a42022-12-06 22:24:27 +0000110 if (initialize) {
111 century = TM_YEAR_BASE;
112 relyear = -1;
113 fields = 0;
114 }
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800115
Elliott Hughesd3627a42022-12-06 22:24:27 +0000116 bp = (const unsigned char *)buf;
117 while ((c = *fmt) != '\0') {
118 /* Clear `alternate' modifier prior to new conversion. */
119 alt_format = 0;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800120
Elliott Hughesd3627a42022-12-06 22:24:27 +0000121 /* Eat up white-space. */
122 if (isspace(c)) {
123 while (isspace(*bp))
124 bp++;
Elliott Hughes81baaf22018-02-28 15:22:48 -0800125
Elliott Hughesd3627a42022-12-06 22:24:27 +0000126 fmt++;
127 continue;
128 }
129
130 if ((c = *fmt++) != '%')
131 goto literal;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800132
133
Elliott Hughesd3627a42022-12-06 22:24:27 +0000134again: switch (c = *fmt++) {
135 case '%': /* "%%" is converted to "%". */
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800136literal:
Elliott Hughesd3627a42022-12-06 22:24:27 +0000137 if (c != *bp++)
138 return (NULL);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800139
Elliott Hughesd3627a42022-12-06 22:24:27 +0000140 break;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800141
Elliott Hughesd3627a42022-12-06 22:24:27 +0000142 /*
143 * "Alternative" modifiers. Just set the appropriate flag
144 * and start over again.
145 */
146 case 'E': /* "%E?" alternative conversion modifier. */
147 _LEGAL_ALT(0);
148 alt_format |= _ALT_E;
149 goto again;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800150
Elliott Hughesd3627a42022-12-06 22:24:27 +0000151 case 'O': /* "%O?" alternative conversion modifier. */
152 _LEGAL_ALT(0);
153 alt_format |= _ALT_O;
154 goto again;
Elliott Hughes81baaf22018-02-28 15:22:48 -0800155
Elliott Hughesd3627a42022-12-06 22:24:27 +0000156 /*
157 * "Complex" conversion rules, implemented through recursion.
158 */
159 case 'c': /* Date and time, using the locale's format. */
160 _LEGAL_ALT(_ALT_E);
161 if (!(bp = _strptime(bp, _ctloc(d_t_fmt), tm, 0)))
162 return (NULL);
163 break;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800164
Elliott Hughesd3627a42022-12-06 22:24:27 +0000165 case 'D': /* The date as "%m/%d/%y". */
166 _LEGAL_ALT(0);
167 if (!(bp = _strptime(bp, "%m/%d/%y", tm, 0)))
168 return (NULL);
169 break;
Elliott Hughes81baaf22018-02-28 15:22:48 -0800170
Elliott Hughesd3627a42022-12-06 22:24:27 +0000171 case 'F': /* The date as "%Y-%m-%d". */
172 _LEGAL_ALT(0);
173 if (!(bp = _strptime(bp, "%Y-%m-%d", tm, 0)))
174 return (NULL);
175 continue;
Elliott Hughesa1fb15b2019-03-26 19:07:40 -0700176
Elliott Hughesd3627a42022-12-06 22:24:27 +0000177 case 'R': /* The time as "%H:%M". */
178 _LEGAL_ALT(0);
179 if (!(bp = _strptime(bp, "%H:%M", tm, 0)))
180 return (NULL);
181 break;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800182
Elliott Hughesd3627a42022-12-06 22:24:27 +0000183 case 'r': /* The time as "%I:%M:%S %p". */
184 _LEGAL_ALT(0);
185 if (!(bp = _strptime(bp, "%I:%M:%S %p", tm, 0)))
186 return (NULL);
187 break;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800188
Elliott Hughesd3627a42022-12-06 22:24:27 +0000189 case 'T': /* The time as "%H:%M:%S". */
190 _LEGAL_ALT(0);
191 if (!(bp = _strptime(bp, "%H:%M:%S", tm, 0)))
192 return (NULL);
193 break;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800194
Elliott Hughesd3627a42022-12-06 22:24:27 +0000195 case 'v': /* Android: the date as "%e-%b-%Y" for strftime() compat; glibc does this too. */
196 _LEGAL_ALT(0);
197 if (!(bp = _strptime(bp, "%e-%b-%Y", tm, 0)))
198 return (NULL);
199 break;
Elliott Hughesa1fb15b2019-03-26 19:07:40 -0700200
Elliott Hughesd3627a42022-12-06 22:24:27 +0000201 case 'X': /* The time, using the locale's format. */
202 _LEGAL_ALT(_ALT_E);
203 if (!(bp = _strptime(bp, _ctloc(t_fmt), tm, 0)))
204 return (NULL);
205 break;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800206
Elliott Hughesd3627a42022-12-06 22:24:27 +0000207 case 'x': /* The date, using the locale's format. */
208 _LEGAL_ALT(_ALT_E);
209 if (!(bp = _strptime(bp, _ctloc(d_fmt), tm, 0)))
210 return (NULL);
211 break;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800212
Elliott Hughesd3627a42022-12-06 22:24:27 +0000213 /*
214 * "Elementary" conversion rules.
215 */
216 case 'A': /* The day of week, using the locale's form. */
217 case 'a':
218 _LEGAL_ALT(0);
219 for (i = 0; i < 7; i++) {
220 /* Full name. */
221 len = strlen(_ctloc(day[i]));
222 if (strncasecmp(_ctloc(day[i]), bp, len) == 0)
223 break;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800224
Elliott Hughesd3627a42022-12-06 22:24:27 +0000225 /* Abbreviated name. */
226 len = strlen(_ctloc(abday[i]));
227 if (strncasecmp(_ctloc(abday[i]), bp, len) == 0)
228 break;
229 }
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800230
Elliott Hughesd3627a42022-12-06 22:24:27 +0000231 /* Nothing matched. */
232 if (i == 7)
233 return (NULL);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800234
Elliott Hughesd3627a42022-12-06 22:24:27 +0000235 tm->tm_wday = i;
236 bp += len;
237 fields |= FIELD_TM_WDAY;
238 break;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800239
Elliott Hughesd3627a42022-12-06 22:24:27 +0000240 case 'B': /* The month, using the locale's form. */
241 case 'b':
242 case 'h':
243 _LEGAL_ALT(0);
244 for (i = 0; i < 12; i++) {
245 /* Full name. */
246 len = strlen(_ctloc(mon[i]));
247 if (strncasecmp(_ctloc(mon[i]), bp, len) == 0)
248 break;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800249
Elliott Hughesd3627a42022-12-06 22:24:27 +0000250 /* Abbreviated name. */
251 len = strlen(_ctloc(abmon[i]));
252 if (strncasecmp(_ctloc(abmon[i]), bp, len) == 0)
253 break;
254 }
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800255
Elliott Hughesd3627a42022-12-06 22:24:27 +0000256 /* Nothing matched. */
257 if (i == 12)
258 return (NULL);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800259
Elliott Hughesd3627a42022-12-06 22:24:27 +0000260 tm->tm_mon = i;
261 bp += len;
262 fields |= FIELD_TM_MON;
263 break;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800264
Elliott Hughesd3627a42022-12-06 22:24:27 +0000265 case 'C': /* The century number. */
266 _LEGAL_ALT(_ALT_E);
267 if (!(_conv_num(&bp, &i, 0, 99)))
268 return (NULL);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800269
Elliott Hughesd3627a42022-12-06 22:24:27 +0000270 century = i * 100;
271 break;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800272
Elliott Hughesd3627a42022-12-06 22:24:27 +0000273 case 'e': /* The day of month. */
274 if (isspace(*bp))
275 bp++;
276 /* FALLTHROUGH */
277 case 'd':
278 _LEGAL_ALT(_ALT_O);
279 if (!(_conv_num(&bp, &tm->tm_mday, 1, 31)))
280 return (NULL);
281 fields |= FIELD_TM_MDAY;
282 break;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800283
Elliott Hughesd3627a42022-12-06 22:24:27 +0000284 case 'k': /* The hour (24-hour clock representation). */
285 _LEGAL_ALT(0);
286 /* FALLTHROUGH */
287 case 'H':
288 _LEGAL_ALT(_ALT_O);
289 if (!(_conv_num(&bp, &tm->tm_hour, 0, 23)))
290 return (NULL);
291 break;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800292
Elliott Hughesd3627a42022-12-06 22:24:27 +0000293 case 'l': /* The hour (12-hour clock representation). */
294 _LEGAL_ALT(0);
295 /* FALLTHROUGH */
296 case 'I':
297 _LEGAL_ALT(_ALT_O);
298 if (!(_conv_num(&bp, &tm->tm_hour, 1, 12)))
299 return (NULL);
300 break;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800301
Elliott Hughesd3627a42022-12-06 22:24:27 +0000302 case 'j': /* The day of year. */
303 _LEGAL_ALT(0);
304 if (!(_conv_num(&bp, &tm->tm_yday, 1, 366)))
305 return (NULL);
306 tm->tm_yday--;
307 fields |= FIELD_TM_YDAY;
308 break;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800309
Elliott Hughesd3627a42022-12-06 22:24:27 +0000310 case 'M': /* The minute. */
311 _LEGAL_ALT(_ALT_O);
312 if (!(_conv_num(&bp, &tm->tm_min, 0, 59)))
313 return (NULL);
314 break;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800315
Elliott Hughesd3627a42022-12-06 22:24:27 +0000316 case 'm': /* The month. */
317 _LEGAL_ALT(_ALT_O);
318 if (!(_conv_num(&bp, &tm->tm_mon, 1, 12)))
319 return (NULL);
320 tm->tm_mon--;
321 fields |= FIELD_TM_MON;
322 break;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800323
Elliott Hughesd3627a42022-12-06 22:24:27 +0000324 case 'P': /* Android addition for strftime() compat; glibc does this too. */
325 case 'p': /* The locale's equivalent of AM/PM. */
326 _LEGAL_ALT(0);
327 /* AM? */
328 len = strlen(_ctloc(am_pm[0]));
329 if (strncasecmp(_ctloc(am_pm[0]), bp, len) == 0) {
330 if (tm->tm_hour > 12) /* i.e., 13:00 AM ?! */
331 return (NULL);
332 else if (tm->tm_hour == 12)
333 tm->tm_hour = 0;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800334
Elliott Hughesd3627a42022-12-06 22:24:27 +0000335 bp += len;
336 break;
337 }
338 /* PM? */
339 len = strlen(_ctloc(am_pm[1]));
340 if (strncasecmp(_ctloc(am_pm[1]), bp, len) == 0) {
341 if (tm->tm_hour > 12) /* i.e., 13:00 PM ?! */
342 return (NULL);
343 else if (tm->tm_hour < 12)
344 tm->tm_hour += 12;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800345
Elliott Hughesd3627a42022-12-06 22:24:27 +0000346 bp += len;
347 break;
348 }
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800349
Elliott Hughesd3627a42022-12-06 22:24:27 +0000350 /* Nothing matched. */
351 return (NULL);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800352
Elliott Hughesd3627a42022-12-06 22:24:27 +0000353 case 'S': /* The seconds. */
354 _LEGAL_ALT(_ALT_O);
355 if (!(_conv_num(&bp, &tm->tm_sec, 0, 60)))
356 return (NULL);
357 break;
Elliott Hughes53b6dfb2023-03-06 23:32:48 +0000358 case 's': /* Seconds since epoch. */
359 if (!(epoch_to_tm(&bp, tm)))
360 return (NULL);
361 fields = 0xffff; /* everything */
Elliott Hughesd3627a42022-12-06 22:24:27 +0000362 break;
363 case 'U': /* The week of year, beginning on sunday. */
364 case 'W': /* The week of year, beginning on monday. */
365 _LEGAL_ALT(_ALT_O);
366 /*
367 * XXX This is bogus, as we can not assume any valid
368 * information present in the tm structure at this
369 * point to calculate a real value, so just check the
370 * range for now.
371 */
372 if (!(_conv_num(&bp, &i, 0, 53)))
373 return (NULL);
374 break;
Elliott Hughes81baaf22018-02-28 15:22:48 -0800375
Elliott Hughesd3627a42022-12-06 22:24:27 +0000376 case 'w': /* The day of week, beginning on sunday. */
377 _LEGAL_ALT(_ALT_O);
378 if (!(_conv_num(&bp, &tm->tm_wday, 0, 6)))
379 return (NULL);
380 fields |= FIELD_TM_WDAY;
381 break;
Elliott Hughes81baaf22018-02-28 15:22:48 -0800382
Elliott Hughesd3627a42022-12-06 22:24:27 +0000383 case 'u': /* The day of week, monday = 1. */
384 _LEGAL_ALT(_ALT_O);
385 if (!(_conv_num(&bp, &i, 1, 7)))
386 return (NULL);
387 tm->tm_wday = i % 7;
388 fields |= FIELD_TM_WDAY;
389 continue;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800390
Elliott Hughesd3627a42022-12-06 22:24:27 +0000391 case 'g': /* The year corresponding to the ISO week
392 * number but without the century.
393 */
394 if (!(_conv_num(&bp, &i, 0, 99)))
395 return (NULL);
396 continue;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800397
Elliott Hughesd3627a42022-12-06 22:24:27 +0000398 case 'G': /* The year corresponding to the ISO week
399 * number with century.
400 */
401 do
402 bp++;
403 while (isdigit(*bp));
404 continue;
Elliott Hughesa1fb15b2019-03-26 19:07:40 -0700405
Elliott Hughesd3627a42022-12-06 22:24:27 +0000406 case 'V': /* The ISO 8601:1988 week number as decimal */
407 if (!(_conv_num(&bp, &i, 0, 53)))
408 return (NULL);
409 continue;
Elliott Hughesa1fb15b2019-03-26 19:07:40 -0700410
Elliott Hughesd3627a42022-12-06 22:24:27 +0000411 case 'Y': /* The year. */
412 _LEGAL_ALT(_ALT_E);
413 if (!(_conv_num(&bp, &i, 0, 9999)))
414 return (NULL);
Elliott Hughesa1fb15b2019-03-26 19:07:40 -0700415
Elliott Hughesd3627a42022-12-06 22:24:27 +0000416 relyear = -1;
417 tm->tm_year = i - TM_YEAR_BASE;
418 fields |= FIELD_TM_YEAR;
419 break;
Elliott Hughesa1fb15b2019-03-26 19:07:40 -0700420
Elliott Hughesd3627a42022-12-06 22:24:27 +0000421 case 'y': /* The year within the century (2 digits). */
422 _LEGAL_ALT(_ALT_E | _ALT_O);
423 if (!(_conv_num(&bp, &relyear, 0, 99)))
424 return (NULL);
425 break;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800426
Elliott Hughesd065c042020-09-01 19:02:44 -0700427 case 'Z':
428 tzset();
429 if (strncmp((const char *)bp, gmt, 3) == 0) {
430 tm->tm_isdst = 0;
431 tm->tm_gmtoff = 0;
432 tm->tm_zone = gmt;
433 bp += 3;
434 } else if (strncmp((const char *)bp, utc, 3) == 0) {
435 tm->tm_isdst = 0;
436 tm->tm_gmtoff = 0;
437 tm->tm_zone = utc;
438 bp += 3;
439 } else {
440 ep = _find_string(bp, &i,
441 (const char * const *)tzname,
442 NULL, 2);
443 if (ep == NULL)
444 return (NULL);
445
446 tm->tm_isdst = i;
447 tm->tm_gmtoff = -(timezone);
448 tm->tm_zone = tzname[i];
449 bp = ep;
450 }
451 continue;
452
453 case 'z':
454 /*
455 * We recognize all ISO 8601 formats:
456 * Z = Zulu time/UTC
457 * [+-]hhmm
458 * [+-]hh:mm
459 * [+-]hh
460 * We recognize all RFC-822/RFC-2822 formats:
461 * UT|GMT
462 * North American : UTC offsets
463 * E[DS]T = Eastern : -4 | -5
464 * C[DS]T = Central : -5 | -6
465 * M[DS]T = Mountain: -6 | -7
466 * P[DS]T = Pacific : -7 | -8
467 */
468 while (isspace(*bp))
469 bp++;
470
471 switch (*bp++) {
472 case 'G':
473 if (*bp++ != 'M')
474 return NULL;
475 /*FALLTHROUGH*/
476 case 'U':
477 if (*bp++ != 'T')
478 return NULL;
479 /*FALLTHROUGH*/
480 case 'Z':
481 tm->tm_isdst = 0;
482 tm->tm_gmtoff = 0;
483 tm->tm_zone = utc;
484 continue;
485 case '+':
486 neg = 0;
487 break;
488 case '-':
489 neg = 1;
490 break;
491 default:
492 --bp;
493 ep = _find_string(bp, &i, nast, NULL, 4);
494 if (ep != NULL) {
495 tm->tm_gmtoff = (-5 - i) * SECSPERHOUR;
496 tm->tm_zone = (char *)nast[i];
497 bp = ep;
498 continue;
499 }
500 ep = _find_string(bp, &i, nadt, NULL, 4);
501 if (ep != NULL) {
502 tm->tm_isdst = 1;
503 tm->tm_gmtoff = (-4 - i) * SECSPERHOUR;
504 tm->tm_zone = (char *)nadt[i];
505 bp = ep;
506 continue;
507 }
508 return NULL;
509 }
510 if (!isdigit(bp[0]) || !isdigit(bp[1]))
511 return NULL;
512 offs = ((bp[0]-'0') * 10 + (bp[1]-'0')) * SECSPERHOUR;
513 bp += 2;
514 if (*bp == ':')
515 bp++;
516 if (isdigit(*bp)) {
517 offs += (*bp++ - '0') * 10 * SECSPERMIN;
518 if (!isdigit(*bp))
519 return NULL;
520 offs += (*bp++ - '0') * SECSPERMIN;
521 }
522 if (neg)
523 offs = -offs;
524 tm->tm_isdst = 0; /* XXX */
525 tm->tm_gmtoff = offs;
526 tm->tm_zone = NULL; /* XXX */
527 continue;
528
Elliott Hughesd3627a42022-12-06 22:24:27 +0000529 /*
530 * Miscellaneous conversions.
531 */
532 case 'n': /* Any kind of white-space. */
533 case 't':
534 _LEGAL_ALT(0);
535 while (isspace(*bp))
536 bp++;
537 break;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800538
539
Elliott Hughesd3627a42022-12-06 22:24:27 +0000540 default: /* Unknown/unsupported conversion. */
541 return (NULL);
542 }
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800543
544
Elliott Hughesd3627a42022-12-06 22:24:27 +0000545 }
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800546
Elliott Hughesd3627a42022-12-06 22:24:27 +0000547 /*
548 * We need to evaluate the two digit year spec (%y)
549 * last as we can get a century spec (%C) at any time.
550 */
551 if (relyear != -1) {
552 if (century == TM_YEAR_BASE) {
553 if (relyear <= 68)
554 tm->tm_year = relyear + 2000 - TM_YEAR_BASE;
555 else
556 tm->tm_year = relyear + 1900 - TM_YEAR_BASE;
557 } else {
558 tm->tm_year = relyear + century - TM_YEAR_BASE;
559 }
560 fields |= FIELD_TM_YEAR;
561 }
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800562
Elliott Hughesd3627a42022-12-06 22:24:27 +0000563 /* Compute some missing values when possible. */
564 if (fields & FIELD_TM_YEAR) {
565 const int year = tm->tm_year + TM_YEAR_BASE;
566 const int *mon_lens = mon_lengths[isleap(year)];
567 if (!(fields & FIELD_TM_YDAY) &&
568 (fields & FIELD_TM_MON) && (fields & FIELD_TM_MDAY)) {
569 tm->tm_yday = tm->tm_mday - 1;
570 for (i = 0; i < tm->tm_mon; i++)
571 tm->tm_yday += mon_lens[i];
572 fields |= FIELD_TM_YDAY;
573 }
574 if (fields & FIELD_TM_YDAY) {
575 int days = tm->tm_yday;
576 if (!(fields & FIELD_TM_WDAY)) {
577 tm->tm_wday = EPOCH_WDAY +
578 ((year - EPOCH_YEAR) % DAYSPERWEEK) *
579 (DAYSPERNYEAR % DAYSPERWEEK) +
580 leaps_thru_end_of(year - 1) -
581 leaps_thru_end_of(EPOCH_YEAR - 1) +
582 tm->tm_yday;
583 tm->tm_wday %= DAYSPERWEEK;
584 if (tm->tm_wday < 0)
585 tm->tm_wday += DAYSPERWEEK;
586 }
587 if (!(fields & FIELD_TM_MON)) {
588 tm->tm_mon = 0;
589 while (tm->tm_mon < MONSPERYEAR && days >= mon_lens[tm->tm_mon])
590 days -= mon_lens[tm->tm_mon++];
591 }
592 if (!(fields & FIELD_TM_MDAY))
593 tm->tm_mday = days + 1;
594 }
595 }
596
597 return ((char *)bp);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800598}
599
Elliott Hughesd3627a42022-12-06 22:24:27 +0000600
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800601static int
602_conv_num(const unsigned char **buf, int *dest, int llim, int ulim)
603{
Elliott Hughesd065c042020-09-01 19:02:44 -0700604 int result = 0;
605 int rulim = ulim;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800606
Elliott Hughesd065c042020-09-01 19:02:44 -0700607 if (**buf < '0' || **buf > '9')
608 return (0);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800609
Elliott Hughesd065c042020-09-01 19:02:44 -0700610 /* we use rulim to break out of the loop when we run out of digits */
611 do {
612 result *= 10;
613 result += *(*buf)++ - '0';
614 rulim /= 10;
615 } while ((result * 10 <= ulim) && rulim && **buf >= '0' && **buf <= '9');
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800616
Elliott Hughesd065c042020-09-01 19:02:44 -0700617 if (result < llim || result > ulim)
618 return (0);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800619
Elliott Hughesd065c042020-09-01 19:02:44 -0700620 *dest = result;
621 return (1);
622}
623
Elliott Hughesd3627a42022-12-06 22:24:27 +0000624static int
Elliott Hughes53b6dfb2023-03-06 23:32:48 +0000625epoch_to_tm(const unsigned char **buf, struct tm *tm)
Elliott Hughesd3627a42022-12-06 22:24:27 +0000626{
Elliott Hughes53b6dfb2023-03-06 23:32:48 +0000627 int saved_errno = errno;
628 int ret = 0;
629 time_t secs;
630 char *ep;
Elliott Hughesd3627a42022-12-06 22:24:27 +0000631
Elliott Hughes53b6dfb2023-03-06 23:32:48 +0000632 errno = 0;
633 secs = strtoll(*buf, &ep, 10);
634 if (*buf == (unsigned char *)ep)
635 goto done;
636 if (secs < 0 ||
637 secs == LLONG_MAX && errno == ERANGE)
638 goto done;
639 if (localtime_r(&secs, tm) == NULL)
640 goto done;
641 ret = 1;
642done:
643 *buf = ep;
644 errno = saved_errno;
645 return (ret);
Elliott Hughesd3627a42022-12-06 22:24:27 +0000646}
647
Elliott Hughesd065c042020-09-01 19:02:44 -0700648static const u_char *
649_find_string(const u_char *bp, int *tgt, const char * const *n1,
650 const char * const *n2, int c)
651{
652 int i;
653 unsigned int len;
654
655 /* check full name - then abbreviated ones */
656 for (; n1 != NULL; n1 = n2, n2 = NULL) {
657 for (i = 0; i < c; i++, n1++) {
658 len = strlen(*n1);
659 if (strncasecmp(*n1, (const char *)bp, len) == 0) {
660 *tgt = i;
661 return bp + len;
662 }
663 }
664 }
665
666 /* Nothing matched */
667 return NULL;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800668}
Elliott Hughes3376c232018-02-13 23:14:12 -0800669
Elliott Hughesd3627a42022-12-06 22:24:27 +0000670static int
671leaps_thru_end_of(const int y)
672{
673 return (y >= 0) ? (y / 4 - y / 100 + y / 400) :
674 -(leaps_thru_end_of(-(y + 1)) + 1);
Elliott Hughes3376c232018-02-13 23:14:12 -0800675}