xref: /freebsd/lib/libc/stdtime/strptime.c (revision 420bc6ce)
10413bfcaSPedro F. Giffuni /*-
2cd9dcb03SPedro F. Giffuni  * Copyright (c) 2014 Gary Mills
3cd9dcb03SPedro F. Giffuni  * Copyright 2011, Nexenta Systems, Inc.  All rights reserved.
4d7641983SJoerg Wunsch  * Copyright (c) 1994 Powerdog Industries.  All rights reserved.
5d7641983SJoerg Wunsch  *
63c87aa1dSDavid Chisnall  * Copyright (c) 2011 The FreeBSD Foundation
73c87aa1dSDavid Chisnall  * All rights reserved.
83c87aa1dSDavid Chisnall  * Portions of this software were developed by David Chisnall
93c87aa1dSDavid Chisnall  * under sponsorship from the FreeBSD Foundation.
103c87aa1dSDavid Chisnall  *
1137486f03SJoerg Wunsch  * Redistribution and use in source and binary forms, with or without
12d7641983SJoerg Wunsch  * modification, are permitted provided that the following conditions
13d7641983SJoerg Wunsch  * are met:
14d7641983SJoerg Wunsch  * 1. Redistributions of source code must retain the above copyright
15d7641983SJoerg Wunsch  *    notice, this list of conditions and the following disclaimer.
16d7641983SJoerg Wunsch  * 2. Redistributions in binary form must reproduce the above copyright
17d7641983SJoerg Wunsch  *    notice, this list of conditions and the following disclaimer
18d7641983SJoerg Wunsch  *    in the documentation and/or other materials provided with the
19d7641983SJoerg Wunsch  *    distribution.
20d7641983SJoerg Wunsch  *
21d7641983SJoerg Wunsch  * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY
22d7641983SJoerg Wunsch  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23d7641983SJoerg Wunsch  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24d7641983SJoerg Wunsch  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE
25d7641983SJoerg Wunsch  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26d7641983SJoerg Wunsch  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27d7641983SJoerg Wunsch  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28d7641983SJoerg Wunsch  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29d7641983SJoerg Wunsch  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
30d7641983SJoerg Wunsch  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
31d7641983SJoerg Wunsch  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
320413bfcaSPedro F. Giffuni  *
330413bfcaSPedro F. Giffuni  * The views and conclusions contained in the software and documentation
340413bfcaSPedro F. Giffuni  * are those of the authors and should not be interpreted as representing
350413bfcaSPedro F. Giffuni  * official policies, either expressed or implied, of Powerdog Industries.
36d7641983SJoerg Wunsch  */
37d7641983SJoerg Wunsch 
38e0554a53SJacques Vidrine #include <sys/cdefs.h>
39d7641983SJoerg Wunsch #ifndef lint
4037486f03SJoerg Wunsch #ifndef NOID
41e0554a53SJacques Vidrine static char copyright[] __unused =
42d7641983SJoerg Wunsch "@(#) Copyright (c) 1994 Powerdog Industries.  All rights reserved.";
43e0554a53SJacques Vidrine static char sccsid[] __unused = "@(#)strptime.c	0.1 (Powerdog) 94/03/27";
4437486f03SJoerg Wunsch #endif /* !defined NOID */
45d7641983SJoerg Wunsch #endif /* not lint */
46333fc21eSDavid E. O'Brien __FBSDID("$FreeBSD$");
47d7641983SJoerg Wunsch 
48d201fe46SDaniel Eischen #include "namespace.h"
49d7641983SJoerg Wunsch #include <time.h>
50d7641983SJoerg Wunsch #include <ctype.h>
51c91e947dSJacques Vidrine #include <errno.h>
52427fc5edSDima Dorfman #include <stdlib.h>
53d7641983SJoerg Wunsch #include <string.h>
54cb7a4779SDavid E. O'Brien #include <pthread.h>
55d201fe46SDaniel Eischen #include "un-namespace.h"
56d201fe46SDaniel Eischen #include "libc_private.h"
5737486f03SJoerg Wunsch #include "timelocal.h"
582e465587SPedro F. Giffuni #include "tzfile.h"
591a6804c6SPedro F. Giffuni 
603c87aa1dSDavid Chisnall static char * _strptime(const char *, const char *, struct tm *, int *, locale_t);
61cb7a4779SDavid E. O'Brien 
62d7641983SJoerg Wunsch #define	asizeof(a)	(sizeof(a) / sizeof((a)[0]))
63d7641983SJoerg Wunsch 
642e465587SPedro F. Giffuni #define	FLAG_NONE	(1 << 0)
652e465587SPedro F. Giffuni #define	FLAG_YEAR	(1 << 1)
662e465587SPedro F. Giffuni #define	FLAG_MONTH	(1 << 2)
672e465587SPedro F. Giffuni #define	FLAG_YDAY	(1 << 3)
682e465587SPedro F. Giffuni #define	FLAG_MDAY	(1 << 4)
692e465587SPedro F. Giffuni #define	FLAG_WDAY	(1 << 5)
702e465587SPedro F. Giffuni 
712e465587SPedro F. Giffuni /*
722e465587SPedro F. Giffuni  * Calculate the week day of the first day of a year. Valid for
732e465587SPedro F. Giffuni  * the Gregorian calendar, which began Sept 14, 1752 in the UK
742e465587SPedro F. Giffuni  * and its colonies. Ref:
752e465587SPedro F. Giffuni  * http://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week
762e465587SPedro F. Giffuni  */
772e465587SPedro F. Giffuni 
782e465587SPedro F. Giffuni static int
792e465587SPedro F. Giffuni first_wday_of(int year)
802e465587SPedro F. Giffuni {
812e465587SPedro F. Giffuni 	return (((2 * (3 - (year / 100) % 4)) + (year % 100) +
822e465587SPedro F. Giffuni 		((year % 100) / 4) + (isleap(year) ? 6 : 0) + 1) % 7);
832e465587SPedro F. Giffuni }
842e465587SPedro F. Giffuni 
85cb7a4779SDavid E. O'Brien static char *
863c87aa1dSDavid Chisnall _strptime(const char *buf, const char *fmt, struct tm *tm, int *GMTp,
873c87aa1dSDavid Chisnall 		locale_t locale)
88d7641983SJoerg Wunsch {
8937486f03SJoerg Wunsch 	char	c;
9037486f03SJoerg Wunsch 	const char *ptr;
912e465587SPedro F. Giffuni 	int	day_offset = -1, wday_offset;
922e465587SPedro F. Giffuni 	int week_offset;
933624c752SPedro F. Giffuni 	int	i, len;
942e465587SPedro F. Giffuni 	int flags;
951d6c9941SAndrey A. Chernov 	int Ealternative, Oalternative;
962e465587SPedro F. Giffuni 	const struct lc_time_T *tptr = __get_current_time_locale(locale);
972e465587SPedro F. Giffuni 	static int start_of_month[2][13] = {
982e465587SPedro F. Giffuni 		{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
992e465587SPedro F. Giffuni 		{0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}
1002e465587SPedro F. Giffuni 	};
1012e465587SPedro F. Giffuni 
1022e465587SPedro F. Giffuni 	flags = FLAG_NONE;
103d7641983SJoerg Wunsch 
104d7641983SJoerg Wunsch 	ptr = fmt;
105d7641983SJoerg Wunsch 	while (*ptr != 0) {
106d7641983SJoerg Wunsch 		if (*buf == 0)
107d7641983SJoerg Wunsch 			break;
108d7641983SJoerg Wunsch 
109d7641983SJoerg Wunsch 		c = *ptr++;
110d7641983SJoerg Wunsch 
111d7641983SJoerg Wunsch 		if (c != '%') {
1123c87aa1dSDavid Chisnall 			if (isspace_l((unsigned char)c, locale))
1133c87aa1dSDavid Chisnall 				while (*buf != 0 &&
1143c87aa1dSDavid Chisnall 				       isspace_l((unsigned char)*buf, locale))
115d7641983SJoerg Wunsch 					buf++;
116d7641983SJoerg Wunsch 			else if (c != *buf++)
1173624c752SPedro F. Giffuni 				return (NULL);
118d7641983SJoerg Wunsch 			continue;
119d7641983SJoerg Wunsch 		}
120d7641983SJoerg Wunsch 
1211d6c9941SAndrey A. Chernov 		Ealternative = 0;
1221d6c9941SAndrey A. Chernov 		Oalternative = 0;
1231d6c9941SAndrey A. Chernov label:
124d7641983SJoerg Wunsch 		c = *ptr++;
125d7641983SJoerg Wunsch 		switch (c) {
126d7641983SJoerg Wunsch 		case 0:
127d7641983SJoerg Wunsch 		case '%':
128d7641983SJoerg Wunsch 			if (*buf++ != '%')
1293624c752SPedro F. Giffuni 				return (NULL);
130d7641983SJoerg Wunsch 			break;
131d7641983SJoerg Wunsch 
1321d6c9941SAndrey A. Chernov 		case '+':
1333c87aa1dSDavid Chisnall 			buf = _strptime(buf, tptr->date_fmt, tm, GMTp, locale);
1343624c752SPedro F. Giffuni 			if (buf == NULL)
1353624c752SPedro F. Giffuni 				return (NULL);
1362e465587SPedro F. Giffuni 			flags |= FLAG_WDAY | FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
137d7641983SJoerg Wunsch 			break;
138d7641983SJoerg Wunsch 
1391d6c9941SAndrey A. Chernov 		case 'C':
1403c87aa1dSDavid Chisnall 			if (!isdigit_l((unsigned char)*buf, locale))
1413624c752SPedro F. Giffuni 				return (NULL);
1421d6c9941SAndrey A. Chernov 
143398592ffSSheldon Hearn 			/* XXX This will break for 3-digit centuries. */
144398592ffSSheldon Hearn 			len = 2;
1453c87aa1dSDavid Chisnall 			for (i = 0; len && *buf != 0 &&
1463c87aa1dSDavid Chisnall 			     isdigit_l((unsigned char)*buf, locale); buf++) {
1471d6c9941SAndrey A. Chernov 				i *= 10;
1481d6c9941SAndrey A. Chernov 				i += *buf - '0';
149398592ffSSheldon Hearn 				len--;
1501d6c9941SAndrey A. Chernov 			}
1511d6c9941SAndrey A. Chernov 			if (i < 19)
1523624c752SPedro F. Giffuni 				return (NULL);
1531d6c9941SAndrey A. Chernov 
1542e465587SPedro F. Giffuni 			tm->tm_year = i * 100 - TM_YEAR_BASE;
1552e465587SPedro F. Giffuni 			flags |= FLAG_YEAR;
1562e465587SPedro F. Giffuni 
1571d6c9941SAndrey A. Chernov 			break;
1581d6c9941SAndrey A. Chernov 
159d7641983SJoerg Wunsch 		case 'c':
1603c87aa1dSDavid Chisnall 			buf = _strptime(buf, tptr->c_fmt, tm, GMTp, locale);
1613624c752SPedro F. Giffuni 			if (buf == NULL)
1623624c752SPedro F. Giffuni 				return (NULL);
1632e465587SPedro F. Giffuni 			flags |= FLAG_WDAY | FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
164d7641983SJoerg Wunsch 			break;
165d7641983SJoerg Wunsch 
166d7641983SJoerg Wunsch 		case 'D':
1673c87aa1dSDavid Chisnall 			buf = _strptime(buf, "%m/%d/%y", tm, GMTp, locale);
1683624c752SPedro F. Giffuni 			if (buf == NULL)
1693624c752SPedro F. Giffuni 				return (NULL);
1702e465587SPedro F. Giffuni 			flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
171d7641983SJoerg Wunsch 			break;
172d7641983SJoerg Wunsch 
1731d6c9941SAndrey A. Chernov 		case 'E':
174c63a4303SAndrey A. Chernov 			if (Ealternative || Oalternative)
175c63a4303SAndrey A. Chernov 				break;
1761d6c9941SAndrey A. Chernov 			Ealternative++;
1771d6c9941SAndrey A. Chernov 			goto label;
1781d6c9941SAndrey A. Chernov 
1791d6c9941SAndrey A. Chernov 		case 'O':
180c63a4303SAndrey A. Chernov 			if (Ealternative || Oalternative)
181c63a4303SAndrey A. Chernov 				break;
1821d6c9941SAndrey A. Chernov 			Oalternative++;
1831d6c9941SAndrey A. Chernov 			goto label;
1841d6c9941SAndrey A. Chernov 
185c63a4303SAndrey A. Chernov 		case 'F':
1863c87aa1dSDavid Chisnall 			buf = _strptime(buf, "%Y-%m-%d", tm, GMTp, locale);
1873624c752SPedro F. Giffuni 			if (buf == NULL)
1883624c752SPedro F. Giffuni 				return (NULL);
1892e465587SPedro F. Giffuni 			flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
190c63a4303SAndrey A. Chernov 			break;
191c63a4303SAndrey A. Chernov 
192d7641983SJoerg Wunsch 		case 'R':
1933c87aa1dSDavid Chisnall 			buf = _strptime(buf, "%H:%M", tm, GMTp, locale);
1943624c752SPedro F. Giffuni 			if (buf == NULL)
1953624c752SPedro F. Giffuni 				return (NULL);
196d7641983SJoerg Wunsch 			break;
197d7641983SJoerg Wunsch 
198d7641983SJoerg Wunsch 		case 'r':
1993c87aa1dSDavid Chisnall 			buf = _strptime(buf, tptr->ampm_fmt, tm, GMTp, locale);
2003624c752SPedro F. Giffuni 			if (buf == NULL)
2013624c752SPedro F. Giffuni 				return (NULL);
202d7641983SJoerg Wunsch 			break;
203d7641983SJoerg Wunsch 
204d7641983SJoerg Wunsch 		case 'T':
2053c87aa1dSDavid Chisnall 			buf = _strptime(buf, "%H:%M:%S", tm, GMTp, locale);
2063624c752SPedro F. Giffuni 			if (buf == NULL)
2073624c752SPedro F. Giffuni 				return (NULL);
208d7641983SJoerg Wunsch 			break;
209d7641983SJoerg Wunsch 
210d7641983SJoerg Wunsch 		case 'X':
2113c87aa1dSDavid Chisnall 			buf = _strptime(buf, tptr->X_fmt, tm, GMTp, locale);
2123624c752SPedro F. Giffuni 			if (buf == NULL)
2133624c752SPedro F. Giffuni 				return (NULL);
214d7641983SJoerg Wunsch 			break;
215d7641983SJoerg Wunsch 
216d7641983SJoerg Wunsch 		case 'x':
2173c87aa1dSDavid Chisnall 			buf = _strptime(buf, tptr->x_fmt, tm, GMTp, locale);
2183624c752SPedro F. Giffuni 			if (buf == NULL)
2193624c752SPedro F. Giffuni 				return (NULL);
2202e465587SPedro F. Giffuni 			flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
221d7641983SJoerg Wunsch 			break;
222d7641983SJoerg Wunsch 
223d7641983SJoerg Wunsch 		case 'j':
2243c87aa1dSDavid Chisnall 			if (!isdigit_l((unsigned char)*buf, locale))
2253624c752SPedro F. Giffuni 				return (NULL);
226d7641983SJoerg Wunsch 
227398592ffSSheldon Hearn 			len = 3;
2283c87aa1dSDavid Chisnall 			for (i = 0; len && *buf != 0 &&
2293c87aa1dSDavid Chisnall 			     isdigit_l((unsigned char)*buf, locale); buf++){
230d7641983SJoerg Wunsch 				i *= 10;
231d7641983SJoerg Wunsch 				i += *buf - '0';
232398592ffSSheldon Hearn 				len--;
233d7641983SJoerg Wunsch 			}
23433dbb0a6SSheldon Hearn 			if (i < 1 || i > 366)
2353624c752SPedro F. Giffuni 				return (NULL);
236d7641983SJoerg Wunsch 
23733dbb0a6SSheldon Hearn 			tm->tm_yday = i - 1;
2382e465587SPedro F. Giffuni 			flags |= FLAG_YDAY;
2392e465587SPedro F. Giffuni 
240d7641983SJoerg Wunsch 			break;
241d7641983SJoerg Wunsch 
242d7641983SJoerg Wunsch 		case 'M':
243d7641983SJoerg Wunsch 		case 'S':
2443c87aa1dSDavid Chisnall 			if (*buf == 0 ||
2453c87aa1dSDavid Chisnall 				isspace_l((unsigned char)*buf, locale))
246d7641983SJoerg Wunsch 				break;
247d7641983SJoerg Wunsch 
2483c87aa1dSDavid Chisnall 			if (!isdigit_l((unsigned char)*buf, locale))
2493624c752SPedro F. Giffuni 				return (NULL);
250d7641983SJoerg Wunsch 
251398592ffSSheldon Hearn 			len = 2;
2523c87aa1dSDavid Chisnall 			for (i = 0; len && *buf != 0 &&
2533c87aa1dSDavid Chisnall 				isdigit_l((unsigned char)*buf, locale); buf++){
254d7641983SJoerg Wunsch 				i *= 10;
255d7641983SJoerg Wunsch 				i += *buf - '0';
256398592ffSSheldon Hearn 				len--;
257d7641983SJoerg Wunsch 			}
25833dbb0a6SSheldon Hearn 
25933dbb0a6SSheldon Hearn 			if (c == 'M') {
260d7641983SJoerg Wunsch 				if (i > 59)
2613624c752SPedro F. Giffuni 					return (NULL);
262d7641983SJoerg Wunsch 				tm->tm_min = i;
26333dbb0a6SSheldon Hearn 			} else {
26433dbb0a6SSheldon Hearn 				if (i > 60)
2653624c752SPedro F. Giffuni 					return (NULL);
266d7641983SJoerg Wunsch 				tm->tm_sec = i;
26733dbb0a6SSheldon Hearn 			}
268d7641983SJoerg Wunsch 
269d7641983SJoerg Wunsch 			break;
270d7641983SJoerg Wunsch 
271d7641983SJoerg Wunsch 		case 'H':
272d7641983SJoerg Wunsch 		case 'I':
273d7641983SJoerg Wunsch 		case 'k':
274d7641983SJoerg Wunsch 		case 'l':
275398592ffSSheldon Hearn 			/*
276398592ffSSheldon Hearn 			 * Of these, %l is the only specifier explicitly
277398592ffSSheldon Hearn 			 * documented as not being zero-padded.  However,
278398592ffSSheldon Hearn 			 * there is no harm in allowing zero-padding.
279398592ffSSheldon Hearn 			 *
280398592ffSSheldon Hearn 			 * XXX The %l specifier may gobble one too many
281398592ffSSheldon Hearn 			 * digits if used incorrectly.
282398592ffSSheldon Hearn 			 */
2833c87aa1dSDavid Chisnall 			if (!isdigit_l((unsigned char)*buf, locale))
2843624c752SPedro F. Giffuni 				return (NULL);
285d7641983SJoerg Wunsch 
286398592ffSSheldon Hearn 			len = 2;
2873c87aa1dSDavid Chisnall 			for (i = 0; len && *buf != 0 &&
2883c87aa1dSDavid Chisnall 			     isdigit_l((unsigned char)*buf, locale); buf++) {
289d7641983SJoerg Wunsch 				i *= 10;
290d7641983SJoerg Wunsch 				i += *buf - '0';
291398592ffSSheldon Hearn 				len--;
292d7641983SJoerg Wunsch 			}
293d7641983SJoerg Wunsch 			if (c == 'H' || c == 'k') {
294d7641983SJoerg Wunsch 				if (i > 23)
2953624c752SPedro F. Giffuni 					return (NULL);
296882f32c1SSheldon Hearn 			} else if (i > 12)
2973624c752SPedro F. Giffuni 				return (NULL);
298d7641983SJoerg Wunsch 
299d7641983SJoerg Wunsch 			tm->tm_hour = i;
300d7641983SJoerg Wunsch 
301d7641983SJoerg Wunsch 			break;
302d7641983SJoerg Wunsch 
303d7641983SJoerg Wunsch 		case 'p':
304398592ffSSheldon Hearn 			/*
305398592ffSSheldon Hearn 			 * XXX This is bogus if parsed before hour-related
306398592ffSSheldon Hearn 			 * specifiers.
307398592ffSSheldon Hearn 			 */
308930cd711SAlexey Zelkin 			len = strlen(tptr->am);
3093c87aa1dSDavid Chisnall 			if (strncasecmp_l(buf, tptr->am, len, locale) == 0) {
310d7641983SJoerg Wunsch 				if (tm->tm_hour > 12)
3113624c752SPedro F. Giffuni 					return (NULL);
312d7641983SJoerg Wunsch 				if (tm->tm_hour == 12)
313d7641983SJoerg Wunsch 					tm->tm_hour = 0;
314d7641983SJoerg Wunsch 				buf += len;
315d7641983SJoerg Wunsch 				break;
316d7641983SJoerg Wunsch 			}
317d7641983SJoerg Wunsch 
318930cd711SAlexey Zelkin 			len = strlen(tptr->pm);
3193c87aa1dSDavid Chisnall 			if (strncasecmp_l(buf, tptr->pm, len, locale) == 0) {
320d7641983SJoerg Wunsch 				if (tm->tm_hour > 12)
3213624c752SPedro F. Giffuni 					return (NULL);
322d7641983SJoerg Wunsch 				if (tm->tm_hour != 12)
323d7641983SJoerg Wunsch 					tm->tm_hour += 12;
324d7641983SJoerg Wunsch 				buf += len;
325d7641983SJoerg Wunsch 				break;
326d7641983SJoerg Wunsch 			}
327d7641983SJoerg Wunsch 
3283624c752SPedro F. Giffuni 			return (NULL);
329d7641983SJoerg Wunsch 
330d7641983SJoerg Wunsch 		case 'A':
331d7641983SJoerg Wunsch 		case 'a':
332930cd711SAlexey Zelkin 			for (i = 0; i < asizeof(tptr->weekday); i++) {
333930cd711SAlexey Zelkin 				len = strlen(tptr->weekday[i]);
3343c87aa1dSDavid Chisnall 				if (strncasecmp_l(buf, tptr->weekday[i],
3353c87aa1dSDavid Chisnall 						len, locale) == 0)
336d7641983SJoerg Wunsch 					break;
337930cd711SAlexey Zelkin 				len = strlen(tptr->wday[i]);
3383c87aa1dSDavid Chisnall 				if (strncasecmp_l(buf, tptr->wday[i],
3393c87aa1dSDavid Chisnall 						len, locale) == 0)
340d7641983SJoerg Wunsch 					break;
341d7641983SJoerg Wunsch 			}
342930cd711SAlexey Zelkin 			if (i == asizeof(tptr->weekday))
3433624c752SPedro F. Giffuni 				return (NULL);
344d7641983SJoerg Wunsch 
3451a6804c6SPedro F. Giffuni 			buf += len;
346d7641983SJoerg Wunsch 			tm->tm_wday = i;
3472e465587SPedro F. Giffuni 			flags |= FLAG_WDAY;
348d7641983SJoerg Wunsch 			break;
349d7641983SJoerg Wunsch 
35033dbb0a6SSheldon Hearn 		case 'U':
35133dbb0a6SSheldon Hearn 		case 'W':
35233dbb0a6SSheldon Hearn 			/*
35333dbb0a6SSheldon Hearn 			 * XXX This is bogus, as we can not assume any valid
35433dbb0a6SSheldon Hearn 			 * information present in the tm structure at this
35533dbb0a6SSheldon Hearn 			 * point to calculate a real value, so just check the
35633dbb0a6SSheldon Hearn 			 * range for now.
35733dbb0a6SSheldon Hearn 			 */
3583c87aa1dSDavid Chisnall 			if (!isdigit_l((unsigned char)*buf, locale))
3593624c752SPedro F. Giffuni 				return (NULL);
36033dbb0a6SSheldon Hearn 
361398592ffSSheldon Hearn 			len = 2;
3623c87aa1dSDavid Chisnall 			for (i = 0; len && *buf != 0 &&
3633c87aa1dSDavid Chisnall 			     isdigit_l((unsigned char)*buf, locale); buf++) {
36433dbb0a6SSheldon Hearn 				i *= 10;
36533dbb0a6SSheldon Hearn 				i += *buf - '0';
366398592ffSSheldon Hearn 				len--;
36733dbb0a6SSheldon Hearn 			}
36833dbb0a6SSheldon Hearn 			if (i > 53)
3693624c752SPedro F. Giffuni 				return (NULL);
37033dbb0a6SSheldon Hearn 
3712e465587SPedro F. Giffuni 			if (c == 'U')
3722e465587SPedro F. Giffuni 				day_offset = TM_SUNDAY;
3732e465587SPedro F. Giffuni 			else
3742e465587SPedro F. Giffuni 				day_offset = TM_MONDAY;
3752e465587SPedro F. Giffuni 
3762e465587SPedro F. Giffuni 
3772e465587SPedro F. Giffuni 			week_offset = i;
3782e465587SPedro F. Giffuni 
37933dbb0a6SSheldon Hearn 			break;
38033dbb0a6SSheldon Hearn 
38133dbb0a6SSheldon Hearn 		case 'w':
3823c87aa1dSDavid Chisnall 			if (!isdigit_l((unsigned char)*buf, locale))
3833624c752SPedro F. Giffuni 				return (NULL);
38433dbb0a6SSheldon Hearn 
385398592ffSSheldon Hearn 			i = *buf - '0';
38633dbb0a6SSheldon Hearn 			if (i > 6)
3873624c752SPedro F. Giffuni 				return (NULL);
38833dbb0a6SSheldon Hearn 
38933dbb0a6SSheldon Hearn 			tm->tm_wday = i;
3902e465587SPedro F. Giffuni 			flags |= FLAG_WDAY;
39133dbb0a6SSheldon Hearn 
39233dbb0a6SSheldon Hearn 			break;
39333dbb0a6SSheldon Hearn 
394d7641983SJoerg Wunsch 		case 'e':
395398592ffSSheldon Hearn 			/*
396cd9dcb03SPedro F. Giffuni 			 * With %e format, our strftime(3) adds a blank space
397cd9dcb03SPedro F. Giffuni 			 * before single digits.
398cd9dcb03SPedro F. Giffuni 			 */
399cd9dcb03SPedro F. Giffuni 			if (*buf != 0 &&
400cd9dcb03SPedro F. Giffuni 			    isspace_l((unsigned char)*buf, locale))
401cd9dcb03SPedro F. Giffuni 			       buf++;
402cd9dcb03SPedro F. Giffuni 			/* FALLTHROUGH */
403cd9dcb03SPedro F. Giffuni 		case 'd':
404cd9dcb03SPedro F. Giffuni 			/*
405cd9dcb03SPedro F. Giffuni 			 * The %e specifier was once explicitly documented as
406cd9dcb03SPedro F. Giffuni 			 * not being zero-padded but was later changed to
407cd9dcb03SPedro F. Giffuni 			 * equivalent to %d.  There is no harm in allowing
408398592ffSSheldon Hearn 			 * such padding.
409398592ffSSheldon Hearn 			 *
410398592ffSSheldon Hearn 			 * XXX The %e specifier may gobble one too many
411398592ffSSheldon Hearn 			 * digits if used incorrectly.
412398592ffSSheldon Hearn 			 */
4133c87aa1dSDavid Chisnall 			if (!isdigit_l((unsigned char)*buf, locale))
4143624c752SPedro F. Giffuni 				return (NULL);
415d7641983SJoerg Wunsch 
416398592ffSSheldon Hearn 			len = 2;
4173c87aa1dSDavid Chisnall 			for (i = 0; len && *buf != 0 &&
4183c87aa1dSDavid Chisnall 			     isdigit_l((unsigned char)*buf, locale); buf++) {
419d7641983SJoerg Wunsch 				i *= 10;
420d7641983SJoerg Wunsch 				i += *buf - '0';
421398592ffSSheldon Hearn 				len--;
422d7641983SJoerg Wunsch 			}
423d7641983SJoerg Wunsch 			if (i > 31)
4243624c752SPedro F. Giffuni 				return (NULL);
425d7641983SJoerg Wunsch 
426d7641983SJoerg Wunsch 			tm->tm_mday = i;
4272e465587SPedro F. Giffuni 			flags |= FLAG_MDAY;
428d7641983SJoerg Wunsch 
429d7641983SJoerg Wunsch 			break;
430d7641983SJoerg Wunsch 
431d7641983SJoerg Wunsch 		case 'B':
432d7641983SJoerg Wunsch 		case 'b':
433d7641983SJoerg Wunsch 		case 'h':
434930cd711SAlexey Zelkin 			for (i = 0; i < asizeof(tptr->month); i++) {
4351d6c9941SAndrey A. Chernov 				if (Oalternative) {
4361d6c9941SAndrey A. Chernov 					if (c == 'B') {
437930cd711SAlexey Zelkin 						len = strlen(tptr->alt_month[i]);
4383c87aa1dSDavid Chisnall 						if (strncasecmp_l(buf,
439930cd711SAlexey Zelkin 								tptr->alt_month[i],
4403c87aa1dSDavid Chisnall 								len, locale) == 0)
4411d6c9941SAndrey A. Chernov 							break;
4421d6c9941SAndrey A. Chernov 					}
4431d6c9941SAndrey A. Chernov 				} else {
444930cd711SAlexey Zelkin 					len = strlen(tptr->month[i]);
4453c87aa1dSDavid Chisnall 					if (strncasecmp_l(buf, tptr->month[i],
4463c87aa1dSDavid Chisnall 							len, locale) == 0)
447d7641983SJoerg Wunsch 						break;
4483d74e220SEdwin Groothuis 				}
4493d74e220SEdwin Groothuis 			}
4503d74e220SEdwin Groothuis 			/*
4513d74e220SEdwin Groothuis 			 * Try the abbreviated month name if the full name
4523d74e220SEdwin Groothuis 			 * wasn't found and Oalternative was not requested.
4533d74e220SEdwin Groothuis 			 */
4543d74e220SEdwin Groothuis 			if (i == asizeof(tptr->month) && !Oalternative) {
4553d74e220SEdwin Groothuis 				for (i = 0; i < asizeof(tptr->month); i++) {
456930cd711SAlexey Zelkin 					len = strlen(tptr->mon[i]);
4573c87aa1dSDavid Chisnall 					if (strncasecmp_l(buf, tptr->mon[i],
4583c87aa1dSDavid Chisnall 							len, locale) == 0)
459d7641983SJoerg Wunsch 						break;
460d7641983SJoerg Wunsch 				}
4611d6c9941SAndrey A. Chernov 			}
462930cd711SAlexey Zelkin 			if (i == asizeof(tptr->month))
4633624c752SPedro F. Giffuni 				return (NULL);
464d7641983SJoerg Wunsch 
465d7641983SJoerg Wunsch 			tm->tm_mon = i;
466d7641983SJoerg Wunsch 			buf += len;
4672e465587SPedro F. Giffuni 			flags |= FLAG_MONTH;
4682e465587SPedro F. Giffuni 
469d7641983SJoerg Wunsch 			break;
470d7641983SJoerg Wunsch 
471d7641983SJoerg Wunsch 		case 'm':
4723c87aa1dSDavid Chisnall 			if (!isdigit_l((unsigned char)*buf, locale))
4733624c752SPedro F. Giffuni 				return (NULL);
474d7641983SJoerg Wunsch 
475398592ffSSheldon Hearn 			len = 2;
4763c87aa1dSDavid Chisnall 			for (i = 0; len && *buf != 0 &&
4773c87aa1dSDavid Chisnall 			     isdigit_l((unsigned char)*buf, locale); buf++) {
478d7641983SJoerg Wunsch 				i *= 10;
479d7641983SJoerg Wunsch 				i += *buf - '0';
480398592ffSSheldon Hearn 				len--;
481d7641983SJoerg Wunsch 			}
482d7641983SJoerg Wunsch 			if (i < 1 || i > 12)
4833624c752SPedro F. Giffuni 				return (NULL);
484d7641983SJoerg Wunsch 
485d7641983SJoerg Wunsch 			tm->tm_mon = i - 1;
4862e465587SPedro F. Giffuni 			flags |= FLAG_MONTH;
487d7641983SJoerg Wunsch 
488d7641983SJoerg Wunsch 			break;
489d7641983SJoerg Wunsch 
490427fc5edSDima Dorfman 		case 's':
491427fc5edSDima Dorfman 			{
492427fc5edSDima Dorfman 			char *cp;
493c91e947dSJacques Vidrine 			int sverrno;
494c91e947dSJacques Vidrine 			long n;
495427fc5edSDima Dorfman 			time_t t;
496427fc5edSDima Dorfman 
497c91e947dSJacques Vidrine 			sverrno = errno;
498c91e947dSJacques Vidrine 			errno = 0;
4993c87aa1dSDavid Chisnall 			n = strtol_l(buf, &cp, 10, locale);
500c91e947dSJacques Vidrine 			if (errno == ERANGE || (long)(t = n) != n) {
501c91e947dSJacques Vidrine 				errno = sverrno;
5023624c752SPedro F. Giffuni 				return (NULL);
503c91e947dSJacques Vidrine 			}
504c91e947dSJacques Vidrine 			errno = sverrno;
505427fc5edSDima Dorfman 			buf = cp;
506eff6f444SPedro F. Giffuni 			if (gmtime_r(&t, tm) == NULL)
507eff6f444SPedro F. Giffuni 				return (NULL);
508fe71e0b8SMike Makonnen 			*GMTp = 1;
509eff6f444SPedro F. Giffuni 			flags |= FLAG_YDAY | FLAG_WDAY | FLAG_MONTH |
510eff6f444SPedro F. Giffuni 			    FLAG_MDAY | FLAG_YEAR;
511427fc5edSDima Dorfman 			}
512427fc5edSDima Dorfman 			break;
513427fc5edSDima Dorfman 
514d7641983SJoerg Wunsch 		case 'Y':
515d7641983SJoerg Wunsch 		case 'y':
5163c87aa1dSDavid Chisnall 			if (*buf == 0 ||
5173c87aa1dSDavid Chisnall 			    isspace_l((unsigned char)*buf, locale))
518d7641983SJoerg Wunsch 				break;
519d7641983SJoerg Wunsch 
5203c87aa1dSDavid Chisnall 			if (!isdigit_l((unsigned char)*buf, locale))
5213624c752SPedro F. Giffuni 				return (NULL);
522d7641983SJoerg Wunsch 
523398592ffSSheldon Hearn 			len = (c == 'Y') ? 4 : 2;
5243c87aa1dSDavid Chisnall 			for (i = 0; len && *buf != 0 &&
5253c87aa1dSDavid Chisnall 			     isdigit_l((unsigned char)*buf, locale); buf++) {
526d7641983SJoerg Wunsch 				i *= 10;
527d7641983SJoerg Wunsch 				i += *buf - '0';
528398592ffSSheldon Hearn 				len--;
529d7641983SJoerg Wunsch 			}
530d7641983SJoerg Wunsch 			if (c == 'Y')
5312e465587SPedro F. Giffuni 				i -= TM_YEAR_BASE;
532aba0410bSWes Peters 			if (c == 'y' && i < 69)
533a00b1d8fSWes Peters 				i += 100;
534d7641983SJoerg Wunsch 			if (i < 0)
5353624c752SPedro F. Giffuni 				return (NULL);
536d7641983SJoerg Wunsch 
537d7641983SJoerg Wunsch 			tm->tm_year = i;
5382e465587SPedro F. Giffuni 			flags |= FLAG_YEAR;
539d7641983SJoerg Wunsch 
540d7641983SJoerg Wunsch 			break;
541b47f20dfSDavid E. O'Brien 
542b47f20dfSDavid E. O'Brien 		case 'Z':
543b47f20dfSDavid E. O'Brien 			{
544b47f20dfSDavid E. O'Brien 			const char *cp;
545b47f20dfSDavid E. O'Brien 			char *zonestr;
546b47f20dfSDavid E. O'Brien 
5473c87aa1dSDavid Chisnall 			for (cp = buf; *cp &&
5483c87aa1dSDavid Chisnall 			     isupper_l((unsigned char)*cp, locale); ++cp) {
5493c87aa1dSDavid Chisnall 				/*empty*/}
550b47f20dfSDavid E. O'Brien 			if (cp - buf) {
551b47f20dfSDavid E. O'Brien 				zonestr = alloca(cp - buf + 1);
552b47f20dfSDavid E. O'Brien 				strncpy(zonestr, buf, cp - buf);
553b47f20dfSDavid E. O'Brien 				zonestr[cp - buf] = '\0';
554b47f20dfSDavid E. O'Brien 				tzset();
555420bc6ceSAndrey A. Chernov 				if (0 == strcmp(zonestr, "GMT") ||
556420bc6ceSAndrey A. Chernov 				    0 == strcmp(zonestr, "UTC")) {
557fe71e0b8SMike Makonnen 				    *GMTp = 1;
558b47f20dfSDavid E. O'Brien 				} else if (0 == strcmp(zonestr, tzname[0])) {
559b47f20dfSDavid E. O'Brien 				    tm->tm_isdst = 0;
560b47f20dfSDavid E. O'Brien 				} else if (0 == strcmp(zonestr, tzname[1])) {
561b47f20dfSDavid E. O'Brien 				    tm->tm_isdst = 1;
562b47f20dfSDavid E. O'Brien 				} else {
5633624c752SPedro F. Giffuni 				    return (NULL);
564b47f20dfSDavid E. O'Brien 				}
565b47f20dfSDavid E. O'Brien 				buf += cp - buf;
566b47f20dfSDavid E. O'Brien 			}
567b47f20dfSDavid E. O'Brien 			}
568b47f20dfSDavid E. O'Brien 			break;
56940523da7SXin LI 
57040523da7SXin LI 		case 'z':
57140523da7SXin LI 			{
57240523da7SXin LI 			int sign = 1;
57340523da7SXin LI 
57440523da7SXin LI 			if (*buf != '+') {
57540523da7SXin LI 				if (*buf == '-')
57640523da7SXin LI 					sign = -1;
57740523da7SXin LI 				else
5783624c752SPedro F. Giffuni 					return (NULL);
57940523da7SXin LI 			}
58040523da7SXin LI 
58140523da7SXin LI 			buf++;
58240523da7SXin LI 			i = 0;
58340523da7SXin LI 			for (len = 4; len > 0; len--) {
5843c87aa1dSDavid Chisnall 				if (isdigit_l((unsigned char)*buf, locale)) {
58540523da7SXin LI 					i *= 10;
58640523da7SXin LI 					i += *buf - '0';
58740523da7SXin LI 					buf++;
58840523da7SXin LI 				} else
5893624c752SPedro F. Giffuni 					return (NULL);
59040523da7SXin LI 			}
59140523da7SXin LI 
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;
603d7641983SJoerg Wunsch 		}
604d7641983SJoerg Wunsch 	}
6052e465587SPedro F. Giffuni 
6062e465587SPedro F. Giffuni 	if (!(flags & FLAG_YDAY) && (flags & FLAG_YEAR)) {
6072e465587SPedro F. Giffuni 		if ((flags & (FLAG_MONTH | FLAG_MDAY)) ==
6082e465587SPedro F. Giffuni 		    (FLAG_MONTH | FLAG_MDAY)) {
6092e465587SPedro F. Giffuni 			tm->tm_yday = start_of_month[isleap(tm->tm_year +
6102e465587SPedro F. Giffuni 			    TM_YEAR_BASE)][tm->tm_mon] + (tm->tm_mday - 1);
6112e465587SPedro F. Giffuni 			flags |= FLAG_YDAY;
6122e465587SPedro F. Giffuni 		} else if (day_offset != -1) {
6132e465587SPedro F. Giffuni 			/* Set the date to the first Sunday (or Monday)
6142e465587SPedro F. Giffuni 			 * of the specified week of the year.
6152e465587SPedro F. Giffuni 			 */
6162e465587SPedro F. Giffuni 			if (!(flags & FLAG_WDAY)) {
6172e465587SPedro F. Giffuni 				tm->tm_wday = day_offset;
6182e465587SPedro F. Giffuni 				flags |= FLAG_WDAY;
6192e465587SPedro F. Giffuni 			}
6202e465587SPedro F. Giffuni 			tm->tm_yday = (7 -
6212e465587SPedro F. Giffuni 			    first_wday_of(tm->tm_year + TM_YEAR_BASE) +
6222e465587SPedro F. Giffuni 			    day_offset) % 7 + (week_offset - 1) * 7 +
6232e465587SPedro F. Giffuni 			    tm->tm_wday - day_offset;
6242e465587SPedro F. Giffuni 			flags |= FLAG_YDAY;
6252e465587SPedro F. Giffuni 		}
626ce1d331eSPedro F. Giffuni 	}
627cb7a4779SDavid E. O'Brien 
6282e465587SPedro F. Giffuni 	if ((flags & (FLAG_YEAR | FLAG_YDAY)) == (FLAG_YEAR | FLAG_YDAY)) {
6292e465587SPedro F. Giffuni 		if (!(flags & FLAG_MONTH)) {
6302e465587SPedro F. Giffuni 			i = 0;
6312e465587SPedro F. Giffuni 			while (tm->tm_yday >=
6322e465587SPedro F. Giffuni 			    start_of_month[isleap(tm->tm_year +
6332e465587SPedro F. Giffuni 			    TM_YEAR_BASE)][i])
6342e465587SPedro F. Giffuni 				i++;
6352e465587SPedro F. Giffuni 			if (i > 12) {
6362e465587SPedro F. Giffuni 				i = 1;
6372e465587SPedro F. Giffuni 				tm->tm_yday -=
6382e465587SPedro F. Giffuni 				    start_of_month[isleap(tm->tm_year +
6392e465587SPedro F. Giffuni 				    TM_YEAR_BASE)][12];
6402e465587SPedro F. Giffuni 				tm->tm_year++;
6412e465587SPedro F. Giffuni 			}
6422e465587SPedro F. Giffuni 			tm->tm_mon = i - 1;
6432e465587SPedro F. Giffuni 			flags |= FLAG_MONTH;
6442e465587SPedro F. Giffuni 		}
6452e465587SPedro F. Giffuni 		if (!(flags & FLAG_MDAY)) {
6462e465587SPedro F. Giffuni 			tm->tm_mday = tm->tm_yday -
6472e465587SPedro F. Giffuni 			    start_of_month[isleap(tm->tm_year + TM_YEAR_BASE)]
6482e465587SPedro F. Giffuni 			    [tm->tm_mon] + 1;
6492e465587SPedro F. Giffuni 			flags |= FLAG_MDAY;
6502e465587SPedro F. Giffuni 		}
6512e465587SPedro F. Giffuni 		if (!(flags & FLAG_WDAY)) {
6522e465587SPedro F. Giffuni 			i = 0;
6532e465587SPedro F. Giffuni 			wday_offset = first_wday_of(tm->tm_year);
6542e465587SPedro F. Giffuni 			while (i++ <= tm->tm_yday) {
6552e465587SPedro F. Giffuni 				if (wday_offset++ >= 6)
6562e465587SPedro F. Giffuni 					wday_offset = 0;
6572e465587SPedro F. Giffuni 			}
6582e465587SPedro F. Giffuni 			tm->tm_wday = wday_offset;
6592e465587SPedro F. Giffuni 			flags |= FLAG_WDAY;
6602e465587SPedro F. Giffuni 		}
6612e465587SPedro F. Giffuni 	}
6622e465587SPedro F. Giffuni 
6632e465587SPedro F. Giffuni 	return ((char *)buf);
6642e465587SPedro F. Giffuni }
665c1cd488cSPedro F. Giffuni 
666cb7a4779SDavid E. O'Brien char *
6673c87aa1dSDavid Chisnall strptime_l(const char * __restrict buf, const char * __restrict fmt,
6683c87aa1dSDavid Chisnall     struct tm * __restrict tm, locale_t loc)
669cb7a4779SDavid E. O'Brien {
670cb7a4779SDavid E. O'Brien 	char *ret;
671fe71e0b8SMike Makonnen 	int gmt;
6723c87aa1dSDavid Chisnall 	FIX_LOCALE(loc);
673cb7a4779SDavid E. O'Brien 
674fe71e0b8SMike Makonnen 	gmt = 0;
6753c87aa1dSDavid Chisnall 	ret = _strptime(buf, fmt, tm, &gmt, loc);
6766c688436SMike Makonnen 	if (ret && gmt) {
6776c688436SMike Makonnen 		time_t t = timegm(tm);
678420bc6ceSAndrey A. Chernov 
679420bc6ceSAndrey A. Chernov 		if (t == -1)
680420bc6ceSAndrey A. Chernov 			return (NULL);
681b47f20dfSDavid E. O'Brien 		localtime_r(&t, tm);
682b47f20dfSDavid E. O'Brien 	}
683cb7a4779SDavid E. O'Brien 
684fe71e0b8SMike Makonnen 	return (ret);
685d7641983SJoerg Wunsch }
6862e465587SPedro F. Giffuni 
6873c87aa1dSDavid Chisnall char *
6883c87aa1dSDavid Chisnall strptime(const char * __restrict buf, const char * __restrict fmt,
6893c87aa1dSDavid Chisnall     struct tm * __restrict tm)
6903c87aa1dSDavid Chisnall {
6913c87aa1dSDavid Chisnall 	return strptime_l(buf, fmt, tm, __get_locale());
6923c87aa1dSDavid Chisnall }
693