strptime.c revision 53083
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 *
2528021Sjoerg * Redistribution and use in source and binary forms, with or without
2628019Sjoerg * modification, are permitted provided that the following conditions
2728019Sjoerg * are met:
2828019Sjoerg * 1. Redistributions of source code must retain the above copyright
2928019Sjoerg *    notice, this list of conditions and the following disclaimer.
3028019Sjoerg * 2. Redistributions in binary form must reproduce the above copyright
3128019Sjoerg *    notice, this list of conditions and the following disclaimer
3228019Sjoerg *    in the documentation and/or other materials provided with the
3328019Sjoerg *    distribution.
3428019Sjoerg * 3. All advertising materials mentioning features or use of this
3528019Sjoerg *    software must display the following acknowledgement:
3628019Sjoerg *      This product includes software developed by Powerdog Industries.
3728019Sjoerg * 4. The name of Powerdog Industries may not be used to endorse or
3828019Sjoerg *    promote products derived from this software without specific prior
3928019Sjoerg *    written permission.
4028019Sjoerg *
4128019Sjoerg * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY
4228019Sjoerg * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
4328019Sjoerg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
4428019Sjoerg * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE
4528019Sjoerg * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
4628019Sjoerg * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
4728019Sjoerg * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
4828019Sjoerg * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
4928019Sjoerg * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
5028019Sjoerg * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
5128019Sjoerg * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
5228019Sjoerg */
5328019Sjoerg
5428021Sjoerg#ifdef LIBC_RCS
5528021Sjoergstatic const char rcsid[] =
5650476Speter  "$FreeBSD: head/lib/libc/stdtime/strptime.c 53083 1999-11-10 14:40:59Z sheldonh $";
5728021Sjoerg#endif
5828021Sjoerg
5928019Sjoerg#ifndef lint
6028021Sjoerg#ifndef NOID
6128019Sjoergstatic char copyright[] =
6228019Sjoerg"@(#) Copyright (c) 1994 Powerdog Industries.  All rights reserved.";
6328021Sjoergstatic char sccsid[] = "@(#)strptime.c	0.1 (Powerdog) 94/03/27";
6428021Sjoerg#endif /* !defined NOID */
6528019Sjoerg#endif /* not lint */
6628019Sjoerg
6728019Sjoerg#include <time.h>
6828019Sjoerg#include <ctype.h>
6928019Sjoerg#include <string.h>
7048614Sobrien#ifdef	_THREAD_SAFE
7148614Sobrien#include <pthread.h>
7248614Sobrien#include "pthread_private.h"
7348614Sobrien#endif
7428021Sjoerg#include "timelocal.h"
7528019Sjoerg
7648614Sobrienstatic char * _strptime(const char *, const char *, struct tm *);
7748614Sobrien
7848614Sobrien#ifdef	_THREAD_SAFE
7948614Sobrienstatic struct pthread_mutex	_gotgmt_mutexd = PTHREAD_MUTEX_STATIC_INITIALIZER;
8048614Sobrienstatic pthread_mutex_t		gotgmt_mutex   = &_gotgmt_mutexd;
8148614Sobrien#endif
8248614Sobrienstatic int got_GMT;
8348614Sobrien
8428021Sjoerg#define asizeof(a)	(sizeof (a) / sizeof ((a)[0]))
8528019Sjoerg
8648614Sobrienstatic char *
8748614Sobrien_strptime(const char *buf, const char *fmt, struct tm *tm)
8828019Sjoerg{
8928021Sjoerg	char	c;
9028021Sjoerg	const char *ptr;
9128021Sjoerg	int	i,
9228021Sjoerg		len;
9328019Sjoerg
9428021Sjoerg	ptr = fmt;
9528021Sjoerg	while (*ptr != 0) {
9628021Sjoerg		if (*buf == 0)
9728021Sjoerg			break;
9828019Sjoerg
9928021Sjoerg		c = *ptr++;
10028019Sjoerg
10128021Sjoerg		if (c != '%') {
10228164Sache			if (isspace((unsigned char)c))
10328164Sache				while (*buf != 0 && isspace((unsigned char)*buf))
10428021Sjoerg					buf++;
10528021Sjoerg			else if (c != *buf++)
10628021Sjoerg				return 0;
10728021Sjoerg			continue;
10828021Sjoerg		}
10928019Sjoerg
11028021Sjoerg		c = *ptr++;
11128021Sjoerg		switch (c) {
11228021Sjoerg		case 0:
11328021Sjoerg		case '%':
11428021Sjoerg			if (*buf++ != '%')
11528021Sjoerg				return 0;
11628021Sjoerg			break;
11728019Sjoerg
11828021Sjoerg		case 'C':
11948614Sobrien			buf = _strptime(buf, Locale->date_fmt, tm);
12028021Sjoerg			if (buf == 0)
12128021Sjoerg				return 0;
12228021Sjoerg			break;
12328019Sjoerg
12428021Sjoerg		case 'c':
12548614Sobrien			buf = _strptime(buf, "%x %X", tm);
12628021Sjoerg			if (buf == 0)
12728021Sjoerg				return 0;
12828021Sjoerg			break;
12928019Sjoerg
13028021Sjoerg		case 'D':
13148614Sobrien			buf = _strptime(buf, "%m/%d/%y", tm);
13228021Sjoerg			if (buf == 0)
13328021Sjoerg				return 0;
13428021Sjoerg			break;
13528019Sjoerg
13628021Sjoerg		case 'R':
13748614Sobrien			buf = _strptime(buf, "%H:%M", tm);
13828021Sjoerg			if (buf == 0)
13928021Sjoerg				return 0;
14028021Sjoerg			break;
14128019Sjoerg
14228021Sjoerg		case 'r':
14348614Sobrien			buf = _strptime(buf, "%I:%M:%S %p", tm);
14428021Sjoerg			if (buf == 0)
14528021Sjoerg				return 0;
14628021Sjoerg			break;
14728019Sjoerg
14828021Sjoerg		case 'T':
14948614Sobrien			buf = _strptime(buf, "%H:%M:%S", tm);
15028021Sjoerg			if (buf == 0)
15128021Sjoerg				return 0;
15228021Sjoerg			break;
15328019Sjoerg
15428021Sjoerg		case 'X':
15548614Sobrien			buf = _strptime(buf, Locale->X_fmt, tm);
15628021Sjoerg			if (buf == 0)
15728021Sjoerg				return 0;
15828021Sjoerg			break;
15928019Sjoerg
16028021Sjoerg		case 'x':
16148614Sobrien			buf = _strptime(buf, Locale->x_fmt, tm);
16228021Sjoerg			if (buf == 0)
16328021Sjoerg				return 0;
16428021Sjoerg			break;
16528019Sjoerg
16628021Sjoerg		case 'j':
16728164Sache			if (!isdigit((unsigned char)*buf))
16828021Sjoerg				return 0;
16928019Sjoerg
17028164Sache			for (i = 0; *buf != 0 && isdigit((unsigned char)*buf); buf++) {
17128021Sjoerg				i *= 10;
17228021Sjoerg				i += *buf - '0';
17328021Sjoerg			}
17453083Ssheldonh			if (i < 1 || i > 366)
17528021Sjoerg				return 0;
17628019Sjoerg
17753083Ssheldonh			tm->tm_yday = i - 1;
17828021Sjoerg			break;
17928019Sjoerg
18028021Sjoerg		case 'M':
18128021Sjoerg		case 'S':
18228164Sache			if (*buf == 0 || isspace((unsigned char)*buf))
18328021Sjoerg				break;
18428019Sjoerg
18528164Sache			if (!isdigit((unsigned char)*buf))
18628021Sjoerg				return 0;
18728019Sjoerg
18828164Sache			for (i = 0; *buf != 0 && isdigit((unsigned char)*buf); buf++) {
18928021Sjoerg				i *= 10;
19028021Sjoerg				i += *buf - '0';
19128021Sjoerg			}
19228019Sjoerg
19353083Ssheldonh			if (c == 'M') {
19453083Ssheldonh				if (i > 59)
19553083Ssheldonh					return 0;
19628021Sjoerg				tm->tm_min = i;
19753083Ssheldonh			} else {
19853083Ssheldonh				if (i > 60)
19953083Ssheldonh					return 0;
20028021Sjoerg				tm->tm_sec = i;
20153083Ssheldonh			}
20228019Sjoerg
20328164Sache			if (*buf != 0 && isspace((unsigned char)*buf))
20428164Sache				while (*ptr != 0 && !isspace((unsigned char)*ptr))
20528021Sjoerg					ptr++;
20628021Sjoerg			break;
20728019Sjoerg
20828021Sjoerg		case 'H':
20928021Sjoerg		case 'I':
21028021Sjoerg		case 'k':
21128021Sjoerg		case 'l':
21228164Sache			if (!isdigit((unsigned char)*buf))
21328021Sjoerg				return 0;
21428019Sjoerg
21528164Sache			for (i = 0; *buf != 0 && isdigit((unsigned char)*buf); buf++) {
21628021Sjoerg				i *= 10;
21728021Sjoerg				i += *buf - '0';
21828021Sjoerg			}
21928021Sjoerg			if (c == 'H' || c == 'k') {
22028021Sjoerg				if (i > 23)
22128021Sjoerg					return 0;
22228021Sjoerg			} else if (i > 11)
22328021Sjoerg				return 0;
22428019Sjoerg
22528021Sjoerg			tm->tm_hour = i;
22628019Sjoerg
22728164Sache			if (*buf != 0 && isspace((unsigned char)*buf))
22828164Sache				while (*ptr != 0 && !isspace((unsigned char)*ptr))
22928021Sjoerg					ptr++;
23028021Sjoerg			break;
23128019Sjoerg
23228021Sjoerg		case 'p':
23328021Sjoerg			len = strlen(Locale->am);
23428021Sjoerg			if (strncasecmp(buf, Locale->am, len) == 0) {
23528021Sjoerg				if (tm->tm_hour > 12)
23628021Sjoerg					return 0;
23728021Sjoerg				if (tm->tm_hour == 12)
23828021Sjoerg					tm->tm_hour = 0;
23928021Sjoerg				buf += len;
24028021Sjoerg				break;
24128021Sjoerg			}
24228019Sjoerg
24328021Sjoerg			len = strlen(Locale->pm);
24428021Sjoerg			if (strncasecmp(buf, Locale->pm, len) == 0) {
24528021Sjoerg				if (tm->tm_hour > 12)
24628021Sjoerg					return 0;
24728021Sjoerg				if (tm->tm_hour != 12)
24828021Sjoerg					tm->tm_hour += 12;
24928021Sjoerg				buf += len;
25028021Sjoerg				break;
25128021Sjoerg			}
25228019Sjoerg
25328021Sjoerg			return 0;
25428019Sjoerg
25528021Sjoerg		case 'A':
25628021Sjoerg		case 'a':
25728021Sjoerg			for (i = 0; i < asizeof(Locale->weekday); i++) {
25828021Sjoerg				len = strlen(Locale->weekday[i]);
25928021Sjoerg				if (strncasecmp(buf,
26028021Sjoerg						Locale->weekday[i],
26128021Sjoerg						len) == 0)
26228021Sjoerg					break;
26328019Sjoerg
26428021Sjoerg				len = strlen(Locale->wday[i]);
26528021Sjoerg				if (strncasecmp(buf,
26628021Sjoerg						Locale->wday[i],
26728021Sjoerg						len) == 0)
26828021Sjoerg					break;
26928021Sjoerg			}
27028021Sjoerg			if (i == asizeof(Locale->weekday))
27128021Sjoerg				return 0;
27228019Sjoerg
27328021Sjoerg			tm->tm_wday = i;
27428021Sjoerg			buf += len;
27528021Sjoerg			break;
27628019Sjoerg
27753083Ssheldonh		case 'U':
27853083Ssheldonh		case 'W':
27953083Ssheldonh			/*
28053083Ssheldonh			 * XXX This is bogus, as we can not assume any valid
28153083Ssheldonh			 * information present in the tm structure at this
28253083Ssheldonh			 * point to calculate a real value, so just check the
28353083Ssheldonh			 * range for now.
28453083Ssheldonh			 */
28553083Ssheldonh			if (!isdigit((unsigned char)*buf))
28653083Ssheldonh				return 0;
28753083Ssheldonh
28853083Ssheldonh			for (i = 0; *buf != 0 && isdigit((unsigned char)*buf); buf++) {
28953083Ssheldonh				i *= 10;
29053083Ssheldonh				i += *buf - '0';
29153083Ssheldonh			}
29253083Ssheldonh			if (i > 53)
29353083Ssheldonh				return 0;
29453083Ssheldonh
29553083Ssheldonh			if (*buf != 0 && isspace((unsigned char)*buf))
29653083Ssheldonh				while (*ptr != 0 && !isspace((unsigned char)*ptr))
29753083Ssheldonh					ptr++;
29853083Ssheldonh			break;
29953083Ssheldonh
30053083Ssheldonh		case 'w':
30153083Ssheldonh			if (!isdigit((unsigned char)*buf))
30253083Ssheldonh				return 0;
30353083Ssheldonh
30453083Ssheldonh			for (i = 0; *buf != 0 && isdigit((unsigned char)*buf); buf++) {
30553083Ssheldonh				i *= 10;
30653083Ssheldonh				i += *buf - '0';
30753083Ssheldonh			}
30853083Ssheldonh			if (i > 6)
30953083Ssheldonh				return 0;
31053083Ssheldonh
31153083Ssheldonh			tm->tm_wday = i;
31253083Ssheldonh
31353083Ssheldonh			if (*buf != 0 && isspace((unsigned char)*buf))
31453083Ssheldonh				while (*ptr != 0 && !isspace((unsigned char)*ptr))
31553083Ssheldonh					ptr++;
31653083Ssheldonh			break;
31753083Ssheldonh
31828021Sjoerg		case 'd':
31928021Sjoerg		case 'e':
32028164Sache			if (!isdigit((unsigned char)*buf))
32128021Sjoerg				return 0;
32228019Sjoerg
32328164Sache			for (i = 0; *buf != 0 && isdigit((unsigned char)*buf); buf++) {
32428021Sjoerg				i *= 10;
32528021Sjoerg				i += *buf - '0';
32628021Sjoerg			}
32728021Sjoerg			if (i > 31)
32828021Sjoerg				return 0;
32928019Sjoerg
33028021Sjoerg			tm->tm_mday = i;
33128019Sjoerg
33228164Sache			if (*buf != 0 && isspace((unsigned char)*buf))
33328164Sache				while (*ptr != 0 && !isspace((unsigned char)*ptr))
33428021Sjoerg					ptr++;
33528021Sjoerg			break;
33628019Sjoerg
33728021Sjoerg		case 'B':
33828021Sjoerg		case 'b':
33928021Sjoerg		case 'h':
34028021Sjoerg			for (i = 0; i < asizeof(Locale->month); i++) {
34128021Sjoerg				len = strlen(Locale->month[i]);
34228021Sjoerg				if (strncasecmp(buf,
34328021Sjoerg						Locale->month[i],
34428021Sjoerg						len) == 0)
34528021Sjoerg					break;
34628019Sjoerg
34728021Sjoerg				len = strlen(Locale->mon[i]);
34828021Sjoerg				if (strncasecmp(buf,
34928021Sjoerg						Locale->mon[i],
35028021Sjoerg						len) == 0)
35128021Sjoerg					break;
35228021Sjoerg			}
35328021Sjoerg			if (i == asizeof(Locale->month))
35428021Sjoerg				return 0;
35528019Sjoerg
35628021Sjoerg			tm->tm_mon = i;
35728021Sjoerg			buf += len;
35828021Sjoerg			break;
35928019Sjoerg
36028021Sjoerg		case 'm':
36128164Sache			if (!isdigit((unsigned char)*buf))
36228021Sjoerg				return 0;
36328019Sjoerg
36428164Sache			for (i = 0; *buf != 0 && isdigit((unsigned char)*buf); buf++) {
36528021Sjoerg				i *= 10;
36628021Sjoerg				i += *buf - '0';
36728021Sjoerg			}
36828021Sjoerg			if (i < 1 || i > 12)
36928021Sjoerg				return 0;
37028019Sjoerg
37128021Sjoerg			tm->tm_mon = i - 1;
37228019Sjoerg
37328164Sache			if (*buf != 0 && isspace((unsigned char)*buf))
37428164Sache				while (*ptr != 0 && !isspace((unsigned char)*ptr))
37528021Sjoerg					ptr++;
37628021Sjoerg			break;
37728019Sjoerg
37828021Sjoerg		case 'Y':
37928021Sjoerg		case 'y':
38028164Sache			if (*buf == 0 || isspace((unsigned char)*buf))
38128021Sjoerg				break;
38228019Sjoerg
38328164Sache			if (!isdigit((unsigned char)*buf))
38428021Sjoerg				return 0;
38528019Sjoerg
38628164Sache			for (i = 0; *buf != 0 && isdigit((unsigned char)*buf); buf++) {
38728021Sjoerg				i *= 10;
38828021Sjoerg				i += *buf - '0';
38928021Sjoerg			}
39028021Sjoerg			if (c == 'Y')
39128021Sjoerg				i -= 1900;
39246051Swes			if (c == 'y' && i < 69)
39346042Swes				i += 100;
39428021Sjoerg			if (i < 0)
39528021Sjoerg				return 0;
39628019Sjoerg
39728021Sjoerg			tm->tm_year = i;
39828019Sjoerg
39928164Sache			if (*buf != 0 && isspace((unsigned char)*buf))
40028164Sache				while (*ptr != 0 && !isspace((unsigned char)*ptr))
40128021Sjoerg					ptr++;
40228021Sjoerg			break;
40348550Sobrien
40448550Sobrien		case 'Z':
40548550Sobrien			{
40648550Sobrien			const char *cp;
40748550Sobrien			char *zonestr;
40848550Sobrien
40952860Sache			for (cp = buf; *cp && isupper((unsigned char)*cp); ++cp) {/*empty*/}
41048550Sobrien			if (cp - buf) {
41148550Sobrien				zonestr = alloca(cp - buf + 1);
41248550Sobrien				strncpy(zonestr, buf, cp - buf);
41348550Sobrien				zonestr[cp - buf] = '\0';
41448550Sobrien				tzset();
41548550Sobrien				if (0 == strcmp(zonestr, "GMT")) {
41648614Sobrien				    got_GMT = 1;
41748550Sobrien				} else if (0 == strcmp(zonestr, tzname[0])) {
41848614Sobrien				    tm->tm_isdst = 0;
41948550Sobrien				} else if (0 == strcmp(zonestr, tzname[1])) {
42048614Sobrien				    tm->tm_isdst = 1;
42148550Sobrien				} else {
42248614Sobrien				    return 0;
42348550Sobrien				}
42448550Sobrien				buf += cp - buf;
42548550Sobrien			}
42648550Sobrien			}
42748550Sobrien			break;
42828021Sjoerg		}
42928021Sjoerg	}
43048614Sobrien	return (char *)buf;
43148614Sobrien}
43228019Sjoerg
43348614Sobrien
43448614Sobrienchar *
43548614Sobrienstrptime(const char *buf, const char *fmt, struct tm *tm)
43648614Sobrien{
43748614Sobrien	char *ret;
43848614Sobrien
43948614Sobrien#ifdef	_THREAD_SAFE
44048614Sobrien	pthread_mutex_lock(&gotgmt_mutex);
44148614Sobrien#endif
44248614Sobrien
44348614Sobrien	got_GMT = 0;
44448614Sobrien	ret = _strptime(buf, fmt, tm);
44548614Sobrien	if (ret && got_GMT) {
44648550Sobrien		time_t t = timegm(tm);
44748614Sobrien	    localtime_r(&t, tm);
44848614Sobrien		got_GMT = 0;
44948550Sobrien	}
45048614Sobrien
45148614Sobrien#ifdef	_THREAD_SAFE
45248614Sobrien	pthread_mutex_unlock(&gotgmt_mutex);
45348614Sobrien#endif
45448614Sobrien
45548614Sobrien	return ret;
45628019Sjoerg}
457