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