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