xref: /freebsd/lib/libc/stdtime/strptime.c (revision 0b8224d1)
10413bfcaSPedro F. Giffuni /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3d915a14eSPedro F. Giffuni  *
4cd9dcb03SPedro F. Giffuni  * Copyright (c) 2014 Gary Mills
5cd9dcb03SPedro F. Giffuni  * Copyright 2011, Nexenta Systems, Inc.  All rights reserved.
6d7641983SJoerg Wunsch  * Copyright (c) 1994 Powerdog Industries.  All rights reserved.
7d7641983SJoerg Wunsch  *
83c87aa1dSDavid Chisnall  * Copyright (c) 2011 The FreeBSD Foundation
95b5fa75aSEd Maste  *
103c87aa1dSDavid Chisnall  * Portions of this software were developed by David Chisnall
113c87aa1dSDavid Chisnall  * under sponsorship from the FreeBSD Foundation.
123c87aa1dSDavid Chisnall  *
1337486f03SJoerg Wunsch  * Redistribution and use in source and binary forms, with or without
14d7641983SJoerg Wunsch  * modification, are permitted provided that the following conditions
15d7641983SJoerg Wunsch  * are met:
16d7641983SJoerg Wunsch  * 1. Redistributions of source code must retain the above copyright
17d7641983SJoerg Wunsch  *    notice, this list of conditions and the following disclaimer.
18d7641983SJoerg Wunsch  * 2. Redistributions in binary form must reproduce the above copyright
19d7641983SJoerg Wunsch  *    notice, this list of conditions and the following disclaimer
20d7641983SJoerg Wunsch  *    in the documentation and/or other materials provided with the
21d7641983SJoerg Wunsch  *    distribution.
22d7641983SJoerg Wunsch  *
23d7641983SJoerg Wunsch  * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY
24d7641983SJoerg Wunsch  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25d7641983SJoerg Wunsch  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26d7641983SJoerg Wunsch  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE
27d7641983SJoerg Wunsch  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28d7641983SJoerg Wunsch  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29d7641983SJoerg Wunsch  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
30d7641983SJoerg Wunsch  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
31d7641983SJoerg Wunsch  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
32d7641983SJoerg Wunsch  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
33d7641983SJoerg Wunsch  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
340413bfcaSPedro F. Giffuni  *
350413bfcaSPedro F. Giffuni  * The views and conclusions contained in the software and documentation
360413bfcaSPedro F. Giffuni  * are those of the authors and should not be interpreted as representing
370413bfcaSPedro F. Giffuni  * official policies, either expressed or implied, of Powerdog Industries.
38d7641983SJoerg Wunsch  */
39d7641983SJoerg Wunsch 
40d201fe46SDaniel Eischen #include "namespace.h"
41d7641983SJoerg Wunsch #include <time.h>
42d7641983SJoerg Wunsch #include <ctype.h>
43c91e947dSJacques Vidrine #include <errno.h>
44427fc5edSDima Dorfman #include <stdlib.h>
45d7641983SJoerg Wunsch #include <string.h>
46cb7a4779SDavid E. O'Brien #include <pthread.h>
47bc421551SDag-Erling Smørgrav #include "private.h"
48d201fe46SDaniel Eischen #include "un-namespace.h"
49d201fe46SDaniel Eischen #include "libc_private.h"
5037486f03SJoerg Wunsch #include "timelocal.h"
512e465587SPedro F. Giffuni #include "tzfile.h"
521a6804c6SPedro F. Giffuni 
533c87aa1dSDavid Chisnall static char * _strptime(const char *, const char *, struct tm *, int *, locale_t);
54cb7a4779SDavid E. O'Brien 
55d7641983SJoerg Wunsch #define	asizeof(a)	(sizeof(a) / sizeof((a)[0]))
56d7641983SJoerg Wunsch 
572e465587SPedro F. Giffuni #define	FLAG_NONE	(1 << 0)
582e465587SPedro F. Giffuni #define	FLAG_YEAR	(1 << 1)
592e465587SPedro F. Giffuni #define	FLAG_MONTH	(1 << 2)
602e465587SPedro F. Giffuni #define	FLAG_YDAY	(1 << 3)
612e465587SPedro F. Giffuni #define	FLAG_MDAY	(1 << 4)
622e465587SPedro F. Giffuni #define	FLAG_WDAY	(1 << 5)
632e465587SPedro F. Giffuni 
642e465587SPedro F. Giffuni /*
652e465587SPedro F. Giffuni  * Calculate the week day of the first day of a year. Valid for
662e465587SPedro F. Giffuni  * the Gregorian calendar, which began Sept 14, 1752 in the UK
672e465587SPedro F. Giffuni  * and its colonies. Ref:
682e465587SPedro F. Giffuni  * http://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week
692e465587SPedro F. Giffuni  */
702e465587SPedro F. Giffuni 
712e465587SPedro F. Giffuni static int
first_wday_of(int year)722e465587SPedro F. Giffuni first_wday_of(int year)
732e465587SPedro F. Giffuni {
742e465587SPedro F. Giffuni 	return (((2 * (3 - (year / 100) % 4)) + (year % 100) +
752e465587SPedro F. Giffuni 		((year % 100) / 4) + (isleap(year) ? 6 : 0) + 1) % 7);
762e465587SPedro F. Giffuni }
772e465587SPedro F. Giffuni 
78cb7a4779SDavid E. O'Brien static char *
_strptime(const char * buf,const char * fmt,struct tm * tm,int * GMTp,locale_t locale)793c87aa1dSDavid Chisnall _strptime(const char *buf, const char *fmt, struct tm *tm, int *GMTp,
803c87aa1dSDavid Chisnall 		locale_t locale)
81d7641983SJoerg Wunsch {
8237486f03SJoerg Wunsch 	char	c;
8337486f03SJoerg Wunsch 	const char *ptr;
842e465587SPedro F. Giffuni 	int	day_offset = -1, wday_offset;
852e465587SPedro F. Giffuni 	int week_offset;
863624c752SPedro F. Giffuni 	int	i, len;
872e465587SPedro F. Giffuni 	int flags;
881d6c9941SAndrey A. Chernov 	int Ealternative, Oalternative;
89eb144aa0SYuri Pankov 	int century, year;
902e465587SPedro F. Giffuni 	const struct lc_time_T *tptr = __get_current_time_locale(locale);
912e465587SPedro F. Giffuni 	static int start_of_month[2][13] = {
922e465587SPedro F. Giffuni 		{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
932e465587SPedro F. Giffuni 		{0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}
942e465587SPedro F. Giffuni 	};
952e465587SPedro F. Giffuni 
962e465587SPedro F. Giffuni 	flags = FLAG_NONE;
97eb144aa0SYuri Pankov 	century = -1;
98eb144aa0SYuri Pankov 	year = -1;
99d7641983SJoerg Wunsch 
100d7641983SJoerg Wunsch 	ptr = fmt;
101d7641983SJoerg Wunsch 	while (*ptr != 0) {
102d7641983SJoerg Wunsch 		c = *ptr++;
103d7641983SJoerg Wunsch 
104d7641983SJoerg Wunsch 		if (c != '%') {
1053c87aa1dSDavid Chisnall 			if (isspace_l((unsigned char)c, locale))
1063c87aa1dSDavid Chisnall 				while (*buf != 0 &&
1073c87aa1dSDavid Chisnall 				       isspace_l((unsigned char)*buf, locale))
108d7641983SJoerg Wunsch 					buf++;
109d7641983SJoerg Wunsch 			else if (c != *buf++)
1103624c752SPedro F. Giffuni 				return (NULL);
111d7641983SJoerg Wunsch 			continue;
112d7641983SJoerg Wunsch 		}
113d7641983SJoerg Wunsch 
1141d6c9941SAndrey A. Chernov 		Ealternative = 0;
1151d6c9941SAndrey A. Chernov 		Oalternative = 0;
1161d6c9941SAndrey A. Chernov label:
117d7641983SJoerg Wunsch 		c = *ptr++;
118d7641983SJoerg Wunsch 		switch (c) {
119d7641983SJoerg Wunsch 		case '%':
120d7641983SJoerg Wunsch 			if (*buf++ != '%')
1213624c752SPedro F. Giffuni 				return (NULL);
122d7641983SJoerg Wunsch 			break;
123d7641983SJoerg Wunsch 
1241d6c9941SAndrey A. Chernov 		case '+':
1253c87aa1dSDavid Chisnall 			buf = _strptime(buf, tptr->date_fmt, tm, GMTp, locale);
1263624c752SPedro F. Giffuni 			if (buf == NULL)
1273624c752SPedro F. Giffuni 				return (NULL);
1282e465587SPedro F. Giffuni 			flags |= FLAG_WDAY | FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
129d7641983SJoerg Wunsch 			break;
130d7641983SJoerg Wunsch 
1311d6c9941SAndrey A. Chernov 		case 'C':
1323c87aa1dSDavid Chisnall 			if (!isdigit_l((unsigned char)*buf, locale))
1333624c752SPedro F. Giffuni 				return (NULL);
1341d6c9941SAndrey A. Chernov 
135398592ffSSheldon Hearn 			/* XXX This will break for 3-digit centuries. */
136398592ffSSheldon Hearn 			len = 2;
1373c87aa1dSDavid Chisnall 			for (i = 0; len && *buf != 0 &&
1383c87aa1dSDavid Chisnall 			     isdigit_l((unsigned char)*buf, locale); buf++) {
1391d6c9941SAndrey A. Chernov 				i *= 10;
1401d6c9941SAndrey A. Chernov 				i += *buf - '0';
141398592ffSSheldon Hearn 				len--;
1421d6c9941SAndrey A. Chernov 			}
1431d6c9941SAndrey A. Chernov 
144eb144aa0SYuri Pankov 			century = i;
1452e465587SPedro F. Giffuni 			flags |= FLAG_YEAR;
1462e465587SPedro F. Giffuni 
1471d6c9941SAndrey A. Chernov 			break;
1481d6c9941SAndrey A. Chernov 
149d7641983SJoerg Wunsch 		case 'c':
1503c87aa1dSDavid Chisnall 			buf = _strptime(buf, tptr->c_fmt, tm, GMTp, locale);
1513624c752SPedro F. Giffuni 			if (buf == NULL)
1523624c752SPedro F. Giffuni 				return (NULL);
1532e465587SPedro F. Giffuni 			flags |= FLAG_WDAY | FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
154d7641983SJoerg Wunsch 			break;
155d7641983SJoerg Wunsch 
156d7641983SJoerg Wunsch 		case 'D':
1573c87aa1dSDavid Chisnall 			buf = _strptime(buf, "%m/%d/%y", tm, GMTp, locale);
1583624c752SPedro F. Giffuni 			if (buf == NULL)
1593624c752SPedro F. Giffuni 				return (NULL);
1602e465587SPedro F. Giffuni 			flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
161d7641983SJoerg Wunsch 			break;
162d7641983SJoerg Wunsch 
1631d6c9941SAndrey A. Chernov 		case 'E':
164c63a4303SAndrey A. Chernov 			if (Ealternative || Oalternative)
165c63a4303SAndrey A. Chernov 				break;
1661d6c9941SAndrey A. Chernov 			Ealternative++;
1671d6c9941SAndrey A. Chernov 			goto label;
1681d6c9941SAndrey A. Chernov 
1691d6c9941SAndrey A. Chernov 		case 'O':
170c63a4303SAndrey A. Chernov 			if (Ealternative || Oalternative)
171c63a4303SAndrey A. Chernov 				break;
1721d6c9941SAndrey A. Chernov 			Oalternative++;
1731d6c9941SAndrey A. Chernov 			goto label;
1741d6c9941SAndrey A. Chernov 
175c63a4303SAndrey A. Chernov 		case 'F':
1763c87aa1dSDavid Chisnall 			buf = _strptime(buf, "%Y-%m-%d", tm, GMTp, locale);
1773624c752SPedro F. Giffuni 			if (buf == NULL)
1783624c752SPedro F. Giffuni 				return (NULL);
1792e465587SPedro F. Giffuni 			flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
180c63a4303SAndrey A. Chernov 			break;
181c63a4303SAndrey A. Chernov 
182d7641983SJoerg Wunsch 		case 'R':
1833c87aa1dSDavid Chisnall 			buf = _strptime(buf, "%H:%M", tm, GMTp, locale);
1843624c752SPedro F. Giffuni 			if (buf == NULL)
1853624c752SPedro F. Giffuni 				return (NULL);
186d7641983SJoerg Wunsch 			break;
187d7641983SJoerg Wunsch 
188d7641983SJoerg Wunsch 		case 'r':
1893c87aa1dSDavid Chisnall 			buf = _strptime(buf, tptr->ampm_fmt, tm, GMTp, locale);
1903624c752SPedro F. Giffuni 			if (buf == NULL)
1913624c752SPedro F. Giffuni 				return (NULL);
192d7641983SJoerg Wunsch 			break;
193d7641983SJoerg Wunsch 
194d7641983SJoerg Wunsch 		case 'T':
1953c87aa1dSDavid Chisnall 			buf = _strptime(buf, "%H:%M:%S", tm, GMTp, locale);
1963624c752SPedro F. Giffuni 			if (buf == NULL)
1973624c752SPedro F. Giffuni 				return (NULL);
198d7641983SJoerg Wunsch 			break;
199d7641983SJoerg Wunsch 
200d7641983SJoerg Wunsch 		case 'X':
2013c87aa1dSDavid Chisnall 			buf = _strptime(buf, tptr->X_fmt, tm, GMTp, locale);
2023624c752SPedro F. Giffuni 			if (buf == NULL)
2033624c752SPedro F. Giffuni 				return (NULL);
204d7641983SJoerg Wunsch 			break;
205d7641983SJoerg Wunsch 
206d7641983SJoerg Wunsch 		case 'x':
2073c87aa1dSDavid Chisnall 			buf = _strptime(buf, tptr->x_fmt, tm, GMTp, locale);
2083624c752SPedro F. Giffuni 			if (buf == NULL)
2093624c752SPedro F. Giffuni 				return (NULL);
2102e465587SPedro F. Giffuni 			flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
211d7641983SJoerg Wunsch 			break;
212d7641983SJoerg Wunsch 
213d7641983SJoerg Wunsch 		case 'j':
2143c87aa1dSDavid Chisnall 			if (!isdigit_l((unsigned char)*buf, locale))
2153624c752SPedro F. Giffuni 				return (NULL);
216d7641983SJoerg Wunsch 
217398592ffSSheldon Hearn 			len = 3;
2183c87aa1dSDavid Chisnall 			for (i = 0; len && *buf != 0 &&
2193c87aa1dSDavid Chisnall 			     isdigit_l((unsigned char)*buf, locale); buf++){
220d7641983SJoerg Wunsch 				i *= 10;
221d7641983SJoerg Wunsch 				i += *buf - '0';
222398592ffSSheldon Hearn 				len--;
223d7641983SJoerg Wunsch 			}
22433dbb0a6SSheldon Hearn 			if (i < 1 || i > 366)
2253624c752SPedro F. Giffuni 				return (NULL);
226d7641983SJoerg Wunsch 
22733dbb0a6SSheldon Hearn 			tm->tm_yday = i - 1;
2282e465587SPedro F. Giffuni 			flags |= FLAG_YDAY;
2292e465587SPedro F. Giffuni 
230d7641983SJoerg Wunsch 			break;
231d7641983SJoerg Wunsch 
232d7641983SJoerg Wunsch 		case 'M':
233d7641983SJoerg Wunsch 		case 'S':
2343c87aa1dSDavid Chisnall 			if (*buf == 0 ||
2353c87aa1dSDavid Chisnall 				isspace_l((unsigned char)*buf, locale))
236d7641983SJoerg Wunsch 				break;
237d7641983SJoerg Wunsch 
2383c87aa1dSDavid Chisnall 			if (!isdigit_l((unsigned char)*buf, locale))
2393624c752SPedro F. Giffuni 				return (NULL);
240d7641983SJoerg Wunsch 
241398592ffSSheldon Hearn 			len = 2;
2423c87aa1dSDavid Chisnall 			for (i = 0; len && *buf != 0 &&
2433c87aa1dSDavid Chisnall 				isdigit_l((unsigned char)*buf, locale); buf++){
244d7641983SJoerg Wunsch 				i *= 10;
245d7641983SJoerg Wunsch 				i += *buf - '0';
246398592ffSSheldon Hearn 				len--;
247d7641983SJoerg Wunsch 			}
24833dbb0a6SSheldon Hearn 
24933dbb0a6SSheldon Hearn 			if (c == 'M') {
250d7641983SJoerg Wunsch 				if (i > 59)
2513624c752SPedro F. Giffuni 					return (NULL);
252d7641983SJoerg Wunsch 				tm->tm_min = i;
25333dbb0a6SSheldon Hearn 			} else {
25433dbb0a6SSheldon Hearn 				if (i > 60)
2553624c752SPedro F. Giffuni 					return (NULL);
256d7641983SJoerg Wunsch 				tm->tm_sec = i;
25733dbb0a6SSheldon Hearn 			}
258d7641983SJoerg Wunsch 
259d7641983SJoerg Wunsch 			break;
260d7641983SJoerg Wunsch 
261d7641983SJoerg Wunsch 		case 'H':
262d7641983SJoerg Wunsch 		case 'I':
263d7641983SJoerg Wunsch 		case 'k':
264d7641983SJoerg Wunsch 		case 'l':
265398592ffSSheldon Hearn 			/*
26632a8ec8bSYuri Pankov 			 * %k and %l specifiers are documented as being
26732a8ec8bSYuri Pankov 			 * blank-padded.  However, there is no harm in
26832a8ec8bSYuri Pankov 			 * allowing zero-padding.
269398592ffSSheldon Hearn 			 *
27032a8ec8bSYuri Pankov 			 * XXX %k and %l specifiers may gobble one too many
271398592ffSSheldon Hearn 			 * digits if used incorrectly.
272398592ffSSheldon Hearn 			 */
27332a8ec8bSYuri Pankov 
27432a8ec8bSYuri Pankov 			len = 2;
27532a8ec8bSYuri Pankov 			if ((c == 'k' || c == 'l') &&
27632a8ec8bSYuri Pankov 			    isblank_l((unsigned char)*buf, locale)) {
27732a8ec8bSYuri Pankov 				buf++;
27832a8ec8bSYuri Pankov 				len = 1;
27932a8ec8bSYuri Pankov 			}
28032a8ec8bSYuri Pankov 
2813c87aa1dSDavid Chisnall 			if (!isdigit_l((unsigned char)*buf, locale))
2823624c752SPedro F. Giffuni 				return (NULL);
283d7641983SJoerg Wunsch 
2843c87aa1dSDavid Chisnall 			for (i = 0; len && *buf != 0 &&
2853c87aa1dSDavid Chisnall 			     isdigit_l((unsigned char)*buf, locale); buf++) {
286d7641983SJoerg Wunsch 				i *= 10;
287d7641983SJoerg Wunsch 				i += *buf - '0';
288398592ffSSheldon Hearn 				len--;
289d7641983SJoerg Wunsch 			}
290d7641983SJoerg Wunsch 			if (c == 'H' || c == 'k') {
291d7641983SJoerg Wunsch 				if (i > 23)
2923624c752SPedro F. Giffuni 					return (NULL);
293cbc00629SYuri Pankov 			} else if (i == 0 || i > 12)
2943624c752SPedro F. Giffuni 				return (NULL);
295d7641983SJoerg Wunsch 
296d7641983SJoerg Wunsch 			tm->tm_hour = i;
297d7641983SJoerg Wunsch 
298d7641983SJoerg Wunsch 			break;
299d7641983SJoerg Wunsch 
300d7641983SJoerg Wunsch 		case 'p':
301398592ffSSheldon Hearn 			/*
302398592ffSSheldon Hearn 			 * XXX This is bogus if parsed before hour-related
303398592ffSSheldon Hearn 			 * specifiers.
304398592ffSSheldon Hearn 			 */
305d7641983SJoerg Wunsch 			if (tm->tm_hour > 12)
3063624c752SPedro F. Giffuni 				return (NULL);
30762b939ebSAndrey A. Chernov 
30862b939ebSAndrey A. Chernov 			len = strlen(tptr->am);
30962b939ebSAndrey A. Chernov 			if (strncasecmp_l(buf, tptr->am, len, locale) == 0) {
310d7641983SJoerg Wunsch 				if (tm->tm_hour == 12)
311d7641983SJoerg Wunsch 					tm->tm_hour = 0;
312d7641983SJoerg Wunsch 				buf += len;
313d7641983SJoerg Wunsch 				break;
314d7641983SJoerg Wunsch 			}
315d7641983SJoerg Wunsch 
316930cd711SAlexey Zelkin 			len = strlen(tptr->pm);
3173c87aa1dSDavid Chisnall 			if (strncasecmp_l(buf, tptr->pm, len, locale) == 0) {
318d7641983SJoerg Wunsch 				if (tm->tm_hour != 12)
319d7641983SJoerg Wunsch 					tm->tm_hour += 12;
320d7641983SJoerg Wunsch 				buf += len;
321d7641983SJoerg Wunsch 				break;
322d7641983SJoerg Wunsch 			}
323d7641983SJoerg Wunsch 
3243624c752SPedro F. Giffuni 			return (NULL);
325d7641983SJoerg Wunsch 
326d7641983SJoerg Wunsch 		case 'A':
327d7641983SJoerg Wunsch 		case 'a':
328930cd711SAlexey Zelkin 			for (i = 0; i < asizeof(tptr->weekday); i++) {
329930cd711SAlexey Zelkin 				len = strlen(tptr->weekday[i]);
3303c87aa1dSDavid Chisnall 				if (strncasecmp_l(buf, tptr->weekday[i],
3313c87aa1dSDavid Chisnall 						len, locale) == 0)
332d7641983SJoerg Wunsch 					break;
333930cd711SAlexey Zelkin 				len = strlen(tptr->wday[i]);
3343c87aa1dSDavid Chisnall 				if (strncasecmp_l(buf, tptr->wday[i],
3353c87aa1dSDavid Chisnall 						len, locale) == 0)
336d7641983SJoerg Wunsch 					break;
337d7641983SJoerg Wunsch 			}
338930cd711SAlexey Zelkin 			if (i == asizeof(tptr->weekday))
3393624c752SPedro F. Giffuni 				return (NULL);
340d7641983SJoerg Wunsch 
3411a6804c6SPedro F. Giffuni 			buf += len;
342d7641983SJoerg Wunsch 			tm->tm_wday = i;
3432e465587SPedro F. Giffuni 			flags |= FLAG_WDAY;
344d7641983SJoerg Wunsch 			break;
345d7641983SJoerg Wunsch 
34633dbb0a6SSheldon Hearn 		case 'U':
34733dbb0a6SSheldon Hearn 		case 'W':
34833dbb0a6SSheldon Hearn 			/*
34933dbb0a6SSheldon Hearn 			 * XXX This is bogus, as we can not assume any valid
35033dbb0a6SSheldon Hearn 			 * information present in the tm structure at this
35133dbb0a6SSheldon Hearn 			 * point to calculate a real value, so just check the
35233dbb0a6SSheldon Hearn 			 * range for now.
35333dbb0a6SSheldon Hearn 			 */
3543c87aa1dSDavid Chisnall 			if (!isdigit_l((unsigned char)*buf, locale))
3553624c752SPedro F. Giffuni 				return (NULL);
35633dbb0a6SSheldon Hearn 
357398592ffSSheldon Hearn 			len = 2;
3583c87aa1dSDavid Chisnall 			for (i = 0; len && *buf != 0 &&
3593c87aa1dSDavid Chisnall 			     isdigit_l((unsigned char)*buf, locale); buf++) {
36033dbb0a6SSheldon Hearn 				i *= 10;
36133dbb0a6SSheldon Hearn 				i += *buf - '0';
362398592ffSSheldon Hearn 				len--;
36333dbb0a6SSheldon Hearn 			}
36433dbb0a6SSheldon Hearn 			if (i > 53)
3653624c752SPedro F. Giffuni 				return (NULL);
36633dbb0a6SSheldon Hearn 
3672e465587SPedro F. Giffuni 			if (c == 'U')
3682e465587SPedro F. Giffuni 				day_offset = TM_SUNDAY;
3692e465587SPedro F. Giffuni 			else
3702e465587SPedro F. Giffuni 				day_offset = TM_MONDAY;
3712e465587SPedro F. Giffuni 
3722e465587SPedro F. Giffuni 
3732e465587SPedro F. Giffuni 			week_offset = i;
3742e465587SPedro F. Giffuni 
37533dbb0a6SSheldon Hearn 			break;
37633dbb0a6SSheldon Hearn 
37762b939ebSAndrey A. Chernov 		case 'u':
37833dbb0a6SSheldon Hearn 		case 'w':
3793c87aa1dSDavid Chisnall 			if (!isdigit_l((unsigned char)*buf, locale))
3803624c752SPedro F. Giffuni 				return (NULL);
38133dbb0a6SSheldon Hearn 
38262b939ebSAndrey A. Chernov 			i = *buf++ - '0';
38362b939ebSAndrey A. Chernov 			if (i < 0 || i > 7 || (c == 'u' && i < 1) ||
38462b939ebSAndrey A. Chernov 			    (c == 'w' && i > 6))
3853624c752SPedro F. Giffuni 				return (NULL);
38633dbb0a6SSheldon Hearn 
38762b939ebSAndrey A. Chernov 			tm->tm_wday = i % 7;
3882e465587SPedro F. Giffuni 			flags |= FLAG_WDAY;
38933dbb0a6SSheldon Hearn 
39033dbb0a6SSheldon Hearn 			break;
39133dbb0a6SSheldon Hearn 
392d7641983SJoerg Wunsch 		case 'e':
393398592ffSSheldon Hearn 			/*
394cd9dcb03SPedro F. Giffuni 			 * With %e format, our strftime(3) adds a blank space
395cd9dcb03SPedro F. Giffuni 			 * before single digits.
396cd9dcb03SPedro F. Giffuni 			 */
397cd9dcb03SPedro F. Giffuni 			if (*buf != 0 &&
398cd9dcb03SPedro F. Giffuni 			    isspace_l((unsigned char)*buf, locale))
399cd9dcb03SPedro F. Giffuni 			       buf++;
400cd9dcb03SPedro F. Giffuni 			/* FALLTHROUGH */
401cd9dcb03SPedro F. Giffuni 		case 'd':
402cd9dcb03SPedro F. Giffuni 			/*
403cd9dcb03SPedro F. Giffuni 			 * The %e specifier was once explicitly documented as
404cd9dcb03SPedro F. Giffuni 			 * not being zero-padded but was later changed to
405cd9dcb03SPedro F. Giffuni 			 * equivalent to %d.  There is no harm in allowing
406398592ffSSheldon Hearn 			 * such padding.
407398592ffSSheldon Hearn 			 *
408398592ffSSheldon Hearn 			 * XXX The %e specifier may gobble one too many
409398592ffSSheldon Hearn 			 * digits if used incorrectly.
410398592ffSSheldon Hearn 			 */
4113c87aa1dSDavid Chisnall 			if (!isdigit_l((unsigned char)*buf, locale))
4123624c752SPedro F. Giffuni 				return (NULL);
413d7641983SJoerg Wunsch 
414398592ffSSheldon Hearn 			len = 2;
4153c87aa1dSDavid Chisnall 			for (i = 0; len && *buf != 0 &&
4163c87aa1dSDavid Chisnall 			     isdigit_l((unsigned char)*buf, locale); buf++) {
417d7641983SJoerg Wunsch 				i *= 10;
418d7641983SJoerg Wunsch 				i += *buf - '0';
419398592ffSSheldon Hearn 				len--;
420d7641983SJoerg Wunsch 			}
42192cbfb63SKonstantin Belousov 			if (i == 0 || i > 31)
4223624c752SPedro F. Giffuni 				return (NULL);
423d7641983SJoerg Wunsch 
424d7641983SJoerg Wunsch 			tm->tm_mday = i;
4252e465587SPedro F. Giffuni 			flags |= FLAG_MDAY;
426d7641983SJoerg Wunsch 
427d7641983SJoerg Wunsch 			break;
428d7641983SJoerg Wunsch 
429d7641983SJoerg Wunsch 		case 'B':
430d7641983SJoerg Wunsch 		case 'b':
431d7641983SJoerg Wunsch 		case 'h':
432930cd711SAlexey Zelkin 			for (i = 0; i < asizeof(tptr->month); i++) {
4331d6c9941SAndrey A. Chernov 				if (Oalternative) {
4341d6c9941SAndrey A. Chernov 					if (c == 'B') {
435930cd711SAlexey Zelkin 						len = strlen(tptr->alt_month[i]);
4363c87aa1dSDavid Chisnall 						if (strncasecmp_l(buf,
437930cd711SAlexey Zelkin 								tptr->alt_month[i],
4383c87aa1dSDavid Chisnall 								len, locale) == 0)
4391d6c9941SAndrey A. Chernov 							break;
4401d6c9941SAndrey A. Chernov 					}
4411d6c9941SAndrey A. Chernov 				} else {
442930cd711SAlexey Zelkin 					len = strlen(tptr->month[i]);
4433c87aa1dSDavid Chisnall 					if (strncasecmp_l(buf, tptr->month[i],
4443c87aa1dSDavid Chisnall 							len, locale) == 0)
445d7641983SJoerg Wunsch 						break;
4463d74e220SEdwin Groothuis 				}
4473d74e220SEdwin Groothuis 			}
4483d74e220SEdwin Groothuis 			/*
4493d74e220SEdwin Groothuis 			 * Try the abbreviated month name if the full name
4503d74e220SEdwin Groothuis 			 * wasn't found and Oalternative was not requested.
4513d74e220SEdwin Groothuis 			 */
4523d74e220SEdwin Groothuis 			if (i == asizeof(tptr->month) && !Oalternative) {
4533d74e220SEdwin Groothuis 				for (i = 0; i < asizeof(tptr->month); i++) {
454930cd711SAlexey Zelkin 					len = strlen(tptr->mon[i]);
4553c87aa1dSDavid Chisnall 					if (strncasecmp_l(buf, tptr->mon[i],
4563c87aa1dSDavid Chisnall 							len, locale) == 0)
457d7641983SJoerg Wunsch 						break;
458d7641983SJoerg Wunsch 				}
4591d6c9941SAndrey A. Chernov 			}
460930cd711SAlexey Zelkin 			if (i == asizeof(tptr->month))
4613624c752SPedro F. Giffuni 				return (NULL);
462d7641983SJoerg Wunsch 
463d7641983SJoerg Wunsch 			tm->tm_mon = i;
464d7641983SJoerg Wunsch 			buf += len;
4652e465587SPedro F. Giffuni 			flags |= FLAG_MONTH;
4662e465587SPedro F. Giffuni 
467d7641983SJoerg Wunsch 			break;
468d7641983SJoerg Wunsch 
469d7641983SJoerg Wunsch 		case 'm':
4703c87aa1dSDavid Chisnall 			if (!isdigit_l((unsigned char)*buf, locale))
4713624c752SPedro F. Giffuni 				return (NULL);
472d7641983SJoerg Wunsch 
473398592ffSSheldon Hearn 			len = 2;
4743c87aa1dSDavid Chisnall 			for (i = 0; len && *buf != 0 &&
4753c87aa1dSDavid Chisnall 			     isdigit_l((unsigned char)*buf, locale); buf++) {
476d7641983SJoerg Wunsch 				i *= 10;
477d7641983SJoerg Wunsch 				i += *buf - '0';
478398592ffSSheldon Hearn 				len--;
479d7641983SJoerg Wunsch 			}
480d7641983SJoerg Wunsch 			if (i < 1 || i > 12)
4813624c752SPedro F. Giffuni 				return (NULL);
482d7641983SJoerg Wunsch 
483d7641983SJoerg Wunsch 			tm->tm_mon = i - 1;
4842e465587SPedro F. Giffuni 			flags |= FLAG_MONTH;
485d7641983SJoerg Wunsch 
486d7641983SJoerg Wunsch 			break;
487d7641983SJoerg Wunsch 
488427fc5edSDima Dorfman 		case 's':
489427fc5edSDima Dorfman 			{
490427fc5edSDima Dorfman 			char *cp;
491c91e947dSJacques Vidrine 			int sverrno;
492c91e947dSJacques Vidrine 			long n;
493427fc5edSDima Dorfman 			time_t t;
494427fc5edSDima Dorfman 
495c91e947dSJacques Vidrine 			sverrno = errno;
496c91e947dSJacques Vidrine 			errno = 0;
4973c87aa1dSDavid Chisnall 			n = strtol_l(buf, &cp, 10, locale);
498c91e947dSJacques Vidrine 			if (errno == ERANGE || (long)(t = n) != n) {
499c91e947dSJacques Vidrine 				errno = sverrno;
5003624c752SPedro F. Giffuni 				return (NULL);
501c91e947dSJacques Vidrine 			}
502c91e947dSJacques Vidrine 			errno = sverrno;
503427fc5edSDima Dorfman 			buf = cp;
504eff6f444SPedro F. Giffuni 			if (gmtime_r(&t, tm) == NULL)
505eff6f444SPedro F. Giffuni 				return (NULL);
506fe71e0b8SMike Makonnen 			*GMTp = 1;
507eff6f444SPedro F. Giffuni 			flags |= FLAG_YDAY | FLAG_WDAY | FLAG_MONTH |
508eff6f444SPedro F. Giffuni 			    FLAG_MDAY | FLAG_YEAR;
509427fc5edSDima Dorfman 			}
510427fc5edSDima Dorfman 			break;
511427fc5edSDima Dorfman 
512d7641983SJoerg Wunsch 		case 'Y':
513d7641983SJoerg Wunsch 		case 'y':
5143c87aa1dSDavid Chisnall 			if (*buf == 0 ||
5153c87aa1dSDavid Chisnall 			    isspace_l((unsigned char)*buf, locale))
516d7641983SJoerg Wunsch 				break;
517d7641983SJoerg Wunsch 
5183c87aa1dSDavid Chisnall 			if (!isdigit_l((unsigned char)*buf, locale))
5193624c752SPedro F. Giffuni 				return (NULL);
520d7641983SJoerg Wunsch 
521398592ffSSheldon Hearn 			len = (c == 'Y') ? 4 : 2;
5223c87aa1dSDavid Chisnall 			for (i = 0; len && *buf != 0 &&
5233c87aa1dSDavid Chisnall 			     isdigit_l((unsigned char)*buf, locale); buf++) {
524d7641983SJoerg Wunsch 				i *= 10;
525d7641983SJoerg Wunsch 				i += *buf - '0';
526398592ffSSheldon Hearn 				len--;
527d7641983SJoerg Wunsch 			}
528d7641983SJoerg Wunsch 			if (c == 'Y')
529eb144aa0SYuri Pankov 				century = i / 100;
530eb144aa0SYuri Pankov 			year = i % 100;
531d7641983SJoerg Wunsch 
5322e465587SPedro F. Giffuni 			flags |= FLAG_YEAR;
533d7641983SJoerg Wunsch 
534d7641983SJoerg Wunsch 			break;
535b47f20dfSDavid E. O'Brien 
536b47f20dfSDavid E. O'Brien 		case 'Z':
537b47f20dfSDavid E. O'Brien 			{
538b47f20dfSDavid E. O'Brien 			const char *cp;
539b47f20dfSDavid E. O'Brien 			char *zonestr;
540b47f20dfSDavid E. O'Brien 
5413c87aa1dSDavid Chisnall 			for (cp = buf; *cp &&
5423c87aa1dSDavid Chisnall 			     isupper_l((unsigned char)*cp, locale); ++cp) {
5433c87aa1dSDavid Chisnall 				/*empty*/}
544b47f20dfSDavid E. O'Brien 			if (cp - buf) {
545b47f20dfSDavid E. O'Brien 				zonestr = alloca(cp - buf + 1);
546b47f20dfSDavid E. O'Brien 				strncpy(zonestr, buf, cp - buf);
547b47f20dfSDavid E. O'Brien 				zonestr[cp - buf] = '\0';
548b47f20dfSDavid E. O'Brien 				tzset();
549420bc6ceSAndrey A. Chernov 				if (0 == strcmp(zonestr, "GMT") ||
550420bc6ceSAndrey A. Chernov 				    0 == strcmp(zonestr, "UTC")) {
551fe71e0b8SMike Makonnen 				    *GMTp = 1;
552b47f20dfSDavid E. O'Brien 				} else if (0 == strcmp(zonestr, tzname[0])) {
553b47f20dfSDavid E. O'Brien 				    tm->tm_isdst = 0;
554b47f20dfSDavid E. O'Brien 				} else if (0 == strcmp(zonestr, tzname[1])) {
555b47f20dfSDavid E. O'Brien 				    tm->tm_isdst = 1;
556b47f20dfSDavid E. O'Brien 				} else {
5573624c752SPedro F. Giffuni 				    return (NULL);
558b47f20dfSDavid E. O'Brien 				}
559b47f20dfSDavid E. O'Brien 				buf += cp - buf;
560b47f20dfSDavid E. O'Brien 			}
561b47f20dfSDavid E. O'Brien 			}
562b47f20dfSDavid E. O'Brien 			break;
56340523da7SXin LI 
56440523da7SXin LI 		case 'z':
56540523da7SXin LI 			{
56640523da7SXin LI 			int sign = 1;
56740523da7SXin LI 
56840523da7SXin LI 			if (*buf != '+') {
56940523da7SXin LI 				if (*buf == '-')
57040523da7SXin LI 					sign = -1;
57140523da7SXin LI 				else
5723624c752SPedro F. Giffuni 					return (NULL);
57340523da7SXin LI 			}
57440523da7SXin LI 
57540523da7SXin LI 			buf++;
57640523da7SXin LI 			i = 0;
57740523da7SXin LI 			for (len = 4; len > 0; len--) {
5783c87aa1dSDavid Chisnall 				if (isdigit_l((unsigned char)*buf, locale)) {
57940523da7SXin LI 					i *= 10;
58040523da7SXin LI 					i += *buf - '0';
58140523da7SXin LI 					buf++;
5825fca7e1fSAndrey A. Chernov 				} else if (len == 2) {
5835fca7e1fSAndrey A. Chernov 					i *= 100;
5845fca7e1fSAndrey A. Chernov 					break;
58540523da7SXin LI 				} else
5863624c752SPedro F. Giffuni 					return (NULL);
58740523da7SXin LI 			}
58840523da7SXin LI 
5895fca7e1fSAndrey A. Chernov 			if (i > 1400 || (sign == -1 && i > 1200) ||
5905fca7e1fSAndrey A. Chernov 			    (i % 100) >= 60)
5915fca7e1fSAndrey A. Chernov 				return (NULL);
59240523da7SXin LI 			tm->tm_hour -= sign * (i / 100);
59340523da7SXin LI 			tm->tm_min  -= sign * (i % 100);
59440523da7SXin LI 			*GMTp = 1;
59540523da7SXin LI 			}
59640523da7SXin LI 			break;
597cd9dcb03SPedro F. Giffuni 
598cd9dcb03SPedro F. Giffuni 		case 'n':
599cd9dcb03SPedro F. Giffuni 		case 't':
600cd9dcb03SPedro F. Giffuni 			while (isspace_l((unsigned char)*buf, locale))
601cd9dcb03SPedro F. Giffuni 				buf++;
602cd9dcb03SPedro F. Giffuni 			break;
603097c578cSAndrey A. Chernov 
604097c578cSAndrey A. Chernov 		default:
605097c578cSAndrey A. Chernov 			return (NULL);
606d7641983SJoerg Wunsch 		}
607d7641983SJoerg Wunsch 	}
6082e465587SPedro F. Giffuni 
609eb144aa0SYuri Pankov 	if (century != -1 || year != -1) {
610eb144aa0SYuri Pankov 		if (year == -1)
611eb144aa0SYuri Pankov 			year = 0;
612eb144aa0SYuri Pankov 		if (century == -1) {
613eb144aa0SYuri Pankov 			if (year < 69)
614eb144aa0SYuri Pankov 				year += 100;
615eb144aa0SYuri Pankov 		} else
616eb144aa0SYuri Pankov 			year += century * 100 - TM_YEAR_BASE;
617eb144aa0SYuri Pankov 		tm->tm_year = year;
618eb144aa0SYuri Pankov 	}
619eb144aa0SYuri Pankov 
6202e465587SPedro F. Giffuni 	if (!(flags & FLAG_YDAY) && (flags & FLAG_YEAR)) {
6212e465587SPedro F. Giffuni 		if ((flags & (FLAG_MONTH | FLAG_MDAY)) ==
6222e465587SPedro F. Giffuni 		    (FLAG_MONTH | FLAG_MDAY)) {
6232e465587SPedro F. Giffuni 			tm->tm_yday = start_of_month[isleap(tm->tm_year +
6242e465587SPedro F. Giffuni 			    TM_YEAR_BASE)][tm->tm_mon] + (tm->tm_mday - 1);
6252e465587SPedro F. Giffuni 			flags |= FLAG_YDAY;
6262e465587SPedro F. Giffuni 		} else if (day_offset != -1) {
62762b939ebSAndrey A. Chernov 			int tmpwday, tmpyday, fwo;
62862b939ebSAndrey A. Chernov 
62962b939ebSAndrey A. Chernov 			fwo = first_wday_of(tm->tm_year + TM_YEAR_BASE);
63062b939ebSAndrey A. Chernov 			/* No incomplete week (week 0). */
63162b939ebSAndrey A. Chernov 			if (week_offset == 0 && fwo == day_offset)
63262b939ebSAndrey A. Chernov 				return (NULL);
63362b939ebSAndrey A. Chernov 
6342e465587SPedro F. Giffuni 			/* Set the date to the first Sunday (or Monday)
6352e465587SPedro F. Giffuni 			 * of the specified week of the year.
6362e465587SPedro F. Giffuni 			 */
63762b939ebSAndrey A. Chernov 			tmpwday = (flags & FLAG_WDAY) ? tm->tm_wday :
63862b939ebSAndrey A. Chernov 			    day_offset;
63962b939ebSAndrey A. Chernov 			tmpyday = (7 - fwo + day_offset) % 7 +
64062b939ebSAndrey A. Chernov 			    (week_offset - 1) * 7 +
64162b939ebSAndrey A. Chernov 			    (tmpwday - day_offset + 7) % 7;
64262b939ebSAndrey A. Chernov 			/* Impossible yday for incomplete week (week 0). */
64362b939ebSAndrey A. Chernov 			if (tmpyday < 0) {
64462b939ebSAndrey A. Chernov 				if (flags & FLAG_WDAY)
64562b939ebSAndrey A. Chernov 					return (NULL);
64662b939ebSAndrey A. Chernov 				tmpyday = 0;
6472e465587SPedro F. Giffuni 			}
64862b939ebSAndrey A. Chernov 			tm->tm_yday = tmpyday;
6492e465587SPedro F. Giffuni 			flags |= FLAG_YDAY;
6502e465587SPedro F. Giffuni 		}
651ce1d331eSPedro F. Giffuni 	}
652cb7a4779SDavid E. O'Brien 
6532e465587SPedro F. Giffuni 	if ((flags & (FLAG_YEAR | FLAG_YDAY)) == (FLAG_YEAR | FLAG_YDAY)) {
6542e465587SPedro F. Giffuni 		if (!(flags & FLAG_MONTH)) {
6552e465587SPedro F. Giffuni 			i = 0;
6562e465587SPedro F. Giffuni 			while (tm->tm_yday >=
6572e465587SPedro F. Giffuni 			    start_of_month[isleap(tm->tm_year +
6582e465587SPedro F. Giffuni 			    TM_YEAR_BASE)][i])
6592e465587SPedro F. Giffuni 				i++;
6602e465587SPedro F. Giffuni 			if (i > 12) {
6612e465587SPedro F. Giffuni 				i = 1;
6622e465587SPedro F. Giffuni 				tm->tm_yday -=
6632e465587SPedro F. Giffuni 				    start_of_month[isleap(tm->tm_year +
6642e465587SPedro F. Giffuni 				    TM_YEAR_BASE)][12];
6652e465587SPedro F. Giffuni 				tm->tm_year++;
6662e465587SPedro F. Giffuni 			}
6672e465587SPedro F. Giffuni 			tm->tm_mon = i - 1;
6682e465587SPedro F. Giffuni 			flags |= FLAG_MONTH;
6692e465587SPedro F. Giffuni 		}
6702e465587SPedro F. Giffuni 		if (!(flags & FLAG_MDAY)) {
6712e465587SPedro F. Giffuni 			tm->tm_mday = tm->tm_yday -
6722e465587SPedro F. Giffuni 			    start_of_month[isleap(tm->tm_year + TM_YEAR_BASE)]
6732e465587SPedro F. Giffuni 			    [tm->tm_mon] + 1;
6742e465587SPedro F. Giffuni 			flags |= FLAG_MDAY;
6752e465587SPedro F. Giffuni 		}
6762e465587SPedro F. Giffuni 		if (!(flags & FLAG_WDAY)) {
6772e465587SPedro F. Giffuni 			i = 0;
6782e465587SPedro F. Giffuni 			wday_offset = first_wday_of(tm->tm_year);
6792e465587SPedro F. Giffuni 			while (i++ <= tm->tm_yday) {
6802e465587SPedro F. Giffuni 				if (wday_offset++ >= 6)
6812e465587SPedro F. Giffuni 					wday_offset = 0;
6822e465587SPedro F. Giffuni 			}
6832e465587SPedro F. Giffuni 			tm->tm_wday = wday_offset;
6842e465587SPedro F. Giffuni 			flags |= FLAG_WDAY;
6852e465587SPedro F. Giffuni 		}
6862e465587SPedro F. Giffuni 	}
6872e465587SPedro F. Giffuni 
6882e465587SPedro F. Giffuni 	return ((char *)buf);
6892e465587SPedro F. Giffuni }
690c1cd488cSPedro F. Giffuni 
691cb7a4779SDavid E. O'Brien char *
strptime_l(const char * __restrict buf,const char * __restrict fmt,struct tm * __restrict tm,locale_t loc)6923c87aa1dSDavid Chisnall strptime_l(const char * __restrict buf, const char * __restrict fmt,
6933c87aa1dSDavid Chisnall     struct tm * __restrict tm, locale_t loc)
694cb7a4779SDavid E. O'Brien {
695cb7a4779SDavid E. O'Brien 	char *ret;
696fe71e0b8SMike Makonnen 	int gmt;
6973c87aa1dSDavid Chisnall 	FIX_LOCALE(loc);
698cb7a4779SDavid E. O'Brien 
699fe71e0b8SMike Makonnen 	gmt = 0;
7003c87aa1dSDavid Chisnall 	ret = _strptime(buf, fmt, tm, &gmt, loc);
7016c688436SMike Makonnen 	if (ret && gmt) {
7026c688436SMike Makonnen 		time_t t = timegm(tm);
703420bc6ceSAndrey A. Chernov 
704b47f20dfSDavid E. O'Brien 		localtime_r(&t, tm);
705b47f20dfSDavid E. O'Brien 	}
706cb7a4779SDavid E. O'Brien 
707fe71e0b8SMike Makonnen 	return (ret);
708d7641983SJoerg Wunsch }
7092e465587SPedro F. Giffuni 
7103c87aa1dSDavid Chisnall char *
strptime(const char * __restrict buf,const char * __restrict fmt,struct tm * __restrict tm)7113c87aa1dSDavid Chisnall strptime(const char * __restrict buf, const char * __restrict fmt,
7123c87aa1dSDavid Chisnall     struct tm * __restrict tm)
7133c87aa1dSDavid Chisnall {
7143c87aa1dSDavid Chisnall 	return strptime_l(buf, fmt, tm, __get_locale());
7153c87aa1dSDavid Chisnall }
716