Module Name: src Committed By: christos Date: Thu Oct 29 17:54:49 UTC 2015
Modified Files: src/lib/libc/time: strptime.3 strptime.c Log Message: PR/50380: Balazs Scheidler: strptime() returns incorrect values in tm_gmtoff - Always offset in seconds. - Handle arbitrary timezones. To generate a diff of this commit: cvs rdiff -u -r1.31 -r1.32 src/lib/libc/time/strptime.3 cvs rdiff -u -r1.49 -r1.50 src/lib/libc/time/strptime.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/lib/libc/time/strptime.3 diff -u src/lib/libc/time/strptime.3:1.31 src/lib/libc/time/strptime.3:1.32 --- src/lib/libc/time/strptime.3:1.31 Mon Apr 6 10:38:22 2015 +++ src/lib/libc/time/strptime.3 Thu Oct 29 13:54:49 2015 @@ -1,4 +1,4 @@ -.\" $NetBSD: strptime.3,v 1.31 2015/04/06 14:38:22 ginsbach Exp $ +.\" $NetBSD: strptime.3,v 1.32 2015/10/29 17:54:49 christos Exp $ .\" .\" Copyright (c) 1997, 1998, 2008 The NetBSD Foundation, Inc. .\" All rights reserved. @@ -223,33 +223,56 @@ the year, including the century (i.e., 1 .It Cm \&%z an ISO 8601 or RFC-2822 time zone specification. This is one of the following: -the offset from -Coordinated Universal Time +.Bl -dash -offset indent -compact +.It +The offset from Coordinated Universal Time .Pq Ql UTC specified as: -.Dq [+-]hhmm , -.Dq [+-]hh:mm , -or -.Dq [+-]hh ; +.Bl -bullet -offset indent -compact +.It +[+-]hhmm +.It +[+-]hh:mm +.It +[+-]hh +.El +.It .Ql UTC specified as: -.Dq GMT -.Pq Ql Greenwich Mean Time , -.Dq UT -.Pq Ql Universal Time , -or -.Dq Z -.Pq Ql Zulu Time ; -a three character US time zone specified as: -.Dq EDT , -.Dq EST , -.Dq CDT , -.Dq CST , -.Dq MDT , -.Dq MST , -.Dq PDT , -or -.Dq PST , +.Bl -bullet -offset indent -compact +.It +UTC +.Pq Ql Coordinated Universal time +.It +GMT +.Pq Ql Greenwich Mean Time +.It +UT +.Pq Ql Universal Time +.It +Z +.Pq Ql Zulu Time +.El +.It +A three character US time zone specified as: +.Bl -bullet -offset indent -compact +.It +EDT +.It +EST +.It +CDT +.It +CST +.It +MDT +.It +MST +.It +PDT +.It +PST +.El with the first letter standing for .Ql Eastern .Pq Dq E , @@ -270,15 +293,22 @@ time or .Ql Standard .Pq Dq S -time; +time +.It a single letter military time zone specified as: +.Bl -bullet -offset indent -compact +.It .Dq A through .Dq I -and +.It .Dq K through .Dq Y . +.El +.It +An arbirtrary timezone name that can be loaded from the database. +.El .Po A .Nx Index: src/lib/libc/time/strptime.c diff -u src/lib/libc/time/strptime.c:1.49 src/lib/libc/time/strptime.c:1.50 --- src/lib/libc/time/strptime.c:1.49 Fri Oct 9 13:21:45 2015 +++ src/lib/libc/time/strptime.c Thu Oct 29 13:54:49 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: strptime.c,v 1.49 2015/10/09 17:21:45 christos Exp $ */ +/* $NetBSD: strptime.c,v 1.50 2015/10/29 17:54:49 christos Exp $ */ /*- * Copyright (c) 1997, 1998, 2005, 2008 The NetBSD Foundation, Inc. @@ -31,7 +31,7 @@ #include <sys/cdefs.h> #if defined(LIBC_SCCS) && !defined(lint) -__RCSID("$NetBSD: strptime.c,v 1.49 2015/10/09 17:21:45 christos Exp $"); +__RCSID("$NetBSD: strptime.c,v 1.50 2015/10/29 17:54:49 christos Exp $"); #endif #include "namespace.h" @@ -114,6 +114,34 @@ first_wday_of(int yr) (isleap(yr) ? 6 : 0) + 1) % 7; } +#define delim(p) ((p) == '\0' || isspace((unsigned char)(p))) + +static int +fromzone(const unsigned char **bp, struct tm *tm) +{ + timezone_t tz; + char buf[512], *p; + + for (p = buf; !delim(**bp) && p < &buf[sizeof(buf) - 1]; (*bp)++) + *p++ = **bp; + *p = '\0'; + + tz = tzalloc(buf); + if (tz == NULL) + return 0; + + tm->tm_isdst = 0; /* XXX */ +#ifdef TM_GMTOFF + tm->TM_GMTOFF = tzgetgmtoff(tz, tm->tm_isdst); +#endif +#ifdef TM_ZONE + // Can't use tzgetname() here because we are going to free() + tm->TM_ZONE = utc; /* XXX */ +#endif + tzfree(tz); + return 1; +} + char * strptime(const char *buf, const char *fmt, struct tm *tm) { @@ -124,7 +152,7 @@ char * strptime_l(const char *buf, const char *fmt, struct tm *tm, locale_t loc) { unsigned char c; - const unsigned char *bp, *ep; + const unsigned char *bp, *ep, *zname; int alt_format, i, split_year = 0, neg = 0, state = 0, day_offset = -1, week_offset = 0, offs; const char *new_fmt; @@ -439,13 +467,18 @@ literal: if (ep != NULL) { tm->tm_isdst = i; #ifdef TM_GMTOFF - tm->TM_GMTOFF = -(timezone); +#ifdef USG_COMPAT + tm->TM_GMTOFF = -timezone; +#else + tm->TM_GMTOFF = -timezone(); +#endif #endif #ifdef TM_ZONE tm->TM_ZONE = tzname[i]; + bp = ep; #endif - } - bp = ep; + } else + (void)fromzone(&bp, tm); } continue; @@ -470,16 +503,21 @@ literal: while (isspace(*bp)) bp++; + zname = bp; switch (*bp++) { case 'G': if (*bp++ != 'M') - return NULL; + goto namedzone; /*FALLTHROUGH*/ case 'U': if (*bp++ != 'T') - return NULL; + goto namedzone; + else if (!delim(*bp) && *bp++ != 'C') + goto namedzone; /*FALLTHROUGH*/ case 'Z': + if (!delim(*bp)) + goto namedzone; tm->tm_isdst = 0; #ifdef TM_GMTOFF tm->TM_GMTOFF = 0; @@ -495,11 +533,42 @@ literal: neg = 1; break; default: - --bp; +namedzone: + bp = zname; + + /* Military style */ + if (delim(bp[1]) && + ((*bp >= 'A' && *bp <= 'I') || + (*bp >= 'L' && *bp <= 'Y'))) { +#ifdef TM_GMTOFF + /* Argh! No 'J'! */ + if (*bp >= 'A' && *bp <= 'I') + tm->TM_GMTOFF = + ('A' - 1) - (int)*bp; + else if (*bp >= 'L' && *bp <= 'M') + tm->TM_GMTOFF = 'A' - (int)*bp; + else if (*bp >= 'N' && *bp <= 'Y') + tm->TM_GMTOFF = (int)*bp - 'M'; + tm->TM_GMTOFF *= 3600; +#endif +#ifdef TM_ZONE + tm->TM_ZONE = utc; /* XXX */ +#endif + bp++; + continue; + } + + /* + * From our 3 letter hard-coded table + * XXX: Can be removed, handled by tzload() + */ + if (delim(bp[0]) || delim(bp[1]) || + delim(bp[2]) || !delim(bp[3])) + goto loadzone; ep = find_string(bp, &i, nast, NULL, 4); if (ep != NULL) { #ifdef TM_GMTOFF - tm->TM_GMTOFF = -5 - i; + tm->TM_GMTOFF = (-5 - i) * 3600; #endif #ifdef TM_ZONE tm->TM_ZONE = __UNCONST(nast[i]); @@ -511,7 +580,7 @@ literal: if (ep != NULL) { tm->tm_isdst = 1; #ifdef TM_GMTOFF - tm->TM_GMTOFF = -4 - i; + tm->TM_GMTOFF = (-4 - i) * 3600; #endif #ifdef TM_ZONE tm->TM_ZONE = __UNCONST(nadt[i]); @@ -520,24 +589,12 @@ literal: continue; } - if ((*bp >= 'A' && *bp <= 'I') || - (*bp >= 'L' && *bp <= 'Y')) { -#ifdef TM_GMTOFF - /* Argh! No 'J'! */ - if (*bp >= 'A' && *bp <= 'I') - tm->TM_GMTOFF = - ('A' - 1) - (int)*bp; - else if (*bp >= 'L' && *bp <= 'M') - tm->TM_GMTOFF = 'A' - (int)*bp; - else if (*bp >= 'N' && *bp <= 'Y') - tm->TM_GMTOFF = (int)*bp - 'M'; -#endif -#ifdef TM_ZONE - tm->TM_ZONE = utc; /* XXX */ -#endif - bp++; +loadzone: + /* + * The hard way, load the zone! + */ + if (fromzone(&bp, tm)) continue; - } return NULL; } offs = 0; @@ -553,16 +610,18 @@ literal: } break; } + if (isdigit(*bp)) + return NULL; switch (i) { case 2: - offs *= 100; + offs *= 3600; break; case 4: i = offs % 100; if (i >= 60) return NULL; /* Convert minutes into decimal */ - offs = (offs / 100) * 100 + (i * 50) / 30; + offs = (offs / 100) * 3600 + i * 60; break; default: return NULL;