strptime.c revision 54301
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 54301 1999-12-08 11:11:40Z 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;
9353941Sache	int Ealternative, Oalternative;
9428019Sjoerg
9528021Sjoerg	ptr = fmt;
9628021Sjoerg	while (*ptr != 0) {
9728021Sjoerg		if (*buf == 0)
9828021Sjoerg			break;
9928019Sjoerg
10028021Sjoerg		c = *ptr++;
10128019Sjoerg
10228021Sjoerg		if (c != '%') {
10328164Sache			if (isspace((unsigned char)c))
10428164Sache				while (*buf != 0 && isspace((unsigned char)*buf))
10528021Sjoerg					buf++;
10628021Sjoerg			else if (c != *buf++)
10728021Sjoerg				return 0;
10828021Sjoerg			continue;
10928021Sjoerg		}
11028019Sjoerg
11153941Sache		Ealternative = 0;
11253941Sache		Oalternative = 0;
11353941Sachelabel:
11428021Sjoerg		c = *ptr++;
11528021Sjoerg		switch (c) {
11628021Sjoerg		case 0:
11728021Sjoerg		case '%':
11828021Sjoerg			if (*buf++ != '%')
11928021Sjoerg				return 0;
12028021Sjoerg			break;
12128019Sjoerg
12253941Sache		case '+':
12348614Sobrien			buf = _strptime(buf, Locale->date_fmt, tm);
12428021Sjoerg			if (buf == 0)
12528021Sjoerg				return 0;
12628021Sjoerg			break;
12728019Sjoerg
12853941Sache		case 'C':
12953941Sache			if (!isdigit((unsigned char)*buf))
13053941Sache				return 0;
13153941Sache
13253941Sache			for (i = 0; *buf != 0 && isdigit((unsigned char)*buf); buf++) {
13353941Sache				i *= 10;
13453941Sache				i += *buf - '0';
13553941Sache			}
13653941Sache			if (i < 19)
13753941Sache				return 0;
13853941Sache
13953941Sache			tm->tm_year = i * 100 - 1900;
14053941Sache			break;
14153941Sache
14228021Sjoerg		case 'c':
14353941Sache			buf = _strptime(buf, Locale->c_fmt, tm);
14428021Sjoerg			if (buf == 0)
14528021Sjoerg				return 0;
14628021Sjoerg			break;
14728019Sjoerg
14828021Sjoerg		case 'D':
14948614Sobrien			buf = _strptime(buf, "%m/%d/%y", tm);
15028021Sjoerg			if (buf == 0)
15128021Sjoerg				return 0;
15228021Sjoerg			break;
15328019Sjoerg
15453941Sache		case 'E':
15553960Sache			if (Ealternative || Oalternative)
15653960Sache				break;
15753941Sache			Ealternative++;
15853941Sache			goto label;
15953941Sache
16053941Sache		case 'O':
16153960Sache			if (Ealternative || Oalternative)
16253960Sache				break;
16353941Sache			Oalternative++;
16453941Sache			goto label;
16553941Sache
16653960Sache		case 'F':
16753960Sache		case 'f':
16853960Sache			if (!Ealternative)
16953960Sache				break;
17053960Sache			buf = _strptime(buf, (c == 'f') ? Locale->Ef_fmt : Locale->EF_fmt, tm);
17153960Sache			if (buf == 0)
17253960Sache				return 0;
17353960Sache			break;
17453960Sache
17528021Sjoerg		case 'R':
17648614Sobrien			buf = _strptime(buf, "%H:%M", tm);
17728021Sjoerg			if (buf == 0)
17828021Sjoerg				return 0;
17928021Sjoerg			break;
18028019Sjoerg
18128021Sjoerg		case 'r':
18248614Sobrien			buf = _strptime(buf, "%I:%M:%S %p", tm);
18328021Sjoerg			if (buf == 0)
18428021Sjoerg				return 0;
18528021Sjoerg			break;
18628019Sjoerg
18728021Sjoerg		case 'T':
18848614Sobrien			buf = _strptime(buf, "%H:%M:%S", tm);
18928021Sjoerg			if (buf == 0)
19028021Sjoerg				return 0;
19128021Sjoerg			break;
19228019Sjoerg
19328021Sjoerg		case 'X':
19448614Sobrien			buf = _strptime(buf, Locale->X_fmt, tm);
19528021Sjoerg			if (buf == 0)
19628021Sjoerg				return 0;
19728021Sjoerg			break;
19828019Sjoerg
19928021Sjoerg		case 'x':
20053960Sache			buf = _strptime(buf, Locale->x_fmt, tm);
20128021Sjoerg			if (buf == 0)
20228021Sjoerg				return 0;
20328021Sjoerg			break;
20428019Sjoerg
20528021Sjoerg		case 'j':
20628164Sache			if (!isdigit((unsigned char)*buf))
20728021Sjoerg				return 0;
20828019Sjoerg
20928164Sache			for (i = 0; *buf != 0 && isdigit((unsigned char)*buf); buf++) {
21028021Sjoerg				i *= 10;
21128021Sjoerg				i += *buf - '0';
21228021Sjoerg			}
21353083Ssheldonh			if (i < 1 || i > 366)
21428021Sjoerg				return 0;
21528019Sjoerg
21653083Ssheldonh			tm->tm_yday = i - 1;
21728021Sjoerg			break;
21828019Sjoerg
21928021Sjoerg		case 'M':
22028021Sjoerg		case 'S':
22128164Sache			if (*buf == 0 || isspace((unsigned char)*buf))
22228021Sjoerg				break;
22328019Sjoerg
22428164Sache			if (!isdigit((unsigned char)*buf))
22528021Sjoerg				return 0;
22628019Sjoerg
22728164Sache			for (i = 0; *buf != 0 && isdigit((unsigned char)*buf); buf++) {
22828021Sjoerg				i *= 10;
22928021Sjoerg				i += *buf - '0';
23028021Sjoerg			}
23128019Sjoerg
23253083Ssheldonh			if (c == 'M') {
23353083Ssheldonh				if (i > 59)
23453083Ssheldonh					return 0;
23528021Sjoerg				tm->tm_min = i;
23653083Ssheldonh			} else {
23753083Ssheldonh				if (i > 60)
23853083Ssheldonh					return 0;
23928021Sjoerg				tm->tm_sec = i;
24053083Ssheldonh			}
24128019Sjoerg
24228164Sache			if (*buf != 0 && isspace((unsigned char)*buf))
24328164Sache				while (*ptr != 0 && !isspace((unsigned char)*ptr))
24428021Sjoerg					ptr++;
24528021Sjoerg			break;
24628019Sjoerg
24728021Sjoerg		case 'H':
24828021Sjoerg		case 'I':
24928021Sjoerg		case 'k':
25028021Sjoerg		case 'l':
25128164Sache			if (!isdigit((unsigned char)*buf))
25228021Sjoerg				return 0;
25328019Sjoerg
25428164Sache			for (i = 0; *buf != 0 && isdigit((unsigned char)*buf); buf++) {
25528021Sjoerg				i *= 10;
25628021Sjoerg				i += *buf - '0';
25728021Sjoerg			}
25828021Sjoerg			if (c == 'H' || c == 'k') {
25928021Sjoerg				if (i > 23)
26028021Sjoerg					return 0;
26154301Ssheldonh			} else if (i > 12)
26228021Sjoerg				return 0;
26328019Sjoerg
26428021Sjoerg			tm->tm_hour = i;
26528019Sjoerg
26628164Sache			if (*buf != 0 && isspace((unsigned char)*buf))
26728164Sache				while (*ptr != 0 && !isspace((unsigned char)*ptr))
26828021Sjoerg					ptr++;
26928021Sjoerg			break;
27028019Sjoerg
27128021Sjoerg		case 'p':
27228021Sjoerg			len = strlen(Locale->am);
27328021Sjoerg			if (strncasecmp(buf, Locale->am, len) == 0) {
27428021Sjoerg				if (tm->tm_hour > 12)
27528021Sjoerg					return 0;
27628021Sjoerg				if (tm->tm_hour == 12)
27728021Sjoerg					tm->tm_hour = 0;
27828021Sjoerg				buf += len;
27928021Sjoerg				break;
28028021Sjoerg			}
28128019Sjoerg
28228021Sjoerg			len = strlen(Locale->pm);
28328021Sjoerg			if (strncasecmp(buf, Locale->pm, len) == 0) {
28428021Sjoerg				if (tm->tm_hour > 12)
28528021Sjoerg					return 0;
28628021Sjoerg				if (tm->tm_hour != 12)
28728021Sjoerg					tm->tm_hour += 12;
28828021Sjoerg				buf += len;
28928021Sjoerg				break;
29028021Sjoerg			}
29128019Sjoerg
29228021Sjoerg			return 0;
29328019Sjoerg
29428021Sjoerg		case 'A':
29528021Sjoerg		case 'a':
29628021Sjoerg			for (i = 0; i < asizeof(Locale->weekday); i++) {
29753942Sache				if (c == 'A') {
29853942Sache					len = strlen(Locale->weekday[i]);
29953942Sache					if (strncasecmp(buf,
30053942Sache							Locale->weekday[i],
30153942Sache							len) == 0)
30253942Sache						break;
30353942Sache				} else {
30453942Sache					len = strlen(Locale->wday[i]);
30553942Sache					if (strncasecmp(buf,
30653942Sache							Locale->wday[i],
30753942Sache							len) == 0)
30853942Sache						break;
30953942Sache				}
31028021Sjoerg			}
31128021Sjoerg			if (i == asizeof(Locale->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			 */
32653083Ssheldonh			if (!isdigit((unsigned char)*buf))
32753083Ssheldonh				return 0;
32853083Ssheldonh
32953083Ssheldonh			for (i = 0; *buf != 0 && isdigit((unsigned char)*buf); buf++) {
33053083Ssheldonh				i *= 10;
33153083Ssheldonh				i += *buf - '0';
33253083Ssheldonh			}
33353083Ssheldonh			if (i > 53)
33453083Ssheldonh				return 0;
33553083Ssheldonh
33653083Ssheldonh			if (*buf != 0 && isspace((unsigned char)*buf))
33753083Ssheldonh				while (*ptr != 0 && !isspace((unsigned char)*ptr))
33853083Ssheldonh					ptr++;
33953083Ssheldonh			break;
34053083Ssheldonh
34153083Ssheldonh		case 'w':
34253083Ssheldonh			if (!isdigit((unsigned char)*buf))
34353083Ssheldonh				return 0;
34453083Ssheldonh
34553083Ssheldonh			for (i = 0; *buf != 0 && isdigit((unsigned char)*buf); buf++) {
34653083Ssheldonh				i *= 10;
34753083Ssheldonh				i += *buf - '0';
34853083Ssheldonh			}
34953083Ssheldonh			if (i > 6)
35053083Ssheldonh				return 0;
35153083Ssheldonh
35253083Ssheldonh			tm->tm_wday = i;
35353083Ssheldonh
35453083Ssheldonh			if (*buf != 0 && isspace((unsigned char)*buf))
35553083Ssheldonh				while (*ptr != 0 && !isspace((unsigned char)*ptr))
35653083Ssheldonh					ptr++;
35753083Ssheldonh			break;
35853083Ssheldonh
35928021Sjoerg		case 'd':
36028021Sjoerg		case 'e':
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 > 31)
36928021Sjoerg				return 0;
37028019Sjoerg
37128021Sjoerg			tm->tm_mday = i;
37228019Sjoerg
37328164Sache			if (*buf != 0 && isspace((unsigned char)*buf))
37428164Sache				while (*ptr != 0 && !isspace((unsigned char)*ptr))
37528021Sjoerg					ptr++;
37628021Sjoerg			break;
37728019Sjoerg
37828021Sjoerg		case 'B':
37928021Sjoerg		case 'b':
38028021Sjoerg		case 'h':
38128021Sjoerg			for (i = 0; i < asizeof(Locale->month); i++) {
38253941Sache				if (Oalternative) {
38353941Sache					if (c == 'B') {
38453941Sache						len = strlen(Locale->alt_month[i]);
38553941Sache						if (strncasecmp(buf,
38653941Sache								Locale->alt_month[i],
38753941Sache								len) == 0)
38853941Sache							break;
38953941Sache					}
39053941Sache				} else {
39153941Sache					if (c == 'B') {
39253941Sache						len = strlen(Locale->month[i]);
39353941Sache						if (strncasecmp(buf,
39453941Sache								Locale->month[i],
39553941Sache								len) == 0)
39653941Sache							break;
39753941Sache					} else {
39853941Sache						len = strlen(Locale->mon[i]);
39953941Sache						if (strncasecmp(buf,
40053941Sache								Locale->mon[i],
40153941Sache								len) == 0)
40253941Sache							break;
40353941Sache					}
40453941Sache				}
40528021Sjoerg			}
40628021Sjoerg			if (i == asizeof(Locale->month))
40728021Sjoerg				return 0;
40828019Sjoerg
40928021Sjoerg			tm->tm_mon = i;
41028021Sjoerg			buf += len;
41128021Sjoerg			break;
41228019Sjoerg
41328021Sjoerg		case 'm':
41428164Sache			if (!isdigit((unsigned char)*buf))
41528021Sjoerg				return 0;
41628019Sjoerg
41728164Sache			for (i = 0; *buf != 0 && isdigit((unsigned char)*buf); buf++) {
41828021Sjoerg				i *= 10;
41928021Sjoerg				i += *buf - '0';
42028021Sjoerg			}
42128021Sjoerg			if (i < 1 || i > 12)
42228021Sjoerg				return 0;
42328019Sjoerg
42428021Sjoerg			tm->tm_mon = i - 1;
42528019Sjoerg
42628164Sache			if (*buf != 0 && isspace((unsigned char)*buf))
42728164Sache				while (*ptr != 0 && !isspace((unsigned char)*ptr))
42828021Sjoerg					ptr++;
42928021Sjoerg			break;
43028019Sjoerg
43128021Sjoerg		case 'Y':
43228021Sjoerg		case 'y':
43328164Sache			if (*buf == 0 || isspace((unsigned char)*buf))
43428021Sjoerg				break;
43528019Sjoerg
43628164Sache			if (!isdigit((unsigned char)*buf))
43728021Sjoerg				return 0;
43828019Sjoerg
43928164Sache			for (i = 0; *buf != 0 && isdigit((unsigned char)*buf); buf++) {
44028021Sjoerg				i *= 10;
44128021Sjoerg				i += *buf - '0';
44228021Sjoerg			}
44328021Sjoerg			if (c == 'Y')
44428021Sjoerg				i -= 1900;
44546051Swes			if (c == 'y' && i < 69)
44646042Swes				i += 100;
44728021Sjoerg			if (i < 0)
44828021Sjoerg				return 0;
44928019Sjoerg
45028021Sjoerg			tm->tm_year = i;
45128019Sjoerg
45228164Sache			if (*buf != 0 && isspace((unsigned char)*buf))
45328164Sache				while (*ptr != 0 && !isspace((unsigned char)*ptr))
45428021Sjoerg					ptr++;
45528021Sjoerg			break;
45648550Sobrien
45748550Sobrien		case 'Z':
45848550Sobrien			{
45948550Sobrien			const char *cp;
46048550Sobrien			char *zonestr;
46148550Sobrien
46252860Sache			for (cp = buf; *cp && isupper((unsigned char)*cp); ++cp) {/*empty*/}
46348550Sobrien			if (cp - buf) {
46448550Sobrien				zonestr = alloca(cp - buf + 1);
46548550Sobrien				strncpy(zonestr, buf, cp - buf);
46648550Sobrien				zonestr[cp - buf] = '\0';
46748550Sobrien				tzset();
46848550Sobrien				if (0 == strcmp(zonestr, "GMT")) {
46948614Sobrien				    got_GMT = 1;
47048550Sobrien				} else if (0 == strcmp(zonestr, tzname[0])) {
47148614Sobrien				    tm->tm_isdst = 0;
47248550Sobrien				} else if (0 == strcmp(zonestr, tzname[1])) {
47348614Sobrien				    tm->tm_isdst = 1;
47448550Sobrien				} else {
47548614Sobrien				    return 0;
47648550Sobrien				}
47748550Sobrien				buf += cp - buf;
47848550Sobrien			}
47948550Sobrien			}
48048550Sobrien			break;
48128021Sjoerg		}
48228021Sjoerg	}
48348614Sobrien	return (char *)buf;
48448614Sobrien}
48528019Sjoerg
48648614Sobrien
48748614Sobrienchar *
48848614Sobrienstrptime(const char *buf, const char *fmt, struct tm *tm)
48948614Sobrien{
49048614Sobrien	char *ret;
49148614Sobrien
49248614Sobrien#ifdef	_THREAD_SAFE
49348614Sobrien	pthread_mutex_lock(&gotgmt_mutex);
49448614Sobrien#endif
49548614Sobrien
49648614Sobrien	got_GMT = 0;
49748614Sobrien	ret = _strptime(buf, fmt, tm);
49848614Sobrien	if (ret && got_GMT) {
49948550Sobrien		time_t t = timegm(tm);
50048614Sobrien	    localtime_r(&t, tm);
50148614Sobrien		got_GMT = 0;
50248550Sobrien	}
50348614Sobrien
50448614Sobrien#ifdef	_THREAD_SAFE
50548614Sobrien	pthread_mutex_unlock(&gotgmt_mutex);
50648614Sobrien#endif
50748614Sobrien
50848614Sobrien	return ret;
50928019Sjoerg}
510