strptime.c revision 122830
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 54111010Snectar#include <sys/cdefs.h> 5528019Sjoerg#ifndef lint 5628021Sjoerg#ifndef NOID 57111010Snectarstatic char copyright[] __unused = 5828019Sjoerg"@(#) Copyright (c) 1994 Powerdog Industries. All rights reserved."; 59111010Snectarstatic char sccsid[] __unused = "@(#)strptime.c 0.1 (Powerdog) 94/03/27"; 6028021Sjoerg#endif /* !defined NOID */ 6128019Sjoerg#endif /* not lint */ 6292986Sobrien__FBSDID("$FreeBSD: head/lib/libc/stdtime/strptime.c 122830 2003-11-17 04:19:15Z nectar $"); 6328019Sjoerg 6471579Sdeischen#include "namespace.h" 6528019Sjoerg#include <time.h> 6628019Sjoerg#include <ctype.h> 67122830Snectar#include <errno.h> 6879664Sdd#include <stdlib.h> 6928019Sjoerg#include <string.h> 7048614Sobrien#include <pthread.h> 7171579Sdeischen#include "un-namespace.h" 7271579Sdeischen#include "libc_private.h" 7328021Sjoerg#include "timelocal.h" 7428019Sjoerg 75112156Smtmstatic char * _strptime(const char *, const char *, struct tm *, int *); 7648614Sobrien 7728021Sjoerg#define asizeof(a) (sizeof (a) / sizeof ((a)[0])) 7828019Sjoerg 7948614Sobrienstatic char * 80112156Smtm_strptime(const char *buf, const char *fmt, struct tm *tm, int *GMTp) 8128019Sjoerg{ 8228021Sjoerg char c; 8328021Sjoerg const char *ptr; 8428021Sjoerg int i, 8528021Sjoerg len; 8653941Sache int Ealternative, Oalternative; 8772168Sphantom struct lc_time_T *tptr = __get_current_time_locale(); 8828019Sjoerg 8928021Sjoerg ptr = fmt; 9028021Sjoerg while (*ptr != 0) { 9128021Sjoerg if (*buf == 0) 9228021Sjoerg break; 9328019Sjoerg 9428021Sjoerg c = *ptr++; 9528019Sjoerg 9628021Sjoerg if (c != '%') { 9728164Sache if (isspace((unsigned char)c)) 9828164Sache while (*buf != 0 && isspace((unsigned char)*buf)) 9928021Sjoerg buf++; 10028021Sjoerg else if (c != *buf++) 10128021Sjoerg return 0; 10228021Sjoerg continue; 10328021Sjoerg } 10428019Sjoerg 10553941Sache Ealternative = 0; 10653941Sache Oalternative = 0; 10753941Sachelabel: 10828021Sjoerg c = *ptr++; 10928021Sjoerg switch (c) { 11028021Sjoerg case 0: 11128021Sjoerg case '%': 11228021Sjoerg if (*buf++ != '%') 11328021Sjoerg return 0; 11428021Sjoerg break; 11528019Sjoerg 11653941Sache case '+': 117112156Smtm buf = _strptime(buf, tptr->date_fmt, tm, GMTp); 11828021Sjoerg if (buf == 0) 11928021Sjoerg return 0; 12028021Sjoerg break; 12128019Sjoerg 12253941Sache case 'C': 12353941Sache if (!isdigit((unsigned char)*buf)) 12453941Sache return 0; 12553941Sache 12654316Ssheldonh /* XXX This will break for 3-digit centuries. */ 12754316Ssheldonh len = 2; 12854316Ssheldonh for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 12953941Sache i *= 10; 13053941Sache i += *buf - '0'; 13154316Ssheldonh len--; 13253941Sache } 13353941Sache if (i < 19) 13453941Sache return 0; 13553941Sache 13653941Sache tm->tm_year = i * 100 - 1900; 13753941Sache break; 13853941Sache 13928021Sjoerg case 'c': 140112156Smtm buf = _strptime(buf, tptr->c_fmt, tm, GMTp); 14128021Sjoerg if (buf == 0) 14228021Sjoerg return 0; 14328021Sjoerg break; 14428019Sjoerg 14528021Sjoerg case 'D': 146112156Smtm buf = _strptime(buf, "%m/%d/%y", tm, GMTp); 14728021Sjoerg if (buf == 0) 14828021Sjoerg return 0; 14928021Sjoerg break; 15028019Sjoerg 15153941Sache case 'E': 15253960Sache if (Ealternative || Oalternative) 15353960Sache break; 15453941Sache Ealternative++; 15553941Sache goto label; 15653941Sache 15753941Sache case 'O': 15853960Sache if (Ealternative || Oalternative) 15953960Sache break; 16053941Sache Oalternative++; 16153941Sache goto label; 16253941Sache 16353960Sache case 'F': 164112156Smtm buf = _strptime(buf, "%Y-%m-%d", tm, GMTp); 16574412Sache if (buf == 0) 16674412Sache return 0; 16774412Sache break; 16874412Sache 16928021Sjoerg case 'R': 170112156Smtm buf = _strptime(buf, "%H:%M", tm, GMTp); 17128021Sjoerg if (buf == 0) 17228021Sjoerg return 0; 17328021Sjoerg break; 17428019Sjoerg 17528021Sjoerg case 'r': 176112156Smtm buf = _strptime(buf, tptr->ampm_fmt, tm, GMTp); 17728021Sjoerg if (buf == 0) 17828021Sjoerg return 0; 17928021Sjoerg break; 18028019Sjoerg 18128021Sjoerg case 'T': 182112156Smtm buf = _strptime(buf, "%H:%M:%S", tm, GMTp); 18328021Sjoerg if (buf == 0) 18428021Sjoerg return 0; 18528021Sjoerg break; 18628019Sjoerg 18728021Sjoerg case 'X': 188112156Smtm buf = _strptime(buf, tptr->X_fmt, tm, GMTp); 18928021Sjoerg if (buf == 0) 19028021Sjoerg return 0; 19128021Sjoerg break; 19228019Sjoerg 19328021Sjoerg case 'x': 194112156Smtm buf = _strptime(buf, tptr->x_fmt, tm, GMTp); 19528021Sjoerg if (buf == 0) 19628021Sjoerg return 0; 19728021Sjoerg break; 19828019Sjoerg 19928021Sjoerg case 'j': 20028164Sache if (!isdigit((unsigned char)*buf)) 20128021Sjoerg return 0; 20228019Sjoerg 20354316Ssheldonh len = 3; 20454316Ssheldonh for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 20528021Sjoerg i *= 10; 20628021Sjoerg i += *buf - '0'; 20754316Ssheldonh len--; 20828021Sjoerg } 20953083Ssheldonh if (i < 1 || i > 366) 21028021Sjoerg return 0; 21128019Sjoerg 21253083Ssheldonh tm->tm_yday = i - 1; 21328021Sjoerg break; 21428019Sjoerg 21528021Sjoerg case 'M': 21628021Sjoerg case 'S': 21728164Sache if (*buf == 0 || isspace((unsigned char)*buf)) 21828021Sjoerg break; 21928019Sjoerg 22028164Sache if (!isdigit((unsigned char)*buf)) 22128021Sjoerg return 0; 22228019Sjoerg 22354316Ssheldonh len = 2; 22454316Ssheldonh for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 22528021Sjoerg i *= 10; 22628021Sjoerg i += *buf - '0'; 22754316Ssheldonh len--; 22828021Sjoerg } 22928019Sjoerg 23053083Ssheldonh if (c == 'M') { 23153083Ssheldonh if (i > 59) 23253083Ssheldonh return 0; 23328021Sjoerg tm->tm_min = i; 23453083Ssheldonh } else { 23553083Ssheldonh if (i > 60) 23653083Ssheldonh return 0; 23728021Sjoerg tm->tm_sec = i; 23853083Ssheldonh } 23928019Sjoerg 24028164Sache if (*buf != 0 && isspace((unsigned char)*buf)) 24128164Sache while (*ptr != 0 && !isspace((unsigned char)*ptr)) 24228021Sjoerg ptr++; 24328021Sjoerg break; 24428019Sjoerg 24528021Sjoerg case 'H': 24628021Sjoerg case 'I': 24728021Sjoerg case 'k': 24828021Sjoerg case 'l': 24954316Ssheldonh /* 25054316Ssheldonh * Of these, %l is the only specifier explicitly 25154316Ssheldonh * documented as not being zero-padded. However, 25254316Ssheldonh * there is no harm in allowing zero-padding. 25354316Ssheldonh * 25454316Ssheldonh * XXX The %l specifier may gobble one too many 25554316Ssheldonh * digits if used incorrectly. 25654316Ssheldonh */ 25728164Sache if (!isdigit((unsigned char)*buf)) 25828021Sjoerg return 0; 25928019Sjoerg 26054316Ssheldonh len = 2; 26154316Ssheldonh for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 26228021Sjoerg i *= 10; 26328021Sjoerg i += *buf - '0'; 26454316Ssheldonh len--; 26528021Sjoerg } 26628021Sjoerg if (c == 'H' || c == 'k') { 26728021Sjoerg if (i > 23) 26828021Sjoerg return 0; 26954301Ssheldonh } else if (i > 12) 27028021Sjoerg return 0; 27128019Sjoerg 27228021Sjoerg tm->tm_hour = i; 27328019Sjoerg 27428164Sache if (*buf != 0 && isspace((unsigned char)*buf)) 27528164Sache while (*ptr != 0 && !isspace((unsigned char)*ptr)) 27628021Sjoerg ptr++; 27728021Sjoerg break; 27828019Sjoerg 27928021Sjoerg case 'p': 28054316Ssheldonh /* 28154316Ssheldonh * XXX This is bogus if parsed before hour-related 28254316Ssheldonh * specifiers. 28354316Ssheldonh */ 28472168Sphantom len = strlen(tptr->am); 28572168Sphantom if (strncasecmp(buf, tptr->am, len) == 0) { 28628021Sjoerg if (tm->tm_hour > 12) 28728021Sjoerg return 0; 28828021Sjoerg if (tm->tm_hour == 12) 28928021Sjoerg tm->tm_hour = 0; 29028021Sjoerg buf += len; 29128021Sjoerg break; 29228021Sjoerg } 29328019Sjoerg 29472168Sphantom len = strlen(tptr->pm); 29572168Sphantom if (strncasecmp(buf, tptr->pm, len) == 0) { 29628021Sjoerg if (tm->tm_hour > 12) 29728021Sjoerg return 0; 29828021Sjoerg if (tm->tm_hour != 12) 29928021Sjoerg tm->tm_hour += 12; 30028021Sjoerg buf += len; 30128021Sjoerg break; 30228021Sjoerg } 30328019Sjoerg 30428021Sjoerg return 0; 30528019Sjoerg 30628021Sjoerg case 'A': 30728021Sjoerg case 'a': 30872168Sphantom for (i = 0; i < asizeof(tptr->weekday); i++) { 30974409Sache len = strlen(tptr->weekday[i]); 31074409Sache if (strncasecmp(buf, tptr->weekday[i], 31174409Sache len) == 0) 31274409Sache break; 31374409Sache len = strlen(tptr->wday[i]); 31474409Sache if (strncasecmp(buf, tptr->wday[i], 31574409Sache len) == 0) 31674409Sache break; 31728021Sjoerg } 31872168Sphantom if (i == asizeof(tptr->weekday)) 31928021Sjoerg return 0; 32028019Sjoerg 32128021Sjoerg tm->tm_wday = i; 32228021Sjoerg buf += len; 32328021Sjoerg break; 32428019Sjoerg 32553083Ssheldonh case 'U': 32653083Ssheldonh case 'W': 32753083Ssheldonh /* 32853083Ssheldonh * XXX This is bogus, as we can not assume any valid 32953083Ssheldonh * information present in the tm structure at this 33053083Ssheldonh * point to calculate a real value, so just check the 33153083Ssheldonh * range for now. 33253083Ssheldonh */ 33353083Ssheldonh if (!isdigit((unsigned char)*buf)) 33453083Ssheldonh return 0; 33553083Ssheldonh 33654316Ssheldonh len = 2; 33754316Ssheldonh for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 33853083Ssheldonh i *= 10; 33953083Ssheldonh i += *buf - '0'; 34054316Ssheldonh len--; 34153083Ssheldonh } 34253083Ssheldonh if (i > 53) 34353083Ssheldonh return 0; 34453083Ssheldonh 34553083Ssheldonh if (*buf != 0 && isspace((unsigned char)*buf)) 34653083Ssheldonh while (*ptr != 0 && !isspace((unsigned char)*ptr)) 34753083Ssheldonh ptr++; 34853083Ssheldonh break; 34953083Ssheldonh 35053083Ssheldonh case 'w': 35153083Ssheldonh if (!isdigit((unsigned char)*buf)) 35253083Ssheldonh return 0; 35353083Ssheldonh 35454316Ssheldonh i = *buf - '0'; 35553083Ssheldonh if (i > 6) 35653083Ssheldonh return 0; 35753083Ssheldonh 35853083Ssheldonh tm->tm_wday = i; 35953083Ssheldonh 36053083Ssheldonh if (*buf != 0 && isspace((unsigned char)*buf)) 36153083Ssheldonh while (*ptr != 0 && !isspace((unsigned char)*ptr)) 36253083Ssheldonh ptr++; 36353083Ssheldonh break; 36453083Ssheldonh 36528021Sjoerg case 'd': 36628021Sjoerg case 'e': 36754316Ssheldonh /* 36854316Ssheldonh * The %e specifier is explicitly documented as not 36954316Ssheldonh * being zero-padded but there is no harm in allowing 37054316Ssheldonh * such padding. 37154316Ssheldonh * 37254316Ssheldonh * XXX The %e specifier may gobble one too many 37354316Ssheldonh * digits if used incorrectly. 37454316Ssheldonh */ 37528164Sache if (!isdigit((unsigned char)*buf)) 37628021Sjoerg return 0; 37728019Sjoerg 37854316Ssheldonh len = 2; 37954316Ssheldonh for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 38028021Sjoerg i *= 10; 38128021Sjoerg i += *buf - '0'; 38254316Ssheldonh len--; 38328021Sjoerg } 38428021Sjoerg if (i > 31) 38528021Sjoerg return 0; 38628019Sjoerg 38728021Sjoerg tm->tm_mday = i; 38828019Sjoerg 38928164Sache if (*buf != 0 && isspace((unsigned char)*buf)) 39028164Sache while (*ptr != 0 && !isspace((unsigned char)*ptr)) 39128021Sjoerg ptr++; 39228021Sjoerg break; 39328019Sjoerg 39428021Sjoerg case 'B': 39528021Sjoerg case 'b': 39628021Sjoerg case 'h': 39772168Sphantom for (i = 0; i < asizeof(tptr->month); i++) { 39853941Sache if (Oalternative) { 39953941Sache if (c == 'B') { 40072168Sphantom len = strlen(tptr->alt_month[i]); 40153941Sache if (strncasecmp(buf, 40272168Sphantom tptr->alt_month[i], 40353941Sache len) == 0) 40453941Sache break; 40553941Sache } 40653941Sache } else { 40774409Sache len = strlen(tptr->month[i]); 40874409Sache if (strncasecmp(buf, tptr->month[i], 40974409Sache len) == 0) 41074409Sache break; 41174409Sache len = strlen(tptr->mon[i]); 41274409Sache if (strncasecmp(buf, tptr->mon[i], 41374409Sache len) == 0) 41474409Sache break; 41553941Sache } 41628021Sjoerg } 41772168Sphantom if (i == asizeof(tptr->month)) 41828021Sjoerg return 0; 41928019Sjoerg 42028021Sjoerg tm->tm_mon = i; 42128021Sjoerg buf += len; 42228021Sjoerg break; 42328019Sjoerg 42428021Sjoerg case 'm': 42528164Sache if (!isdigit((unsigned char)*buf)) 42628021Sjoerg return 0; 42728019Sjoerg 42854316Ssheldonh len = 2; 42954316Ssheldonh for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 43028021Sjoerg i *= 10; 43128021Sjoerg i += *buf - '0'; 43254316Ssheldonh len--; 43328021Sjoerg } 43428021Sjoerg if (i < 1 || i > 12) 43528021Sjoerg return 0; 43628019Sjoerg 43728021Sjoerg tm->tm_mon = i - 1; 43828019Sjoerg 43928164Sache if (*buf != 0 && isspace((unsigned char)*buf)) 44028164Sache while (*ptr != 0 && !isspace((unsigned char)*ptr)) 44128021Sjoerg ptr++; 44228021Sjoerg break; 44328019Sjoerg 44479664Sdd case 's': 44579664Sdd { 44679664Sdd char *cp; 447122830Snectar int sverrno; 448122830Snectar long n; 44979664Sdd time_t t; 45079664Sdd 451122830Snectar sverrno = errno; 452122830Snectar errno = 0; 453122830Snectar n = strtol(buf, &cp, 10); 454122830Snectar if (errno == ERANGE || (long)(t = n) != n) { 455122830Snectar errno = sverrno; 45679664Sdd return 0; 457122830Snectar } 458122830Snectar errno = sverrno; 45979664Sdd buf = cp; 46079664Sdd gmtime_r(&t, tm); 461112156Smtm *GMTp = 1; 46279664Sdd } 46379664Sdd break; 46479664Sdd 46528021Sjoerg case 'Y': 46628021Sjoerg case 'y': 46728164Sache if (*buf == 0 || isspace((unsigned char)*buf)) 46828021Sjoerg break; 46928019Sjoerg 47028164Sache if (!isdigit((unsigned char)*buf)) 47128021Sjoerg return 0; 47228019Sjoerg 47354316Ssheldonh len = (c == 'Y') ? 4 : 2; 47454316Ssheldonh for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 47528021Sjoerg i *= 10; 47628021Sjoerg i += *buf - '0'; 47754316Ssheldonh len--; 47828021Sjoerg } 47928021Sjoerg if (c == 'Y') 48028021Sjoerg i -= 1900; 48146051Swes if (c == 'y' && i < 69) 48246042Swes i += 100; 48328021Sjoerg if (i < 0) 48428021Sjoerg return 0; 48528019Sjoerg 48628021Sjoerg tm->tm_year = i; 48728019Sjoerg 48828164Sache if (*buf != 0 && isspace((unsigned char)*buf)) 48928164Sache while (*ptr != 0 && !isspace((unsigned char)*ptr)) 49028021Sjoerg ptr++; 49128021Sjoerg break; 49248550Sobrien 49348550Sobrien case 'Z': 49448550Sobrien { 49548550Sobrien const char *cp; 49648550Sobrien char *zonestr; 49748550Sobrien 49852860Sache for (cp = buf; *cp && isupper((unsigned char)*cp); ++cp) {/*empty*/} 49948550Sobrien if (cp - buf) { 50048550Sobrien zonestr = alloca(cp - buf + 1); 50148550Sobrien strncpy(zonestr, buf, cp - buf); 50248550Sobrien zonestr[cp - buf] = '\0'; 50348550Sobrien tzset(); 50448550Sobrien if (0 == strcmp(zonestr, "GMT")) { 505112156Smtm *GMTp = 1; 50648550Sobrien } else if (0 == strcmp(zonestr, tzname[0])) { 50748614Sobrien tm->tm_isdst = 0; 50848550Sobrien } else if (0 == strcmp(zonestr, tzname[1])) { 50948614Sobrien tm->tm_isdst = 1; 51048550Sobrien } else { 51148614Sobrien return 0; 51248550Sobrien } 51348550Sobrien buf += cp - buf; 51448550Sobrien } 51548550Sobrien } 51648550Sobrien break; 51728021Sjoerg } 51828021Sjoerg } 51948614Sobrien return (char *)buf; 52048614Sobrien} 52128019Sjoerg 52248614Sobrien 52348614Sobrienchar * 524103012Stjrstrptime(const char * __restrict buf, const char * __restrict fmt, 525103012Stjr struct tm * __restrict tm) 52648614Sobrien{ 52748614Sobrien char *ret; 528112156Smtm int gmt; 52948614Sobrien 530112156Smtm gmt = 0; 531112156Smtm ret = _strptime(buf, fmt, tm, &gmt); 532114285Smtm if (ret && gmt) { 533114285Smtm time_t t = timegm(tm); 53471579Sdeischen localtime_r(&t, tm); 53548550Sobrien } 53648614Sobrien 537112156Smtm return (ret); 53828019Sjoerg} 539