PR/50380: Balazs Scheidler: strptime() returns incorrect values in tm_gmtoff

- Always offset in seconds.
- Handle arbitrary timezones.
This commit is contained in:
christos 2015-10-29 17:54:49 +00:00
parent 37de2f9dd3
commit 4ba084a19e
2 changed files with 145 additions and 56 deletions

View File

@ -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., 1996).
.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

View File

@ -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;