strptime.c revision 267677
1267677Spfg/*-
228019Sjoerg * Copyright (c) 1994 Powerdog Industries.  All rights reserved.
328019Sjoerg *
4227753Stheraven * Copyright (c) 2011 The FreeBSD Foundation
5227753Stheraven * All rights reserved.
6227753Stheraven * Portions of this software were developed by David Chisnall
7227753Stheraven * under sponsorship from the FreeBSD Foundation.
8227753Stheraven *
928021Sjoerg * Redistribution and use in source and binary forms, with or without
1028019Sjoerg * modification, are permitted provided that the following conditions
1128019Sjoerg * are met:
1228019Sjoerg * 1. Redistributions of source code must retain the above copyright
1328019Sjoerg *    notice, this list of conditions and the following disclaimer.
1428019Sjoerg * 2. Redistributions in binary form must reproduce the above copyright
1528019Sjoerg *    notice, this list of conditions and the following disclaimer
1628019Sjoerg *    in the documentation and/or other materials provided with the
1728019Sjoerg *    distribution.
1828019Sjoerg *
1928019Sjoerg * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY
2028019Sjoerg * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2128019Sjoerg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2228019Sjoerg * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE
2328019Sjoerg * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2428019Sjoerg * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2528019Sjoerg * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
2628019Sjoerg * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
2728019Sjoerg * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
2828019Sjoerg * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
2928019Sjoerg * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30267677Spfg *
31267677Spfg * The views and conclusions contained in the software and documentation
32267677Spfg * are those of the authors and should not be interpreted as representing
33267677Spfg * official policies, either expressed or implied, of Powerdog Industries.
3428019Sjoerg */
3528019Sjoerg
36111010Snectar#include <sys/cdefs.h>
3728019Sjoerg#ifndef lint
3828021Sjoerg#ifndef NOID
39111010Snectarstatic char copyright[] __unused =
4028019Sjoerg"@(#) Copyright (c) 1994 Powerdog Industries.  All rights reserved.";
41111010Snectarstatic char sccsid[] __unused = "@(#)strptime.c	0.1 (Powerdog) 94/03/27";
4228021Sjoerg#endif /* !defined NOID */
4328019Sjoerg#endif /* not lint */
4492986Sobrien__FBSDID("$FreeBSD: stable/10/lib/libc/stdtime/strptime.c 267677 2014-06-20 15:43:58Z pfg $");
4528019Sjoerg
4671579Sdeischen#include "namespace.h"
4728019Sjoerg#include <time.h>
4828019Sjoerg#include <ctype.h>
49122830Snectar#include <errno.h>
5079664Sdd#include <stdlib.h>
5128019Sjoerg#include <string.h>
5248614Sobrien#include <pthread.h>
5371579Sdeischen#include "un-namespace.h"
5471579Sdeischen#include "libc_private.h"
5528021Sjoerg#include "timelocal.h"
5628019Sjoerg
57227753Stheravenstatic char * _strptime(const char *, const char *, struct tm *, int *, locale_t);
5848614Sobrien
5928021Sjoerg#define asizeof(a)	(sizeof (a) / sizeof ((a)[0]))
6028019Sjoerg
6148614Sobrienstatic char *
62227753Stheraven_strptime(const char *buf, const char *fmt, struct tm *tm, int *GMTp,
63227753Stheraven		locale_t locale)
6428019Sjoerg{
6528021Sjoerg	char	c;
6628021Sjoerg	const char *ptr;
6728021Sjoerg	int	i,
6828021Sjoerg		len;
6953941Sache	int Ealternative, Oalternative;
70227753Stheraven	struct lc_time_T *tptr = __get_current_time_locale(locale);
7128019Sjoerg
7228021Sjoerg	ptr = fmt;
7328021Sjoerg	while (*ptr != 0) {
7428021Sjoerg		if (*buf == 0)
7528021Sjoerg			break;
7628019Sjoerg
7728021Sjoerg		c = *ptr++;
7828019Sjoerg
7928021Sjoerg		if (c != '%') {
80227753Stheraven			if (isspace_l((unsigned char)c, locale))
81227753Stheraven				while (*buf != 0 &&
82227753Stheraven				       isspace_l((unsigned char)*buf, locale))
8328021Sjoerg					buf++;
8428021Sjoerg			else if (c != *buf++)
8528021Sjoerg				return 0;
8628021Sjoerg			continue;
8728021Sjoerg		}
8828019Sjoerg
8953941Sache		Ealternative = 0;
9053941Sache		Oalternative = 0;
9153941Sachelabel:
9228021Sjoerg		c = *ptr++;
9328021Sjoerg		switch (c) {
9428021Sjoerg		case 0:
9528021Sjoerg		case '%':
9628021Sjoerg			if (*buf++ != '%')
9728021Sjoerg				return 0;
9828021Sjoerg			break;
9928019Sjoerg
10053941Sache		case '+':
101227753Stheraven			buf = _strptime(buf, tptr->date_fmt, tm, GMTp, locale);
10228021Sjoerg			if (buf == 0)
10328021Sjoerg				return 0;
10428021Sjoerg			break;
10528019Sjoerg
10653941Sache		case 'C':
107227753Stheraven			if (!isdigit_l((unsigned char)*buf, locale))
10853941Sache				return 0;
10953941Sache
11054316Ssheldonh			/* XXX This will break for 3-digit centuries. */
11154316Ssheldonh			len = 2;
112227753Stheraven			for (i = 0; len && *buf != 0 &&
113227753Stheraven			     isdigit_l((unsigned char)*buf, locale); buf++) {
11453941Sache				i *= 10;
11553941Sache				i += *buf - '0';
11654316Ssheldonh				len--;
11753941Sache			}
11853941Sache			if (i < 19)
11953941Sache				return 0;
12053941Sache
12153941Sache			tm->tm_year = i * 100 - 1900;
12253941Sache			break;
12353941Sache
12428021Sjoerg		case 'c':
125227753Stheraven			buf = _strptime(buf, tptr->c_fmt, tm, GMTp, locale);
12628021Sjoerg			if (buf == 0)
12728021Sjoerg				return 0;
12828021Sjoerg			break;
12928019Sjoerg
13028021Sjoerg		case 'D':
131227753Stheraven			buf = _strptime(buf, "%m/%d/%y", tm, GMTp, locale);
13228021Sjoerg			if (buf == 0)
13328021Sjoerg				return 0;
13428021Sjoerg			break;
13528019Sjoerg
13653941Sache		case 'E':
13753960Sache			if (Ealternative || Oalternative)
13853960Sache				break;
13953941Sache			Ealternative++;
14053941Sache			goto label;
14153941Sache
14253941Sache		case 'O':
14353960Sache			if (Ealternative || Oalternative)
14453960Sache				break;
14553941Sache			Oalternative++;
14653941Sache			goto label;
14753941Sache
14853960Sache		case 'F':
149227753Stheraven			buf = _strptime(buf, "%Y-%m-%d", tm, GMTp, locale);
15074412Sache			if (buf == 0)
15174412Sache				return 0;
15274412Sache			break;
15374412Sache
15428021Sjoerg		case 'R':
155227753Stheraven			buf = _strptime(buf, "%H:%M", tm, GMTp, locale);
15628021Sjoerg			if (buf == 0)
15728021Sjoerg				return 0;
15828021Sjoerg			break;
15928019Sjoerg
16028021Sjoerg		case 'r':
161227753Stheraven			buf = _strptime(buf, tptr->ampm_fmt, tm, GMTp, locale);
16228021Sjoerg			if (buf == 0)
16328021Sjoerg				return 0;
16428021Sjoerg			break;
16528019Sjoerg
16628021Sjoerg		case 'T':
167227753Stheraven			buf = _strptime(buf, "%H:%M:%S", tm, GMTp, locale);
16828021Sjoerg			if (buf == 0)
16928021Sjoerg				return 0;
17028021Sjoerg			break;
17128019Sjoerg
17228021Sjoerg		case 'X':
173227753Stheraven			buf = _strptime(buf, tptr->X_fmt, tm, GMTp, locale);
17428021Sjoerg			if (buf == 0)
17528021Sjoerg				return 0;
17628021Sjoerg			break;
17728019Sjoerg
17828021Sjoerg		case 'x':
179227753Stheraven			buf = _strptime(buf, tptr->x_fmt, tm, GMTp, locale);
18028021Sjoerg			if (buf == 0)
18128021Sjoerg				return 0;
18228021Sjoerg			break;
18328019Sjoerg
18428021Sjoerg		case 'j':
185227753Stheraven			if (!isdigit_l((unsigned char)*buf, locale))
18628021Sjoerg				return 0;
18728019Sjoerg
18854316Ssheldonh			len = 3;
189227753Stheraven			for (i = 0; len && *buf != 0 &&
190227753Stheraven			     isdigit_l((unsigned char)*buf, locale); buf++){
19128021Sjoerg				i *= 10;
19228021Sjoerg				i += *buf - '0';
19354316Ssheldonh				len--;
19428021Sjoerg			}
19553083Ssheldonh			if (i < 1 || i > 366)
19628021Sjoerg				return 0;
19728019Sjoerg
19853083Ssheldonh			tm->tm_yday = i - 1;
19928021Sjoerg			break;
20028019Sjoerg
20128021Sjoerg		case 'M':
20228021Sjoerg		case 'S':
203227753Stheraven			if (*buf == 0 ||
204227753Stheraven				isspace_l((unsigned char)*buf, locale))
20528021Sjoerg				break;
20628019Sjoerg
207227753Stheraven			if (!isdigit_l((unsigned char)*buf, locale))
20828021Sjoerg				return 0;
20928019Sjoerg
21054316Ssheldonh			len = 2;
211227753Stheraven			for (i = 0; len && *buf != 0 &&
212227753Stheraven				isdigit_l((unsigned char)*buf, locale); buf++){
21328021Sjoerg				i *= 10;
21428021Sjoerg				i += *buf - '0';
21554316Ssheldonh				len--;
21628021Sjoerg			}
21728019Sjoerg
21853083Ssheldonh			if (c == 'M') {
21953083Ssheldonh				if (i > 59)
22053083Ssheldonh					return 0;
22128021Sjoerg				tm->tm_min = i;
22253083Ssheldonh			} else {
22353083Ssheldonh				if (i > 60)
22453083Ssheldonh					return 0;
22528021Sjoerg				tm->tm_sec = i;
22653083Ssheldonh			}
22728019Sjoerg
228227753Stheraven			if (*buf != 0 &&
229227753Stheraven				isspace_l((unsigned char)*buf, locale))
230227753Stheraven				while (*ptr != 0 &&
231227753Stheraven				       !isspace_l((unsigned char)*ptr, locale))
23228021Sjoerg					ptr++;
23328021Sjoerg			break;
23428019Sjoerg
23528021Sjoerg		case 'H':
23628021Sjoerg		case 'I':
23728021Sjoerg		case 'k':
23828021Sjoerg		case 'l':
23954316Ssheldonh			/*
24054316Ssheldonh			 * Of these, %l is the only specifier explicitly
24154316Ssheldonh			 * documented as not being zero-padded.  However,
24254316Ssheldonh			 * there is no harm in allowing zero-padding.
24354316Ssheldonh			 *
24454316Ssheldonh			 * XXX The %l specifier may gobble one too many
24554316Ssheldonh			 * digits if used incorrectly.
24654316Ssheldonh			 */
247227753Stheraven			if (!isdigit_l((unsigned char)*buf, locale))
24828021Sjoerg				return 0;
24928019Sjoerg
25054316Ssheldonh			len = 2;
251227753Stheraven			for (i = 0; len && *buf != 0 &&
252227753Stheraven			     isdigit_l((unsigned char)*buf, locale); buf++) {
25328021Sjoerg				i *= 10;
25428021Sjoerg				i += *buf - '0';
25554316Ssheldonh				len--;
25628021Sjoerg			}
25728021Sjoerg			if (c == 'H' || c == 'k') {
25828021Sjoerg				if (i > 23)
25928021Sjoerg					return 0;
26054301Ssheldonh			} else if (i > 12)
26128021Sjoerg				return 0;
26228019Sjoerg
26328021Sjoerg			tm->tm_hour = i;
26428019Sjoerg
265227753Stheraven			if (*buf != 0 &&
266227753Stheraven			    isspace_l((unsigned char)*buf, locale))
267227753Stheraven				while (*ptr != 0 &&
268227753Stheraven				       !isspace_l((unsigned char)*ptr, locale))
26928021Sjoerg					ptr++;
27028021Sjoerg			break;
27128019Sjoerg
27228021Sjoerg		case 'p':
27354316Ssheldonh			/*
27454316Ssheldonh			 * XXX This is bogus if parsed before hour-related
27554316Ssheldonh			 * specifiers.
27654316Ssheldonh			 */
27772168Sphantom			len = strlen(tptr->am);
278227753Stheraven			if (strncasecmp_l(buf, tptr->am, len, locale) == 0) {
27928021Sjoerg				if (tm->tm_hour > 12)
28028021Sjoerg					return 0;
28128021Sjoerg				if (tm->tm_hour == 12)
28228021Sjoerg					tm->tm_hour = 0;
28328021Sjoerg				buf += len;
28428021Sjoerg				break;
28528021Sjoerg			}
28628019Sjoerg
28772168Sphantom			len = strlen(tptr->pm);
288227753Stheraven			if (strncasecmp_l(buf, tptr->pm, len, locale) == 0) {
28928021Sjoerg				if (tm->tm_hour > 12)
29028021Sjoerg					return 0;
29128021Sjoerg				if (tm->tm_hour != 12)
29228021Sjoerg					tm->tm_hour += 12;
29328021Sjoerg				buf += len;
29428021Sjoerg				break;
29528021Sjoerg			}
29628019Sjoerg
29728021Sjoerg			return 0;
29828019Sjoerg
29928021Sjoerg		case 'A':
30028021Sjoerg		case 'a':
30172168Sphantom			for (i = 0; i < asizeof(tptr->weekday); i++) {
30274409Sache				len = strlen(tptr->weekday[i]);
303227753Stheraven				if (strncasecmp_l(buf, tptr->weekday[i],
304227753Stheraven						len, locale) == 0)
30574409Sache					break;
30674409Sache				len = strlen(tptr->wday[i]);
307227753Stheraven				if (strncasecmp_l(buf, tptr->wday[i],
308227753Stheraven						len, locale) == 0)
30974409Sache					break;
31028021Sjoerg			}
31172168Sphantom			if (i == asizeof(tptr->weekday))
31228021Sjoerg				return 0;
31328019Sjoerg
31428021Sjoerg			tm->tm_wday = i;
31528021Sjoerg			buf += len;
31628021Sjoerg			break;
31728019Sjoerg
31853083Ssheldonh		case 'U':
31953083Ssheldonh		case 'W':
32053083Ssheldonh			/*
32153083Ssheldonh			 * XXX This is bogus, as we can not assume any valid
32253083Ssheldonh			 * information present in the tm structure at this
32353083Ssheldonh			 * point to calculate a real value, so just check the
32453083Ssheldonh			 * range for now.
32553083Ssheldonh			 */
326227753Stheraven			if (!isdigit_l((unsigned char)*buf, locale))
32753083Ssheldonh				return 0;
32853083Ssheldonh
32954316Ssheldonh			len = 2;
330227753Stheraven			for (i = 0; len && *buf != 0 &&
331227753Stheraven			     isdigit_l((unsigned char)*buf, locale); buf++) {
33253083Ssheldonh				i *= 10;
33353083Ssheldonh				i += *buf - '0';
33454316Ssheldonh				len--;
33553083Ssheldonh			}
33653083Ssheldonh			if (i > 53)
33753083Ssheldonh				return 0;
33853083Ssheldonh
339227753Stheraven			if (*buf != 0 &&
340227753Stheraven			    isspace_l((unsigned char)*buf, locale))
341227753Stheraven				while (*ptr != 0 &&
342227753Stheraven				       !isspace_l((unsigned char)*ptr, locale))
34353083Ssheldonh					ptr++;
34453083Ssheldonh			break;
34553083Ssheldonh
34653083Ssheldonh		case 'w':
347227753Stheraven			if (!isdigit_l((unsigned char)*buf, locale))
34853083Ssheldonh				return 0;
34953083Ssheldonh
35054316Ssheldonh			i = *buf - '0';
35153083Ssheldonh			if (i > 6)
35253083Ssheldonh				return 0;
35353083Ssheldonh
35453083Ssheldonh			tm->tm_wday = i;
35553083Ssheldonh
356227753Stheraven			if (*buf != 0 &&
357227753Stheraven			    isspace_l((unsigned char)*buf, locale))
358227753Stheraven				while (*ptr != 0 &&
359227753Stheraven				       !isspace_l((unsigned char)*ptr, locale))
36053083Ssheldonh					ptr++;
36153083Ssheldonh			break;
36253083Ssheldonh
36328021Sjoerg		case 'd':
36428021Sjoerg		case 'e':
36554316Ssheldonh			/*
36654316Ssheldonh			 * The %e specifier is explicitly documented as not
36754316Ssheldonh			 * being zero-padded but there is no harm in allowing
36854316Ssheldonh			 * such padding.
36954316Ssheldonh			 *
37054316Ssheldonh			 * XXX The %e specifier may gobble one too many
37154316Ssheldonh			 * digits if used incorrectly.
37254316Ssheldonh			 */
373227753Stheraven			if (!isdigit_l((unsigned char)*buf, locale))
37428021Sjoerg				return 0;
37528019Sjoerg
37654316Ssheldonh			len = 2;
377227753Stheraven			for (i = 0; len && *buf != 0 &&
378227753Stheraven			     isdigit_l((unsigned char)*buf, locale); buf++) {
37928021Sjoerg				i *= 10;
38028021Sjoerg				i += *buf - '0';
38154316Ssheldonh				len--;
38228021Sjoerg			}
38328021Sjoerg			if (i > 31)
38428021Sjoerg				return 0;
38528019Sjoerg
38628021Sjoerg			tm->tm_mday = i;
38728019Sjoerg
388227753Stheraven			if (*buf != 0 &&
389227753Stheraven			    isspace_l((unsigned char)*buf, locale))
390227753Stheraven				while (*ptr != 0 &&
391227753Stheraven				       !isspace_l((unsigned char)*ptr, locale))
39228021Sjoerg					ptr++;
39328021Sjoerg			break;
39428019Sjoerg
39528021Sjoerg		case 'B':
39628021Sjoerg		case 'b':
39728021Sjoerg		case 'h':
39872168Sphantom			for (i = 0; i < asizeof(tptr->month); i++) {
39953941Sache				if (Oalternative) {
40053941Sache					if (c == 'B') {
40172168Sphantom						len = strlen(tptr->alt_month[i]);
402227753Stheraven						if (strncasecmp_l(buf,
40372168Sphantom								tptr->alt_month[i],
404227753Stheraven								len, locale) == 0)
40553941Sache							break;
40653941Sache					}
40753941Sache				} else {
40874409Sache					len = strlen(tptr->month[i]);
409227753Stheraven					if (strncasecmp_l(buf, tptr->month[i],
410227753Stheraven							len, locale) == 0)
41174409Sache						break;
412207830Sedwin				}
413207830Sedwin			}
414207830Sedwin			/*
415207830Sedwin			 * Try the abbreviated month name if the full name
416207830Sedwin			 * wasn't found and Oalternative was not requested.
417207830Sedwin			 */
418207830Sedwin			if (i == asizeof(tptr->month) && !Oalternative) {
419207830Sedwin				for (i = 0; i < asizeof(tptr->month); i++) {
42074409Sache					len = strlen(tptr->mon[i]);
421227753Stheraven					if (strncasecmp_l(buf, tptr->mon[i],
422227753Stheraven							len, locale) == 0)
42374409Sache						break;
42453941Sache				}
42528021Sjoerg			}
42672168Sphantom			if (i == asizeof(tptr->month))
42728021Sjoerg				return 0;
42828019Sjoerg
42928021Sjoerg			tm->tm_mon = i;
43028021Sjoerg			buf += len;
43128021Sjoerg			break;
43228019Sjoerg
43328021Sjoerg		case 'm':
434227753Stheraven			if (!isdigit_l((unsigned char)*buf, locale))
43528021Sjoerg				return 0;
43628019Sjoerg
43754316Ssheldonh			len = 2;
438227753Stheraven			for (i = 0; len && *buf != 0 &&
439227753Stheraven			     isdigit_l((unsigned char)*buf, locale); buf++) {
44028021Sjoerg				i *= 10;
44128021Sjoerg				i += *buf - '0';
44254316Ssheldonh				len--;
44328021Sjoerg			}
44428021Sjoerg			if (i < 1 || i > 12)
44528021Sjoerg				return 0;
44628019Sjoerg
44728021Sjoerg			tm->tm_mon = i - 1;
44828019Sjoerg
449227753Stheraven			if (*buf != 0 &&
450227753Stheraven			    isspace_l((unsigned char)*buf, locale))
451227753Stheraven				while (*ptr != 0 &&
452227753Stheraven				       !isspace_l((unsigned char)*ptr, locale))
45328021Sjoerg					ptr++;
45428021Sjoerg			break;
45528019Sjoerg
45679664Sdd		case 's':
45779664Sdd			{
45879664Sdd			char *cp;
459122830Snectar			int sverrno;
460122830Snectar			long n;
46179664Sdd			time_t t;
46279664Sdd
463122830Snectar			sverrno = errno;
464122830Snectar			errno = 0;
465227753Stheraven			n = strtol_l(buf, &cp, 10, locale);
466122830Snectar			if (errno == ERANGE || (long)(t = n) != n) {
467122830Snectar				errno = sverrno;
46879664Sdd				return 0;
469122830Snectar			}
470122830Snectar			errno = sverrno;
47179664Sdd			buf = cp;
47279664Sdd			gmtime_r(&t, tm);
473112156Smtm			*GMTp = 1;
47479664Sdd			}
47579664Sdd			break;
47679664Sdd
47728021Sjoerg		case 'Y':
47828021Sjoerg		case 'y':
479227753Stheraven			if (*buf == 0 ||
480227753Stheraven			    isspace_l((unsigned char)*buf, locale))
48128021Sjoerg				break;
48228019Sjoerg
483227753Stheraven			if (!isdigit_l((unsigned char)*buf, locale))
48428021Sjoerg				return 0;
48528019Sjoerg
48654316Ssheldonh			len = (c == 'Y') ? 4 : 2;
487227753Stheraven			for (i = 0; len && *buf != 0 &&
488227753Stheraven			     isdigit_l((unsigned char)*buf, locale); buf++) {
48928021Sjoerg				i *= 10;
49028021Sjoerg				i += *buf - '0';
49154316Ssheldonh				len--;
49228021Sjoerg			}
49328021Sjoerg			if (c == 'Y')
49428021Sjoerg				i -= 1900;
49546051Swes			if (c == 'y' && i < 69)
49646042Swes				i += 100;
49728021Sjoerg			if (i < 0)
49828021Sjoerg				return 0;
49928019Sjoerg
50028021Sjoerg			tm->tm_year = i;
50128019Sjoerg
502227753Stheraven			if (*buf != 0 &&
503227753Stheraven			    isspace_l((unsigned char)*buf, locale))
504227753Stheraven				while (*ptr != 0 &&
505227753Stheraven				       !isspace_l((unsigned char)*ptr, locale))
50628021Sjoerg					ptr++;
50728021Sjoerg			break;
50848550Sobrien
50948550Sobrien		case 'Z':
51048550Sobrien			{
51148550Sobrien			const char *cp;
51248550Sobrien			char *zonestr;
51348550Sobrien
514227753Stheraven			for (cp = buf; *cp &&
515227753Stheraven			     isupper_l((unsigned char)*cp, locale); ++cp) {
516227753Stheraven				/*empty*/}
51748550Sobrien			if (cp - buf) {
51848550Sobrien				zonestr = alloca(cp - buf + 1);
51948550Sobrien				strncpy(zonestr, buf, cp - buf);
52048550Sobrien				zonestr[cp - buf] = '\0';
52148550Sobrien				tzset();
52248550Sobrien				if (0 == strcmp(zonestr, "GMT")) {
523112156Smtm				    *GMTp = 1;
52448550Sobrien				} else if (0 == strcmp(zonestr, tzname[0])) {
52548614Sobrien				    tm->tm_isdst = 0;
52648550Sobrien				} else if (0 == strcmp(zonestr, tzname[1])) {
52748614Sobrien				    tm->tm_isdst = 1;
52848550Sobrien				} else {
52948614Sobrien				    return 0;
53048550Sobrien				}
53148550Sobrien				buf += cp - buf;
53248550Sobrien			}
53348550Sobrien			}
53448550Sobrien			break;
535195015Sdelphij
536195015Sdelphij		case 'z':
537195015Sdelphij			{
538195015Sdelphij			int sign = 1;
539195015Sdelphij
540195015Sdelphij			if (*buf != '+') {
541195015Sdelphij				if (*buf == '-')
542195015Sdelphij					sign = -1;
543195015Sdelphij				else
544195015Sdelphij					return 0;
545195015Sdelphij			}
546195015Sdelphij
547195015Sdelphij			buf++;
548195015Sdelphij			i = 0;
549195015Sdelphij			for (len = 4; len > 0; len--) {
550227753Stheraven				if (isdigit_l((unsigned char)*buf, locale)) {
551195015Sdelphij					i *= 10;
552195015Sdelphij					i += *buf - '0';
553195015Sdelphij					buf++;
554195015Sdelphij				} else
555195015Sdelphij					return 0;
556195015Sdelphij			}
557195015Sdelphij
558195015Sdelphij			tm->tm_hour -= sign * (i / 100);
559195015Sdelphij			tm->tm_min  -= sign * (i % 100);
560195015Sdelphij			*GMTp = 1;
561195015Sdelphij			}
562195015Sdelphij			break;
56328021Sjoerg		}
56428021Sjoerg	}
56548614Sobrien	return (char *)buf;
56648614Sobrien}
56728019Sjoerg
56848614Sobrien
56948614Sobrienchar *
570227753Stheravenstrptime_l(const char * __restrict buf, const char * __restrict fmt,
571227753Stheraven    struct tm * __restrict tm, locale_t loc)
57248614Sobrien{
57348614Sobrien	char *ret;
574112156Smtm	int gmt;
575227753Stheraven	FIX_LOCALE(loc);
57648614Sobrien
577112156Smtm	gmt = 0;
578227753Stheraven	ret = _strptime(buf, fmt, tm, &gmt, loc);
579114285Smtm	if (ret && gmt) {
580114285Smtm		time_t t = timegm(tm);
58171579Sdeischen		localtime_r(&t, tm);
58248550Sobrien	}
58348614Sobrien
584112156Smtm	return (ret);
58528019Sjoerg}
586227753Stheravenchar *
587227753Stheravenstrptime(const char * __restrict buf, const char * __restrict fmt,
588227753Stheraven    struct tm * __restrict tm)
589227753Stheraven{
590227753Stheraven	return strptime_l(buf, fmt, tm, __get_locale());
591227753Stheraven}
592