xref: /freebsd/lib/libc/stdtime/strptime.c (revision cd9dcb03)
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"
58d7641983SJoerg Wunsch 
593c87aa1dSDavid Chisnall static char * _strptime(const char *, const char *, struct tm *, int *, locale_t);
60cb7a4779SDavid E. O'Brien 
61d7641983SJoerg Wunsch #define	asizeof(a)	(sizeof (a) / sizeof ((a)[0]))
62d7641983SJoerg Wunsch 
63cb7a4779SDavid E. O'Brien static char *
643c87aa1dSDavid Chisnall _strptime(const char *buf, const char *fmt, struct tm *tm, int *GMTp,
653c87aa1dSDavid Chisnall 		locale_t locale)
66d7641983SJoerg Wunsch {
6737486f03SJoerg Wunsch 	char	c;
6837486f03SJoerg Wunsch 	const char *ptr;
693624c752SPedro F. Giffuni 	int	i, len;
701d6c9941SAndrey A. Chernov 	int Ealternative, Oalternative;
713c87aa1dSDavid Chisnall 	struct lc_time_T *tptr = __get_current_time_locale(locale);
72d7641983SJoerg Wunsch 
73d7641983SJoerg Wunsch 	ptr = fmt;
74d7641983SJoerg Wunsch 	while (*ptr != 0) {
75d7641983SJoerg Wunsch 		if (*buf == 0)
76d7641983SJoerg Wunsch 			break;
77d7641983SJoerg Wunsch 
78d7641983SJoerg Wunsch 		c = *ptr++;
79d7641983SJoerg Wunsch 
80d7641983SJoerg Wunsch 		if (c != '%') {
813c87aa1dSDavid Chisnall 			if (isspace_l((unsigned char)c, locale))
823c87aa1dSDavid Chisnall 				while (*buf != 0 &&
833c87aa1dSDavid Chisnall 				       isspace_l((unsigned char)*buf, locale))
84d7641983SJoerg Wunsch 					buf++;
85d7641983SJoerg Wunsch 			else if (c != *buf++)
863624c752SPedro F. Giffuni 				return (NULL);
87d7641983SJoerg Wunsch 			continue;
88d7641983SJoerg Wunsch 		}
89d7641983SJoerg Wunsch 
901d6c9941SAndrey A. Chernov 		Ealternative = 0;
911d6c9941SAndrey A. Chernov 		Oalternative = 0;
921d6c9941SAndrey A. Chernov label:
93d7641983SJoerg Wunsch 		c = *ptr++;
94d7641983SJoerg Wunsch 		switch (c) {
95d7641983SJoerg Wunsch 		case 0:
96d7641983SJoerg Wunsch 		case '%':
97d7641983SJoerg Wunsch 			if (*buf++ != '%')
983624c752SPedro F. Giffuni 				return (NULL);
99d7641983SJoerg Wunsch 			break;
100d7641983SJoerg Wunsch 
1011d6c9941SAndrey A. Chernov 		case '+':
1023c87aa1dSDavid Chisnall 			buf = _strptime(buf, tptr->date_fmt, tm, GMTp, locale);
1033624c752SPedro F. Giffuni 			if (buf == NULL)
1043624c752SPedro F. Giffuni 				return (NULL);
105d7641983SJoerg Wunsch 			break;
106d7641983SJoerg Wunsch 
1071d6c9941SAndrey A. Chernov 		case 'C':
1083c87aa1dSDavid Chisnall 			if (!isdigit_l((unsigned char)*buf, locale))
1093624c752SPedro F. Giffuni 				return (NULL);
1101d6c9941SAndrey A. Chernov 
111398592ffSSheldon Hearn 			/* XXX This will break for 3-digit centuries. */
112398592ffSSheldon Hearn 			len = 2;
1133c87aa1dSDavid Chisnall 			for (i = 0; len && *buf != 0 &&
1143c87aa1dSDavid Chisnall 			     isdigit_l((unsigned char)*buf, locale); buf++) {
1151d6c9941SAndrey A. Chernov 				i *= 10;
1161d6c9941SAndrey A. Chernov 				i += *buf - '0';
117398592ffSSheldon Hearn 				len--;
1181d6c9941SAndrey A. Chernov 			}
1191d6c9941SAndrey A. Chernov 			if (i < 19)
1203624c752SPedro F. Giffuni 				return (NULL);
1211d6c9941SAndrey A. Chernov 
1221d6c9941SAndrey A. Chernov 			tm->tm_year = i * 100 - 1900;
1231d6c9941SAndrey A. Chernov 			break;
1241d6c9941SAndrey A. Chernov 
125d7641983SJoerg Wunsch 		case 'c':
1263c87aa1dSDavid Chisnall 			buf = _strptime(buf, tptr->c_fmt, tm, GMTp, locale);
1273624c752SPedro F. Giffuni 			if (buf == NULL)
1283624c752SPedro F. Giffuni 				return (NULL);
129d7641983SJoerg Wunsch 			break;
130d7641983SJoerg Wunsch 
131d7641983SJoerg Wunsch 		case 'D':
1323c87aa1dSDavid Chisnall 			buf = _strptime(buf, "%m/%d/%y", tm, GMTp, locale);
1333624c752SPedro F. Giffuni 			if (buf == NULL)
1343624c752SPedro F. Giffuni 				return (NULL);
135d7641983SJoerg Wunsch 			break;
136d7641983SJoerg Wunsch 
1371d6c9941SAndrey A. Chernov 		case 'E':
138c63a4303SAndrey A. Chernov 			if (Ealternative || Oalternative)
139c63a4303SAndrey A. Chernov 				break;
1401d6c9941SAndrey A. Chernov 			Ealternative++;
1411d6c9941SAndrey A. Chernov 			goto label;
1421d6c9941SAndrey A. Chernov 
1431d6c9941SAndrey A. Chernov 		case 'O':
144c63a4303SAndrey A. Chernov 			if (Ealternative || Oalternative)
145c63a4303SAndrey A. Chernov 				break;
1461d6c9941SAndrey A. Chernov 			Oalternative++;
1471d6c9941SAndrey A. Chernov 			goto label;
1481d6c9941SAndrey A. Chernov 
149c63a4303SAndrey A. Chernov 		case 'F':
1503c87aa1dSDavid Chisnall 			buf = _strptime(buf, "%Y-%m-%d", tm, GMTp, locale);
1513624c752SPedro F. Giffuni 			if (buf == NULL)
1523624c752SPedro F. Giffuni 				return (NULL);
153c63a4303SAndrey A. Chernov 			break;
154c63a4303SAndrey A. Chernov 
155d7641983SJoerg Wunsch 		case 'R':
1563c87aa1dSDavid Chisnall 			buf = _strptime(buf, "%H:%M", tm, GMTp, locale);
1573624c752SPedro F. Giffuni 			if (buf == NULL)
1583624c752SPedro F. Giffuni 				return (NULL);
159d7641983SJoerg Wunsch 			break;
160d7641983SJoerg Wunsch 
161d7641983SJoerg Wunsch 		case 'r':
1623c87aa1dSDavid Chisnall 			buf = _strptime(buf, tptr->ampm_fmt, tm, GMTp, locale);
1633624c752SPedro F. Giffuni 			if (buf == NULL)
1643624c752SPedro F. Giffuni 				return (NULL);
165d7641983SJoerg Wunsch 			break;
166d7641983SJoerg Wunsch 
167d7641983SJoerg Wunsch 		case 'T':
1683c87aa1dSDavid Chisnall 			buf = _strptime(buf, "%H:%M:%S", tm, GMTp, locale);
1693624c752SPedro F. Giffuni 			if (buf == NULL)
1703624c752SPedro F. Giffuni 				return (NULL);
171d7641983SJoerg Wunsch 			break;
172d7641983SJoerg Wunsch 
173d7641983SJoerg Wunsch 		case 'X':
1743c87aa1dSDavid Chisnall 			buf = _strptime(buf, tptr->X_fmt, tm, GMTp, locale);
1753624c752SPedro F. Giffuni 			if (buf == NULL)
1763624c752SPedro F. Giffuni 				return (NULL);
177d7641983SJoerg Wunsch 			break;
178d7641983SJoerg Wunsch 
179d7641983SJoerg Wunsch 		case 'x':
1803c87aa1dSDavid Chisnall 			buf = _strptime(buf, tptr->x_fmt, tm, GMTp, locale);
1813624c752SPedro F. Giffuni 			if (buf == NULL)
1823624c752SPedro F. Giffuni 				return (NULL);
183d7641983SJoerg Wunsch 			break;
184d7641983SJoerg Wunsch 
185d7641983SJoerg Wunsch 		case 'j':
1863c87aa1dSDavid Chisnall 			if (!isdigit_l((unsigned char)*buf, locale))
1873624c752SPedro F. Giffuni 				return (NULL);
188d7641983SJoerg Wunsch 
189398592ffSSheldon Hearn 			len = 3;
1903c87aa1dSDavid Chisnall 			for (i = 0; len && *buf != 0 &&
1913c87aa1dSDavid Chisnall 			     isdigit_l((unsigned char)*buf, locale); buf++){
192d7641983SJoerg Wunsch 				i *= 10;
193d7641983SJoerg Wunsch 				i += *buf - '0';
194398592ffSSheldon Hearn 				len--;
195d7641983SJoerg Wunsch 			}
19633dbb0a6SSheldon Hearn 			if (i < 1 || i > 366)
1973624c752SPedro F. Giffuni 				return (NULL);
198d7641983SJoerg Wunsch 
19933dbb0a6SSheldon Hearn 			tm->tm_yday = i - 1;
200d7641983SJoerg Wunsch 			break;
201d7641983SJoerg Wunsch 
202d7641983SJoerg Wunsch 		case 'M':
203d7641983SJoerg Wunsch 		case 'S':
2043c87aa1dSDavid Chisnall 			if (*buf == 0 ||
2053c87aa1dSDavid Chisnall 				isspace_l((unsigned char)*buf, locale))
206d7641983SJoerg Wunsch 				break;
207d7641983SJoerg Wunsch 
2083c87aa1dSDavid Chisnall 			if (!isdigit_l((unsigned char)*buf, locale))
2093624c752SPedro F. Giffuni 				return (NULL);
210d7641983SJoerg Wunsch 
211398592ffSSheldon Hearn 			len = 2;
2123c87aa1dSDavid Chisnall 			for (i = 0; len && *buf != 0 &&
2133c87aa1dSDavid Chisnall 				isdigit_l((unsigned char)*buf, locale); buf++){
214d7641983SJoerg Wunsch 				i *= 10;
215d7641983SJoerg Wunsch 				i += *buf - '0';
216398592ffSSheldon Hearn 				len--;
217d7641983SJoerg Wunsch 			}
21833dbb0a6SSheldon Hearn 
21933dbb0a6SSheldon Hearn 			if (c == 'M') {
220d7641983SJoerg Wunsch 				if (i > 59)
2213624c752SPedro F. Giffuni 					return (NULL);
222d7641983SJoerg Wunsch 				tm->tm_min = i;
22333dbb0a6SSheldon Hearn 			} else {
22433dbb0a6SSheldon Hearn 				if (i > 60)
2253624c752SPedro F. Giffuni 					return (NULL);
226d7641983SJoerg Wunsch 				tm->tm_sec = i;
22733dbb0a6SSheldon Hearn 			}
228d7641983SJoerg Wunsch 
229d7641983SJoerg Wunsch 			break;
230d7641983SJoerg Wunsch 
231d7641983SJoerg Wunsch 		case 'H':
232d7641983SJoerg Wunsch 		case 'I':
233d7641983SJoerg Wunsch 		case 'k':
234d7641983SJoerg Wunsch 		case 'l':
235398592ffSSheldon Hearn 			/*
236398592ffSSheldon Hearn 			 * Of these, %l is the only specifier explicitly
237398592ffSSheldon Hearn 			 * documented as not being zero-padded.  However,
238398592ffSSheldon Hearn 			 * there is no harm in allowing zero-padding.
239398592ffSSheldon Hearn 			 *
240398592ffSSheldon Hearn 			 * XXX The %l specifier may gobble one too many
241398592ffSSheldon Hearn 			 * digits if used incorrectly.
242398592ffSSheldon Hearn 			 */
2433c87aa1dSDavid Chisnall 			if (!isdigit_l((unsigned char)*buf, locale))
2443624c752SPedro F. Giffuni 				return (NULL);
245d7641983SJoerg Wunsch 
246398592ffSSheldon Hearn 			len = 2;
2473c87aa1dSDavid Chisnall 			for (i = 0; len && *buf != 0 &&
2483c87aa1dSDavid Chisnall 			     isdigit_l((unsigned char)*buf, locale); buf++) {
249d7641983SJoerg Wunsch 				i *= 10;
250d7641983SJoerg Wunsch 				i += *buf - '0';
251398592ffSSheldon Hearn 				len--;
252d7641983SJoerg Wunsch 			}
253d7641983SJoerg Wunsch 			if (c == 'H' || c == 'k') {
254d7641983SJoerg Wunsch 				if (i > 23)
2553624c752SPedro F. Giffuni 					return (NULL);
256882f32c1SSheldon Hearn 			} else if (i > 12)
2573624c752SPedro F. Giffuni 				return (NULL);
258d7641983SJoerg Wunsch 
259d7641983SJoerg Wunsch 			tm->tm_hour = i;
260d7641983SJoerg Wunsch 
261d7641983SJoerg Wunsch 			break;
262d7641983SJoerg Wunsch 
263d7641983SJoerg Wunsch 		case 'p':
264398592ffSSheldon Hearn 			/*
265398592ffSSheldon Hearn 			 * XXX This is bogus if parsed before hour-related
266398592ffSSheldon Hearn 			 * specifiers.
267398592ffSSheldon Hearn 			 */
268930cd711SAlexey Zelkin 			len = strlen(tptr->am);
2693c87aa1dSDavid Chisnall 			if (strncasecmp_l(buf, tptr->am, len, locale) == 0) {
270d7641983SJoerg Wunsch 				if (tm->tm_hour > 12)
2713624c752SPedro F. Giffuni 					return (NULL);
272d7641983SJoerg Wunsch 				if (tm->tm_hour == 12)
273d7641983SJoerg Wunsch 					tm->tm_hour = 0;
274d7641983SJoerg Wunsch 				buf += len;
275d7641983SJoerg Wunsch 				break;
276d7641983SJoerg Wunsch 			}
277d7641983SJoerg Wunsch 
278930cd711SAlexey Zelkin 			len = strlen(tptr->pm);
2793c87aa1dSDavid Chisnall 			if (strncasecmp_l(buf, tptr->pm, len, locale) == 0) {
280d7641983SJoerg Wunsch 				if (tm->tm_hour > 12)
2813624c752SPedro F. Giffuni 					return (NULL);
282d7641983SJoerg Wunsch 				if (tm->tm_hour != 12)
283d7641983SJoerg Wunsch 					tm->tm_hour += 12;
284d7641983SJoerg Wunsch 				buf += len;
285d7641983SJoerg Wunsch 				break;
286d7641983SJoerg Wunsch 			}
287d7641983SJoerg Wunsch 
2883624c752SPedro F. Giffuni 			return (NULL);
289d7641983SJoerg Wunsch 
290d7641983SJoerg Wunsch 		case 'A':
291d7641983SJoerg Wunsch 		case 'a':
292930cd711SAlexey Zelkin 			for (i = 0; i < asizeof(tptr->weekday); i++) {
293930cd711SAlexey Zelkin 				len = strlen(tptr->weekday[i]);
2943c87aa1dSDavid Chisnall 				if (strncasecmp_l(buf, tptr->weekday[i],
2953c87aa1dSDavid Chisnall 						len, locale) == 0)
296d7641983SJoerg Wunsch 					break;
297930cd711SAlexey Zelkin 				len = strlen(tptr->wday[i]);
2983c87aa1dSDavid Chisnall 				if (strncasecmp_l(buf, tptr->wday[i],
2993c87aa1dSDavid Chisnall 						len, locale) == 0)
300d7641983SJoerg Wunsch 					break;
301d7641983SJoerg Wunsch 			}
302930cd711SAlexey Zelkin 			if (i == asizeof(tptr->weekday))
3033624c752SPedro F. Giffuni 				return (NULL);
304d7641983SJoerg Wunsch 
305d7641983SJoerg Wunsch 			tm->tm_wday = i;
306d7641983SJoerg Wunsch 			buf += len;
307d7641983SJoerg Wunsch 			break;
308d7641983SJoerg Wunsch 
30933dbb0a6SSheldon Hearn 		case 'U':
31033dbb0a6SSheldon Hearn 		case 'W':
31133dbb0a6SSheldon Hearn 			/*
31233dbb0a6SSheldon Hearn 			 * XXX This is bogus, as we can not assume any valid
31333dbb0a6SSheldon Hearn 			 * information present in the tm structure at this
31433dbb0a6SSheldon Hearn 			 * point to calculate a real value, so just check the
31533dbb0a6SSheldon Hearn 			 * range for now.
31633dbb0a6SSheldon Hearn 			 */
3173c87aa1dSDavid Chisnall 			if (!isdigit_l((unsigned char)*buf, locale))
3183624c752SPedro F. Giffuni 				return (NULL);
31933dbb0a6SSheldon Hearn 
320398592ffSSheldon Hearn 			len = 2;
3213c87aa1dSDavid Chisnall 			for (i = 0; len && *buf != 0 &&
3223c87aa1dSDavid Chisnall 			     isdigit_l((unsigned char)*buf, locale); buf++) {
32333dbb0a6SSheldon Hearn 				i *= 10;
32433dbb0a6SSheldon Hearn 				i += *buf - '0';
325398592ffSSheldon Hearn 				len--;
32633dbb0a6SSheldon Hearn 			}
32733dbb0a6SSheldon Hearn 			if (i > 53)
3283624c752SPedro F. Giffuni 				return (NULL);
32933dbb0a6SSheldon Hearn 
33033dbb0a6SSheldon Hearn 			break;
33133dbb0a6SSheldon Hearn 
33233dbb0a6SSheldon Hearn 		case 'w':
3333c87aa1dSDavid Chisnall 			if (!isdigit_l((unsigned char)*buf, locale))
3343624c752SPedro F. Giffuni 				return (NULL);
33533dbb0a6SSheldon Hearn 
336398592ffSSheldon Hearn 			i = *buf - '0';
33733dbb0a6SSheldon Hearn 			if (i > 6)
3383624c752SPedro F. Giffuni 				return (NULL);
33933dbb0a6SSheldon Hearn 
34033dbb0a6SSheldon Hearn 			tm->tm_wday = i;
34133dbb0a6SSheldon Hearn 
34233dbb0a6SSheldon Hearn 			break;
34333dbb0a6SSheldon Hearn 
344d7641983SJoerg Wunsch 		case 'e':
345398592ffSSheldon Hearn 			/*
346cd9dcb03SPedro F. Giffuni 			 * With %e format, our strftime(3) adds a blank space
347cd9dcb03SPedro F. Giffuni 			 * before single digits.
348cd9dcb03SPedro F. Giffuni 			 */
349cd9dcb03SPedro F. Giffuni 			if (*buf != 0 &&
350cd9dcb03SPedro F. Giffuni 			    isspace_l((unsigned char)*buf, locale))
351cd9dcb03SPedro F. Giffuni 			       buf++;
352cd9dcb03SPedro F. Giffuni 			/* FALLTHROUGH */
353cd9dcb03SPedro F. Giffuni 		case 'd':
354cd9dcb03SPedro F. Giffuni 			/*
355cd9dcb03SPedro F. Giffuni 			 * The %e specifier was once explicitly documented as
356cd9dcb03SPedro F. Giffuni 			 * not being zero-padded but was later changed to
357cd9dcb03SPedro F. Giffuni 			 * equivalent to %d.  There is no harm in allowing
358398592ffSSheldon Hearn 			 * such padding.
359398592ffSSheldon Hearn 			 *
360398592ffSSheldon Hearn 			 * XXX The %e specifier may gobble one too many
361398592ffSSheldon Hearn 			 * digits if used incorrectly.
362398592ffSSheldon Hearn 			 */
3633c87aa1dSDavid Chisnall 			if (!isdigit_l((unsigned char)*buf, locale))
3643624c752SPedro F. Giffuni 				return (NULL);
365d7641983SJoerg Wunsch 
366398592ffSSheldon Hearn 			len = 2;
3673c87aa1dSDavid Chisnall 			for (i = 0; len && *buf != 0 &&
3683c87aa1dSDavid Chisnall 			     isdigit_l((unsigned char)*buf, locale); buf++) {
369d7641983SJoerg Wunsch 				i *= 10;
370d7641983SJoerg Wunsch 				i += *buf - '0';
371398592ffSSheldon Hearn 				len--;
372d7641983SJoerg Wunsch 			}
373d7641983SJoerg Wunsch 			if (i > 31)
3743624c752SPedro F. Giffuni 				return (NULL);
375d7641983SJoerg Wunsch 
376d7641983SJoerg Wunsch 			tm->tm_mday = i;
377d7641983SJoerg Wunsch 
378d7641983SJoerg Wunsch 			break;
379d7641983SJoerg Wunsch 
380d7641983SJoerg Wunsch 		case 'B':
381d7641983SJoerg Wunsch 		case 'b':
382d7641983SJoerg Wunsch 		case 'h':
383930cd711SAlexey Zelkin 			for (i = 0; i < asizeof(tptr->month); i++) {
3841d6c9941SAndrey A. Chernov 				if (Oalternative) {
3851d6c9941SAndrey A. Chernov 					if (c == 'B') {
386930cd711SAlexey Zelkin 						len = strlen(tptr->alt_month[i]);
3873c87aa1dSDavid Chisnall 						if (strncasecmp_l(buf,
388930cd711SAlexey Zelkin 								tptr->alt_month[i],
3893c87aa1dSDavid Chisnall 								len, locale) == 0)
3901d6c9941SAndrey A. Chernov 							break;
3911d6c9941SAndrey A. Chernov 					}
3921d6c9941SAndrey A. Chernov 				} else {
393930cd711SAlexey Zelkin 					len = strlen(tptr->month[i]);
3943c87aa1dSDavid Chisnall 					if (strncasecmp_l(buf, tptr->month[i],
3953c87aa1dSDavid Chisnall 							len, locale) == 0)
396d7641983SJoerg Wunsch 						break;
3973d74e220SEdwin Groothuis 				}
3983d74e220SEdwin Groothuis 			}
3993d74e220SEdwin Groothuis 			/*
4003d74e220SEdwin Groothuis 			 * Try the abbreviated month name if the full name
4013d74e220SEdwin Groothuis 			 * wasn't found and Oalternative was not requested.
4023d74e220SEdwin Groothuis 			 */
4033d74e220SEdwin Groothuis 			if (i == asizeof(tptr->month) && !Oalternative) {
4043d74e220SEdwin Groothuis 				for (i = 0; i < asizeof(tptr->month); i++) {
405930cd711SAlexey Zelkin 					len = strlen(tptr->mon[i]);
4063c87aa1dSDavid Chisnall 					if (strncasecmp_l(buf, tptr->mon[i],
4073c87aa1dSDavid Chisnall 							len, locale) == 0)
408d7641983SJoerg Wunsch 						break;
409d7641983SJoerg Wunsch 				}
4101d6c9941SAndrey A. Chernov 			}
411930cd711SAlexey Zelkin 			if (i == asizeof(tptr->month))
4123624c752SPedro F. Giffuni 				return (NULL);
413d7641983SJoerg Wunsch 
414d7641983SJoerg Wunsch 			tm->tm_mon = i;
415d7641983SJoerg Wunsch 			buf += len;
416d7641983SJoerg Wunsch 			break;
417d7641983SJoerg Wunsch 
418d7641983SJoerg Wunsch 		case 'm':
4193c87aa1dSDavid Chisnall 			if (!isdigit_l((unsigned char)*buf, locale))
4203624c752SPedro F. Giffuni 				return (NULL);
421d7641983SJoerg Wunsch 
422398592ffSSheldon Hearn 			len = 2;
4233c87aa1dSDavid Chisnall 			for (i = 0; len && *buf != 0 &&
4243c87aa1dSDavid Chisnall 			     isdigit_l((unsigned char)*buf, locale); buf++) {
425d7641983SJoerg Wunsch 				i *= 10;
426d7641983SJoerg Wunsch 				i += *buf - '0';
427398592ffSSheldon Hearn 				len--;
428d7641983SJoerg Wunsch 			}
429d7641983SJoerg Wunsch 			if (i < 1 || i > 12)
4303624c752SPedro F. Giffuni 				return (NULL);
431d7641983SJoerg Wunsch 
432d7641983SJoerg Wunsch 			tm->tm_mon = i - 1;
433d7641983SJoerg Wunsch 
434d7641983SJoerg Wunsch 			break;
435d7641983SJoerg Wunsch 
436427fc5edSDima Dorfman 		case 's':
437427fc5edSDima Dorfman 			{
438427fc5edSDima Dorfman 			char *cp;
439c91e947dSJacques Vidrine 			int sverrno;
440c91e947dSJacques Vidrine 			long n;
441427fc5edSDima Dorfman 			time_t t;
442427fc5edSDima Dorfman 
443c91e947dSJacques Vidrine 			sverrno = errno;
444c91e947dSJacques Vidrine 			errno = 0;
4453c87aa1dSDavid Chisnall 			n = strtol_l(buf, &cp, 10, locale);
446c91e947dSJacques Vidrine 			if (errno == ERANGE || (long)(t = n) != n) {
447c91e947dSJacques Vidrine 				errno = sverrno;
4483624c752SPedro F. Giffuni 				return (NULL);
449c91e947dSJacques Vidrine 			}
450c91e947dSJacques Vidrine 			errno = sverrno;
451427fc5edSDima Dorfman 			buf = cp;
452427fc5edSDima Dorfman 			gmtime_r(&t, tm);
453fe71e0b8SMike Makonnen 			*GMTp = 1;
454427fc5edSDima Dorfman 			}
455427fc5edSDima Dorfman 			break;
456427fc5edSDima Dorfman 
457d7641983SJoerg Wunsch 		case 'Y':
458d7641983SJoerg Wunsch 		case 'y':
4593c87aa1dSDavid Chisnall 			if (*buf == 0 ||
4603c87aa1dSDavid Chisnall 			    isspace_l((unsigned char)*buf, locale))
461d7641983SJoerg Wunsch 				break;
462d7641983SJoerg Wunsch 
4633c87aa1dSDavid Chisnall 			if (!isdigit_l((unsigned char)*buf, locale))
4643624c752SPedro F. Giffuni 				return (NULL);
465d7641983SJoerg Wunsch 
466398592ffSSheldon Hearn 			len = (c == 'Y') ? 4 : 2;
4673c87aa1dSDavid Chisnall 			for (i = 0; len && *buf != 0 &&
4683c87aa1dSDavid Chisnall 			     isdigit_l((unsigned char)*buf, locale); buf++) {
469d7641983SJoerg Wunsch 				i *= 10;
470d7641983SJoerg Wunsch 				i += *buf - '0';
471398592ffSSheldon Hearn 				len--;
472d7641983SJoerg Wunsch 			}
473d7641983SJoerg Wunsch 			if (c == 'Y')
474d7641983SJoerg Wunsch 				i -= 1900;
475aba0410bSWes Peters 			if (c == 'y' && i < 69)
476a00b1d8fSWes Peters 				i += 100;
477d7641983SJoerg Wunsch 			if (i < 0)
4783624c752SPedro F. Giffuni 				return (NULL);
479d7641983SJoerg Wunsch 
480d7641983SJoerg Wunsch 			tm->tm_year = i;
481d7641983SJoerg Wunsch 
482d7641983SJoerg Wunsch 			break;
483b47f20dfSDavid E. O'Brien 
484b47f20dfSDavid E. O'Brien 		case 'Z':
485b47f20dfSDavid E. O'Brien 			{
486b47f20dfSDavid E. O'Brien 			const char *cp;
487b47f20dfSDavid E. O'Brien 			char *zonestr;
488b47f20dfSDavid E. O'Brien 
4893c87aa1dSDavid Chisnall 			for (cp = buf; *cp &&
4903c87aa1dSDavid Chisnall 			     isupper_l((unsigned char)*cp, locale); ++cp) {
4913c87aa1dSDavid Chisnall 				/*empty*/}
492b47f20dfSDavid E. O'Brien 			if (cp - buf) {
493b47f20dfSDavid E. O'Brien 				zonestr = alloca(cp - buf + 1);
494b47f20dfSDavid E. O'Brien 				strncpy(zonestr, buf, cp - buf);
495b47f20dfSDavid E. O'Brien 				zonestr[cp - buf] = '\0';
496b47f20dfSDavid E. O'Brien 				tzset();
497b47f20dfSDavid E. O'Brien 				if (0 == strcmp(zonestr, "GMT")) {
498fe71e0b8SMike Makonnen 				    *GMTp = 1;
499b47f20dfSDavid E. O'Brien 				} else if (0 == strcmp(zonestr, tzname[0])) {
500b47f20dfSDavid E. O'Brien 				    tm->tm_isdst = 0;
501b47f20dfSDavid E. O'Brien 				} else if (0 == strcmp(zonestr, tzname[1])) {
502b47f20dfSDavid E. O'Brien 				    tm->tm_isdst = 1;
503b47f20dfSDavid E. O'Brien 				} else {
5043624c752SPedro F. Giffuni 				    return (NULL);
505b47f20dfSDavid E. O'Brien 				}
506b47f20dfSDavid E. O'Brien 				buf += cp - buf;
507b47f20dfSDavid E. O'Brien 			}
508b47f20dfSDavid E. O'Brien 			}
509b47f20dfSDavid E. O'Brien 			break;
51040523da7SXin LI 
51140523da7SXin LI 		case 'z':
51240523da7SXin LI 			{
51340523da7SXin LI 			int sign = 1;
51440523da7SXin LI 
51540523da7SXin LI 			if (*buf != '+') {
51640523da7SXin LI 				if (*buf == '-')
51740523da7SXin LI 					sign = -1;
51840523da7SXin LI 				else
5193624c752SPedro F. Giffuni 					return (NULL);
52040523da7SXin LI 			}
52140523da7SXin LI 
52240523da7SXin LI 			buf++;
52340523da7SXin LI 			i = 0;
52440523da7SXin LI 			for (len = 4; len > 0; len--) {
5253c87aa1dSDavid Chisnall 				if (isdigit_l((unsigned char)*buf, locale)) {
52640523da7SXin LI 					i *= 10;
52740523da7SXin LI 					i += *buf - '0';
52840523da7SXin LI 					buf++;
52940523da7SXin LI 				} else
5303624c752SPedro F. Giffuni 					return (NULL);
53140523da7SXin LI 			}
53240523da7SXin LI 
53340523da7SXin LI 			tm->tm_hour -= sign * (i / 100);
53440523da7SXin LI 			tm->tm_min  -= sign * (i % 100);
53540523da7SXin LI 			*GMTp = 1;
53640523da7SXin LI 			}
53740523da7SXin LI 			break;
538cd9dcb03SPedro F. Giffuni 
539cd9dcb03SPedro F. Giffuni 		case 'n':
540cd9dcb03SPedro F. Giffuni 		case 't':
541cd9dcb03SPedro F. Giffuni 			while (isspace_l((unsigned char)*buf, locale))
542cd9dcb03SPedro F. Giffuni 				buf++;
543cd9dcb03SPedro F. Giffuni 			break;
544d7641983SJoerg Wunsch 		}
545d7641983SJoerg Wunsch 	}
5463624c752SPedro F. Giffuni 	return ((char *)buf);
547cb7a4779SDavid E. O'Brien }
548d7641983SJoerg Wunsch 
549cb7a4779SDavid E. O'Brien 
550cb7a4779SDavid E. O'Brien char *
5513c87aa1dSDavid Chisnall strptime_l(const char * __restrict buf, const char * __restrict fmt,
5523c87aa1dSDavid Chisnall     struct tm * __restrict tm, locale_t loc)
553cb7a4779SDavid E. O'Brien {
554cb7a4779SDavid E. O'Brien 	char *ret;
555fe71e0b8SMike Makonnen 	int gmt;
5563c87aa1dSDavid Chisnall 	FIX_LOCALE(loc);
557cb7a4779SDavid E. O'Brien 
558fe71e0b8SMike Makonnen 	gmt = 0;
5593c87aa1dSDavid Chisnall 	ret = _strptime(buf, fmt, tm, &gmt, loc);
5606c688436SMike Makonnen 	if (ret && gmt) {
5616c688436SMike Makonnen 		time_t t = timegm(tm);
562b47f20dfSDavid E. O'Brien 		localtime_r(&t, tm);
563b47f20dfSDavid E. O'Brien 	}
564cb7a4779SDavid E. O'Brien 
565fe71e0b8SMike Makonnen 	return (ret);
566d7641983SJoerg Wunsch }
5673c87aa1dSDavid Chisnall char *
5683c87aa1dSDavid Chisnall strptime(const char * __restrict buf, const char * __restrict fmt,
5693c87aa1dSDavid Chisnall     struct tm * __restrict tm)
5703c87aa1dSDavid Chisnall {
5713c87aa1dSDavid Chisnall 	return strptime_l(buf, fmt, tm, __get_locale());
5723c87aa1dSDavid Chisnall }
573