128019Sjoerg/*
228021Sjoerg * Powerdog Industries kindly requests feedback from anyone modifying
328021Sjoerg * this function:
428021Sjoerg *
528021Sjoerg * Date: Thu, 05 Jun 1997 23:17:17 -0400
628021Sjoerg * From: Kevin Ruddy <kevin.ruddy@powerdog.com>
728021Sjoerg * To: James FitzGibbon <james@nexis.net>
828021Sjoerg * Subject: Re: Use of your strptime(3) code (fwd)
928021Sjoerg *
1028021Sjoerg * The reason for the "no mod" clause was so that modifications would
1128021Sjoerg * come back and we could integrate them and reissue so that a wider
1228021Sjoerg * audience could use it (thereby spreading the wealth).  This has
1328021Sjoerg * made it possible to get strptime to work on many operating systems.
1428021Sjoerg * I'm not sure why that's "plain unacceptable" to the FreeBSD team.
1528021Sjoerg *
1628021Sjoerg * Anyway, you can change it to "with or without modification" as
1728021Sjoerg * you see fit.  Enjoy.
1828021Sjoerg *
1928021Sjoerg * Kevin Ruddy
2028021Sjoerg * Powerdog Industries, Inc.
2128021Sjoerg */
2228021Sjoerg/*
2328019Sjoerg * Copyright (c) 1994 Powerdog Industries.  All rights reserved.
2428019Sjoerg *
25227753Stheraven * Copyright (c) 2011 The FreeBSD Foundation
26227753Stheraven * All rights reserved.
27227753Stheraven * Portions of this software were developed by David Chisnall
28227753Stheraven * under sponsorship from the FreeBSD Foundation.
29227753Stheraven *
3028021Sjoerg * Redistribution and use in source and binary forms, with or without
3128019Sjoerg * modification, are permitted provided that the following conditions
3228019Sjoerg * are met:
3328019Sjoerg * 1. Redistributions of source code must retain the above copyright
3428019Sjoerg *    notice, this list of conditions and the following disclaimer.
3528019Sjoerg * 2. Redistributions in binary form must reproduce the above copyright
3628019Sjoerg *    notice, this list of conditions and the following disclaimer
3728019Sjoerg *    in the documentation and/or other materials provided with the
3828019Sjoerg *    distribution.
3928019Sjoerg * 3. All advertising materials mentioning features or use of this
4028019Sjoerg *    software must display the following acknowledgement:
4128019Sjoerg *      This product includes software developed by Powerdog Industries.
4228019Sjoerg * 4. The name of Powerdog Industries may not be used to endorse or
4328019Sjoerg *    promote products derived from this software without specific prior
4428019Sjoerg *    written permission.
4528019Sjoerg *
4628019Sjoerg * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY
4728019Sjoerg * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
4828019Sjoerg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
4928019Sjoerg * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE
5028019Sjoerg * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
5128019Sjoerg * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
5228019Sjoerg * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
5328019Sjoerg * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
5428019Sjoerg * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
5528019Sjoerg * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
5628019Sjoerg * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
5728019Sjoerg */
5828019Sjoerg
59111010Snectar#include <sys/cdefs.h>
6028019Sjoerg#ifndef lint
6128021Sjoerg#ifndef NOID
62111010Snectarstatic char copyright[] __unused =
6328019Sjoerg"@(#) Copyright (c) 1994 Powerdog Industries.  All rights reserved.";
64111010Snectarstatic char sccsid[] __unused = "@(#)strptime.c	0.1 (Powerdog) 94/03/27";
6528021Sjoerg#endif /* !defined NOID */
6628019Sjoerg#endif /* not lint */
6792986Sobrien__FBSDID("$FreeBSD$");
6828019Sjoerg
6971579Sdeischen#include "namespace.h"
7028019Sjoerg#include <time.h>
7128019Sjoerg#include <ctype.h>
72122830Snectar#include <errno.h>
7379664Sdd#include <stdlib.h>
7428019Sjoerg#include <string.h>
7548614Sobrien#include <pthread.h>
7671579Sdeischen#include "un-namespace.h"
7771579Sdeischen#include "libc_private.h"
7828021Sjoerg#include "timelocal.h"
7928019Sjoerg
80227753Stheravenstatic char * _strptime(const char *, const char *, struct tm *, int *, locale_t);
8148614Sobrien
8228021Sjoerg#define asizeof(a)	(sizeof (a) / sizeof ((a)[0]))
8328019Sjoerg
8448614Sobrienstatic char *
85227753Stheraven_strptime(const char *buf, const char *fmt, struct tm *tm, int *GMTp,
86227753Stheraven		locale_t locale)
8728019Sjoerg{
8828021Sjoerg	char	c;
8928021Sjoerg	const char *ptr;
9028021Sjoerg	int	i,
9128021Sjoerg		len;
9253941Sache	int Ealternative, Oalternative;
93227753Stheraven	struct lc_time_T *tptr = __get_current_time_locale(locale);
9428019Sjoerg
9528021Sjoerg	ptr = fmt;
9628021Sjoerg	while (*ptr != 0) {
9728021Sjoerg		if (*buf == 0)
9828021Sjoerg			break;
9928019Sjoerg
10028021Sjoerg		c = *ptr++;
10128019Sjoerg
10228021Sjoerg		if (c != '%') {
103227753Stheraven			if (isspace_l((unsigned char)c, locale))
104227753Stheraven				while (*buf != 0 &&
105227753Stheraven				       isspace_l((unsigned char)*buf, locale))
10628021Sjoerg					buf++;
10728021Sjoerg			else if (c != *buf++)
10828021Sjoerg				return 0;
10928021Sjoerg			continue;
11028021Sjoerg		}
11128019Sjoerg
11253941Sache		Ealternative = 0;
11353941Sache		Oalternative = 0;
11453941Sachelabel:
11528021Sjoerg		c = *ptr++;
11628021Sjoerg		switch (c) {
11728021Sjoerg		case 0:
11828021Sjoerg		case '%':
11928021Sjoerg			if (*buf++ != '%')
12028021Sjoerg				return 0;
12128021Sjoerg			break;
12228019Sjoerg
12353941Sache		case '+':
124227753Stheraven			buf = _strptime(buf, tptr->date_fmt, tm, GMTp, locale);
12528021Sjoerg			if (buf == 0)
12628021Sjoerg				return 0;
12728021Sjoerg			break;
12828019Sjoerg
12953941Sache		case 'C':
130227753Stheraven			if (!isdigit_l((unsigned char)*buf, locale))
13153941Sache				return 0;
13253941Sache
13354316Ssheldonh			/* XXX This will break for 3-digit centuries. */
13454316Ssheldonh			len = 2;
135227753Stheraven			for (i = 0; len && *buf != 0 &&
136227753Stheraven			     isdigit_l((unsigned char)*buf, locale); buf++) {
13753941Sache				i *= 10;
13853941Sache				i += *buf - '0';
13954316Ssheldonh				len--;
14053941Sache			}
14153941Sache			if (i < 19)
14253941Sache				return 0;
14353941Sache
14453941Sache			tm->tm_year = i * 100 - 1900;
14553941Sache			break;
14653941Sache
14728021Sjoerg		case 'c':
148227753Stheraven			buf = _strptime(buf, tptr->c_fmt, tm, GMTp, locale);
14928021Sjoerg			if (buf == 0)
15028021Sjoerg				return 0;
15128021Sjoerg			break;
15228019Sjoerg
15328021Sjoerg		case 'D':
154227753Stheraven			buf = _strptime(buf, "%m/%d/%y", tm, GMTp, locale);
15528021Sjoerg			if (buf == 0)
15628021Sjoerg				return 0;
15728021Sjoerg			break;
15828019Sjoerg
15953941Sache		case 'E':
16053960Sache			if (Ealternative || Oalternative)
16153960Sache				break;
16253941Sache			Ealternative++;
16353941Sache			goto label;
16453941Sache
16553941Sache		case 'O':
16653960Sache			if (Ealternative || Oalternative)
16753960Sache				break;
16853941Sache			Oalternative++;
16953941Sache			goto label;
17053941Sache
17153960Sache		case 'F':
172227753Stheraven			buf = _strptime(buf, "%Y-%m-%d", tm, GMTp, locale);
17374412Sache			if (buf == 0)
17474412Sache				return 0;
17574412Sache			break;
17674412Sache
17728021Sjoerg		case 'R':
178227753Stheraven			buf = _strptime(buf, "%H:%M", tm, GMTp, locale);
17928021Sjoerg			if (buf == 0)
18028021Sjoerg				return 0;
18128021Sjoerg			break;
18228019Sjoerg
18328021Sjoerg		case 'r':
184227753Stheraven			buf = _strptime(buf, tptr->ampm_fmt, tm, GMTp, locale);
18528021Sjoerg			if (buf == 0)
18628021Sjoerg				return 0;
18728021Sjoerg			break;
18828019Sjoerg
18928021Sjoerg		case 'T':
190227753Stheraven			buf = _strptime(buf, "%H:%M:%S", tm, GMTp, locale);
19128021Sjoerg			if (buf == 0)
19228021Sjoerg				return 0;
19328021Sjoerg			break;
19428019Sjoerg
19528021Sjoerg		case 'X':
196227753Stheraven			buf = _strptime(buf, tptr->X_fmt, tm, GMTp, locale);
19728021Sjoerg			if (buf == 0)
19828021Sjoerg				return 0;
19928021Sjoerg			break;
20028019Sjoerg
20128021Sjoerg		case 'x':
202227753Stheraven			buf = _strptime(buf, tptr->x_fmt, tm, GMTp, locale);
20328021Sjoerg			if (buf == 0)
20428021Sjoerg				return 0;
20528021Sjoerg			break;
20628019Sjoerg
20728021Sjoerg		case 'j':
208227753Stheraven			if (!isdigit_l((unsigned char)*buf, locale))
20928021Sjoerg				return 0;
21028019Sjoerg
21154316Ssheldonh			len = 3;
212227753Stheraven			for (i = 0; len && *buf != 0 &&
213227753Stheraven			     isdigit_l((unsigned char)*buf, locale); buf++){
21428021Sjoerg				i *= 10;
21528021Sjoerg				i += *buf - '0';
21654316Ssheldonh				len--;
21728021Sjoerg			}
21853083Ssheldonh			if (i < 1 || i > 366)
21928021Sjoerg				return 0;
22028019Sjoerg
22153083Ssheldonh			tm->tm_yday = i - 1;
22228021Sjoerg			break;
22328019Sjoerg
22428021Sjoerg		case 'M':
22528021Sjoerg		case 'S':
226227753Stheraven			if (*buf == 0 ||
227227753Stheraven				isspace_l((unsigned char)*buf, locale))
22828021Sjoerg				break;
22928019Sjoerg
230227753Stheraven			if (!isdigit_l((unsigned char)*buf, locale))
23128021Sjoerg				return 0;
23228019Sjoerg
23354316Ssheldonh			len = 2;
234227753Stheraven			for (i = 0; len && *buf != 0 &&
235227753Stheraven				isdigit_l((unsigned char)*buf, locale); buf++){
23628021Sjoerg				i *= 10;
23728021Sjoerg				i += *buf - '0';
23854316Ssheldonh				len--;
23928021Sjoerg			}
24028019Sjoerg
24153083Ssheldonh			if (c == 'M') {
24253083Ssheldonh				if (i > 59)
24353083Ssheldonh					return 0;
24428021Sjoerg				tm->tm_min = i;
24553083Ssheldonh			} else {
24653083Ssheldonh				if (i > 60)
24753083Ssheldonh					return 0;
24828021Sjoerg				tm->tm_sec = i;
24953083Ssheldonh			}
25028019Sjoerg
251227753Stheraven			if (*buf != 0 &&
252227753Stheraven				isspace_l((unsigned char)*buf, locale))
253227753Stheraven				while (*ptr != 0 &&
254227753Stheraven				       !isspace_l((unsigned char)*ptr, locale))
25528021Sjoerg					ptr++;
25628021Sjoerg			break;
25728019Sjoerg
25828021Sjoerg		case 'H':
25928021Sjoerg		case 'I':
26028021Sjoerg		case 'k':
26128021Sjoerg		case 'l':
26254316Ssheldonh			/*
26354316Ssheldonh			 * Of these, %l is the only specifier explicitly
26454316Ssheldonh			 * documented as not being zero-padded.  However,
26554316Ssheldonh			 * there is no harm in allowing zero-padding.
26654316Ssheldonh			 *
26754316Ssheldonh			 * XXX The %l specifier may gobble one too many
26854316Ssheldonh			 * digits if used incorrectly.
26954316Ssheldonh			 */
270227753Stheraven			if (!isdigit_l((unsigned char)*buf, locale))
27128021Sjoerg				return 0;
27228019Sjoerg
27354316Ssheldonh			len = 2;
274227753Stheraven			for (i = 0; len && *buf != 0 &&
275227753Stheraven			     isdigit_l((unsigned char)*buf, locale); buf++) {
27628021Sjoerg				i *= 10;
27728021Sjoerg				i += *buf - '0';
27854316Ssheldonh				len--;
27928021Sjoerg			}
28028021Sjoerg			if (c == 'H' || c == 'k') {
28128021Sjoerg				if (i > 23)
28228021Sjoerg					return 0;
28354301Ssheldonh			} else if (i > 12)
28428021Sjoerg				return 0;
28528019Sjoerg
28628021Sjoerg			tm->tm_hour = i;
28728019Sjoerg
288227753Stheraven			if (*buf != 0 &&
289227753Stheraven			    isspace_l((unsigned char)*buf, locale))
290227753Stheraven				while (*ptr != 0 &&
291227753Stheraven				       !isspace_l((unsigned char)*ptr, locale))
29228021Sjoerg					ptr++;
29328021Sjoerg			break;
29428019Sjoerg
29528021Sjoerg		case 'p':
29654316Ssheldonh			/*
29754316Ssheldonh			 * XXX This is bogus if parsed before hour-related
29854316Ssheldonh			 * specifiers.
29954316Ssheldonh			 */
30072168Sphantom			len = strlen(tptr->am);
301227753Stheraven			if (strncasecmp_l(buf, tptr->am, len, locale) == 0) {
30228021Sjoerg				if (tm->tm_hour > 12)
30328021Sjoerg					return 0;
30428021Sjoerg				if (tm->tm_hour == 12)
30528021Sjoerg					tm->tm_hour = 0;
30628021Sjoerg				buf += len;
30728021Sjoerg				break;
30828021Sjoerg			}
30928019Sjoerg
31072168Sphantom			len = strlen(tptr->pm);
311227753Stheraven			if (strncasecmp_l(buf, tptr->pm, len, locale) == 0) {
31228021Sjoerg				if (tm->tm_hour > 12)
31328021Sjoerg					return 0;
31428021Sjoerg				if (tm->tm_hour != 12)
31528021Sjoerg					tm->tm_hour += 12;
31628021Sjoerg				buf += len;
31728021Sjoerg				break;
31828021Sjoerg			}
31928019Sjoerg
32028021Sjoerg			return 0;
32128019Sjoerg
32228021Sjoerg		case 'A':
32328021Sjoerg		case 'a':
32472168Sphantom			for (i = 0; i < asizeof(tptr->weekday); i++) {
32574409Sache				len = strlen(tptr->weekday[i]);
326227753Stheraven				if (strncasecmp_l(buf, tptr->weekday[i],
327227753Stheraven						len, locale) == 0)
32874409Sache					break;
32974409Sache				len = strlen(tptr->wday[i]);
330227753Stheraven				if (strncasecmp_l(buf, tptr->wday[i],
331227753Stheraven						len, locale) == 0)
33274409Sache					break;
33328021Sjoerg			}
33472168Sphantom			if (i == asizeof(tptr->weekday))
33528021Sjoerg				return 0;
33628019Sjoerg
33728021Sjoerg			tm->tm_wday = i;
33828021Sjoerg			buf += len;
33928021Sjoerg			break;
34028019Sjoerg
34153083Ssheldonh		case 'U':
34253083Ssheldonh		case 'W':
34353083Ssheldonh			/*
34453083Ssheldonh			 * XXX This is bogus, as we can not assume any valid
34553083Ssheldonh			 * information present in the tm structure at this
34653083Ssheldonh			 * point to calculate a real value, so just check the
34753083Ssheldonh			 * range for now.
34853083Ssheldonh			 */
349227753Stheraven			if (!isdigit_l((unsigned char)*buf, locale))
35053083Ssheldonh				return 0;
35153083Ssheldonh
35254316Ssheldonh			len = 2;
353227753Stheraven			for (i = 0; len && *buf != 0 &&
354227753Stheraven			     isdigit_l((unsigned char)*buf, locale); buf++) {
35553083Ssheldonh				i *= 10;
35653083Ssheldonh				i += *buf - '0';
35754316Ssheldonh				len--;
35853083Ssheldonh			}
35953083Ssheldonh			if (i > 53)
36053083Ssheldonh				return 0;
36153083Ssheldonh
362227753Stheraven			if (*buf != 0 &&
363227753Stheraven			    isspace_l((unsigned char)*buf, locale))
364227753Stheraven				while (*ptr != 0 &&
365227753Stheraven				       !isspace_l((unsigned char)*ptr, locale))
36653083Ssheldonh					ptr++;
36753083Ssheldonh			break;
36853083Ssheldonh
36953083Ssheldonh		case 'w':
370227753Stheraven			if (!isdigit_l((unsigned char)*buf, locale))
37153083Ssheldonh				return 0;
37253083Ssheldonh
37354316Ssheldonh			i = *buf - '0';
37453083Ssheldonh			if (i > 6)
37553083Ssheldonh				return 0;
37653083Ssheldonh
37753083Ssheldonh			tm->tm_wday = i;
37853083Ssheldonh
379227753Stheraven			if (*buf != 0 &&
380227753Stheraven			    isspace_l((unsigned char)*buf, locale))
381227753Stheraven				while (*ptr != 0 &&
382227753Stheraven				       !isspace_l((unsigned char)*ptr, locale))
38353083Ssheldonh					ptr++;
38453083Ssheldonh			break;
38553083Ssheldonh
38628021Sjoerg		case 'd':
38728021Sjoerg		case 'e':
38854316Ssheldonh			/*
38954316Ssheldonh			 * The %e specifier is explicitly documented as not
39054316Ssheldonh			 * being zero-padded but there is no harm in allowing
39154316Ssheldonh			 * such padding.
39254316Ssheldonh			 *
39354316Ssheldonh			 * XXX The %e specifier may gobble one too many
39454316Ssheldonh			 * digits if used incorrectly.
39554316Ssheldonh			 */
396227753Stheraven			if (!isdigit_l((unsigned char)*buf, locale))
39728021Sjoerg				return 0;
39828019Sjoerg
39954316Ssheldonh			len = 2;
400227753Stheraven			for (i = 0; len && *buf != 0 &&
401227753Stheraven			     isdigit_l((unsigned char)*buf, locale); buf++) {
40228021Sjoerg				i *= 10;
40328021Sjoerg				i += *buf - '0';
40454316Ssheldonh				len--;
40528021Sjoerg			}
40628021Sjoerg			if (i > 31)
40728021Sjoerg				return 0;
40828019Sjoerg
40928021Sjoerg			tm->tm_mday = i;
41028019Sjoerg
411227753Stheraven			if (*buf != 0 &&
412227753Stheraven			    isspace_l((unsigned char)*buf, locale))
413227753Stheraven				while (*ptr != 0 &&
414227753Stheraven				       !isspace_l((unsigned char)*ptr, locale))
41528021Sjoerg					ptr++;
41628021Sjoerg			break;
41728019Sjoerg
41828021Sjoerg		case 'B':
41928021Sjoerg		case 'b':
42028021Sjoerg		case 'h':
42172168Sphantom			for (i = 0; i < asizeof(tptr->month); i++) {
42253941Sache				if (Oalternative) {
42353941Sache					if (c == 'B') {
42472168Sphantom						len = strlen(tptr->alt_month[i]);
425227753Stheraven						if (strncasecmp_l(buf,
42672168Sphantom								tptr->alt_month[i],
427227753Stheraven								len, locale) == 0)
42853941Sache							break;
42953941Sache					}
43053941Sache				} else {
43174409Sache					len = strlen(tptr->month[i]);
432227753Stheraven					if (strncasecmp_l(buf, tptr->month[i],
433227753Stheraven							len, locale) == 0)
43474409Sache						break;
435207830Sedwin				}
436207830Sedwin			}
437207830Sedwin			/*
438207830Sedwin			 * Try the abbreviated month name if the full name
439207830Sedwin			 * wasn't found and Oalternative was not requested.
440207830Sedwin			 */
441207830Sedwin			if (i == asizeof(tptr->month) && !Oalternative) {
442207830Sedwin				for (i = 0; i < asizeof(tptr->month); i++) {
44374409Sache					len = strlen(tptr->mon[i]);
444227753Stheraven					if (strncasecmp_l(buf, tptr->mon[i],
445227753Stheraven							len, locale) == 0)
44674409Sache						break;
44753941Sache				}
44828021Sjoerg			}
44972168Sphantom			if (i == asizeof(tptr->month))
45028021Sjoerg				return 0;
45128019Sjoerg
45228021Sjoerg			tm->tm_mon = i;
45328021Sjoerg			buf += len;
45428021Sjoerg			break;
45528019Sjoerg
45628021Sjoerg		case 'm':
457227753Stheraven			if (!isdigit_l((unsigned char)*buf, locale))
45828021Sjoerg				return 0;
45928019Sjoerg
46054316Ssheldonh			len = 2;
461227753Stheraven			for (i = 0; len && *buf != 0 &&
462227753Stheraven			     isdigit_l((unsigned char)*buf, locale); buf++) {
46328021Sjoerg				i *= 10;
46428021Sjoerg				i += *buf - '0';
46554316Ssheldonh				len--;
46628021Sjoerg			}
46728021Sjoerg			if (i < 1 || i > 12)
46828021Sjoerg				return 0;
46928019Sjoerg
47028021Sjoerg			tm->tm_mon = i - 1;
47128019Sjoerg
472227753Stheraven			if (*buf != 0 &&
473227753Stheraven			    isspace_l((unsigned char)*buf, locale))
474227753Stheraven				while (*ptr != 0 &&
475227753Stheraven				       !isspace_l((unsigned char)*ptr, locale))
47628021Sjoerg					ptr++;
47728021Sjoerg			break;
47828019Sjoerg
47979664Sdd		case 's':
48079664Sdd			{
48179664Sdd			char *cp;
482122830Snectar			int sverrno;
483122830Snectar			long n;
48479664Sdd			time_t t;
48579664Sdd
486122830Snectar			sverrno = errno;
487122830Snectar			errno = 0;
488227753Stheraven			n = strtol_l(buf, &cp, 10, locale);
489122830Snectar			if (errno == ERANGE || (long)(t = n) != n) {
490122830Snectar				errno = sverrno;
49179664Sdd				return 0;
492122830Snectar			}
493122830Snectar			errno = sverrno;
49479664Sdd			buf = cp;
49579664Sdd			gmtime_r(&t, tm);
496112156Smtm			*GMTp = 1;
49779664Sdd			}
49879664Sdd			break;
49979664Sdd
50028021Sjoerg		case 'Y':
50128021Sjoerg		case 'y':
502227753Stheraven			if (*buf == 0 ||
503227753Stheraven			    isspace_l((unsigned char)*buf, locale))
50428021Sjoerg				break;
50528019Sjoerg
506227753Stheraven			if (!isdigit_l((unsigned char)*buf, locale))
50728021Sjoerg				return 0;
50828019Sjoerg
50954316Ssheldonh			len = (c == 'Y') ? 4 : 2;
510227753Stheraven			for (i = 0; len && *buf != 0 &&
511227753Stheraven			     isdigit_l((unsigned char)*buf, locale); buf++) {
51228021Sjoerg				i *= 10;
51328021Sjoerg				i += *buf - '0';
51454316Ssheldonh				len--;
51528021Sjoerg			}
51628021Sjoerg			if (c == 'Y')
51728021Sjoerg				i -= 1900;
51846051Swes			if (c == 'y' && i < 69)
51946042Swes				i += 100;
52028021Sjoerg			if (i < 0)
52128021Sjoerg				return 0;
52228019Sjoerg
52328021Sjoerg			tm->tm_year = i;
52428019Sjoerg
525227753Stheraven			if (*buf != 0 &&
526227753Stheraven			    isspace_l((unsigned char)*buf, locale))
527227753Stheraven				while (*ptr != 0 &&
528227753Stheraven				       !isspace_l((unsigned char)*ptr, locale))
52928021Sjoerg					ptr++;
53028021Sjoerg			break;
53148550Sobrien
53248550Sobrien		case 'Z':
53348550Sobrien			{
53448550Sobrien			const char *cp;
53548550Sobrien			char *zonestr;
53648550Sobrien
537227753Stheraven			for (cp = buf; *cp &&
538227753Stheraven			     isupper_l((unsigned char)*cp, locale); ++cp) {
539227753Stheraven				/*empty*/}
54048550Sobrien			if (cp - buf) {
54148550Sobrien				zonestr = alloca(cp - buf + 1);
54248550Sobrien				strncpy(zonestr, buf, cp - buf);
54348550Sobrien				zonestr[cp - buf] = '\0';
54448550Sobrien				tzset();
54548550Sobrien				if (0 == strcmp(zonestr, "GMT")) {
546112156Smtm				    *GMTp = 1;
54748550Sobrien				} else if (0 == strcmp(zonestr, tzname[0])) {
54848614Sobrien				    tm->tm_isdst = 0;
54948550Sobrien				} else if (0 == strcmp(zonestr, tzname[1])) {
55048614Sobrien				    tm->tm_isdst = 1;
55148550Sobrien				} else {
55248614Sobrien				    return 0;
55348550Sobrien				}
55448550Sobrien				buf += cp - buf;
55548550Sobrien			}
55648550Sobrien			}
55748550Sobrien			break;
558195015Sdelphij
559195015Sdelphij		case 'z':
560195015Sdelphij			{
561195015Sdelphij			int sign = 1;
562195015Sdelphij
563195015Sdelphij			if (*buf != '+') {
564195015Sdelphij				if (*buf == '-')
565195015Sdelphij					sign = -1;
566195015Sdelphij				else
567195015Sdelphij					return 0;
568195015Sdelphij			}
569195015Sdelphij
570195015Sdelphij			buf++;
571195015Sdelphij			i = 0;
572195015Sdelphij			for (len = 4; len > 0; len--) {
573227753Stheraven				if (isdigit_l((unsigned char)*buf, locale)) {
574195015Sdelphij					i *= 10;
575195015Sdelphij					i += *buf - '0';
576195015Sdelphij					buf++;
577195015Sdelphij				} else
578195015Sdelphij					return 0;
579195015Sdelphij			}
580195015Sdelphij
581195015Sdelphij			tm->tm_hour -= sign * (i / 100);
582195015Sdelphij			tm->tm_min  -= sign * (i % 100);
583195015Sdelphij			*GMTp = 1;
584195015Sdelphij			}
585195015Sdelphij			break;
58628021Sjoerg		}
58728021Sjoerg	}
58848614Sobrien	return (char *)buf;
58948614Sobrien}
59028019Sjoerg
59148614Sobrien
59248614Sobrienchar *
593227753Stheravenstrptime_l(const char * __restrict buf, const char * __restrict fmt,
594227753Stheraven    struct tm * __restrict tm, locale_t loc)
59548614Sobrien{
59648614Sobrien	char *ret;
597112156Smtm	int gmt;
598227753Stheraven	FIX_LOCALE(loc);
59948614Sobrien
600112156Smtm	gmt = 0;
601227753Stheraven	ret = _strptime(buf, fmt, tm, &gmt, loc);
602114285Smtm	if (ret && gmt) {
603114285Smtm		time_t t = timegm(tm);
60471579Sdeischen		localtime_r(&t, tm);
60548550Sobrien	}
60648614Sobrien
607112156Smtm	return (ret);
60828019Sjoerg}
609227753Stheravenchar *
610227753Stheravenstrptime(const char * __restrict buf, const char * __restrict fmt,
611227753Stheraven    struct tm * __restrict tm)
612227753Stheraven{
613227753Stheraven	return strptime_l(buf, fmt, tm, __get_locale());
614227753Stheraven}
615