Mercurial > illumos > illumos-gate
changeset 13249:abc9578d5a0e
317 strptime needs %Z support
Reviewed by: richlowe@richlowe.net
Reviewed by: lvskiprof@cox.net
Reviewed by: gwr@nexenta.com
Approved by: richlowe@richlowe.net
author | Garrett D'Amore <garrett@nexenta.com> |
---|---|
date | Sat, 04 Dec 2010 17:33:07 -0800 |
parents | ed4997a098c7 |
children | 34250eb8d616 |
files | usr/src/head/time.h usr/src/lib/libc/port/gen/localtime.c usr/src/lib/libc/port/locale/strftime.c usr/src/lib/libc/port/locale/strptime.c usr/src/lib/libc/port/mapfile-vers |
diffstat | 5 files changed, 277 insertions(+), 196 deletions(-) [+] |
line wrap: on
line diff
--- a/usr/src/head/time.h Fri Dec 03 07:32:02 2010 -0800 +++ b/usr/src/head/time.h Sat Dec 04 17:33:07 2010 -0800 @@ -26,12 +26,13 @@ * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ +/* + * Copyright 2010 Nexenta Systems, Inc. Al rights reserved. + */ #ifndef _TIME_H #define _TIME_H -#pragma ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.18 */ - #include <sys/feature_tests.h> #include <iso/time_iso.h> #if (!defined(_STRICT_STDC) && !defined(__XOPEN_OR_POSIX)) || \ @@ -177,6 +178,7 @@ #if (!defined(_STRICT_STDC) && !defined(__XOPEN_OR_POSIX)) || \ defined(__EXTENSIONS__) +extern time_t timegm(struct tm *); extern int cftime(char *, char *, const time_t *); extern int ascftime(char *, const char *, const struct tm *); extern long altzone; @@ -198,6 +200,7 @@ extern int cftime(), ascftime(); extern void tzset(); +extern time_t timegm(); #ifdef _STRPTIME_DONTZERO #ifdef __PRAGMA_REDEFINE_EXTNAME
--- a/usr/src/lib/libc/port/gen/localtime.c Fri Dec 03 07:32:02 2010 -0800 +++ b/usr/src/lib/libc/port/gen/localtime.c Sat Dec 04 17:33:07 2010 -0800 @@ -20,6 +20,10 @@ */ /* + * Copyright 2010 Nexenta Systems, Inc. All rights reserved. + */ + +/* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -673,8 +677,8 @@ * normalized time_t, also inducing documented side-effects in * extern global zone state variables. (See mktime(3C)). */ -time_t -mktime(struct tm *tmptr) +static time_t +mktime1(struct tm *tmptr, int usetz) { struct tm _tm; long long t; /* must hold more than 32-bit time_t */ @@ -722,74 +726,93 @@ else t += SECSPERDAY * __yday_to_month[tmptr->tm_mon]; - unused = ltzset_u((time_t)t); - /* Attempt to convert time to GMT based on tm_isdst setting */ - t += (tmptr->tm_isdst > 0) ? altzone : timezone; + if (usetz) { + /* + * If called from mktime(), then we need to do the TZ + * related transformations. + */ + + unused = ltzset_u((time_t)t); + + /* Attempt to convert time to GMT based on tm_isdst setting */ + t += (tmptr->tm_isdst > 0) ? altzone : timezone; #ifdef _ILP32 - overflow = t > LONG_MAX || t < LONG_MIN || - tmptr->tm_year < 1 || tmptr->tm_year > 138; + overflow = t > LONG_MAX || t < LONG_MIN || + tmptr->tm_year < 1 || tmptr->tm_year > 138; #else - overflow = t > LONG_MAX || t < LONG_MIN; + overflow = t > LONG_MAX || t < LONG_MIN; #endif - set_zone_context((time_t)t); - if (tmptr->tm_isdst < 0) { - long dst_delta = timezone - altzone; - switch (curr_zonerules) { - case ZONEINFO: - if (is_in_dst) { - t -= dst_delta; - set_zone_context((time_t)t); + set_zone_context((time_t)t); + if (tmptr->tm_isdst < 0) { + long dst_delta = timezone - altzone; + switch (curr_zonerules) { + case ZONEINFO: if (is_in_dst) { - (void) offtime_u((time_t)t, - -altzone, &_tm); - _tm.tm_isdst = 1; + t -= dst_delta; + set_zone_context((time_t)t); + if (is_in_dst) { + (void) offtime_u((time_t)t, + -altzone, &_tm); + _tm.tm_isdst = 1; + } else { + (void) offtime_u((time_t)t, + -timezone, &_tm); + } } else { - (void) offtime_u((time_t)t, - -timezone, &_tm); + (void) offtime_u((time_t)t, -timezone, + &_tm); } - } else { - (void) offtime_u((time_t)t, -timezone, &_tm); - } - break; - case POSIX_USA: - case POSIX: - if (is_in_dst) { - t -= dst_delta; - set_zone_context((time_t)t); + break; + case POSIX_USA: + case POSIX: if (is_in_dst) { - (void) offtime_u((time_t)t, - -altzone, &_tm); - _tm.tm_isdst = 1; - } else { - (void) offtime_u((time_t)t, - -timezone, &_tm); - } - } else { /* check for ambiguous 'fallback' transition */ - set_zone_context((time_t)t - dst_delta); - if (is_in_dst) { /* In fallback, force DST */ t -= dst_delta; - (void) offtime_u((time_t)t, - -altzone, &_tm); - _tm.tm_isdst = 1; + set_zone_context((time_t)t); + if (is_in_dst) { + (void) offtime_u((time_t)t, + -altzone, &_tm); + _tm.tm_isdst = 1; + } else { + (void) offtime_u((time_t)t, + -timezone, &_tm); + } } else { - (void) offtime_u((time_t)t, - -timezone, &_tm); + /* + * check for ambiguous + * 'fallback' transition + */ + set_zone_context((time_t)t - dst_delta); + if (is_in_dst) { + /* In fallback, force DST */ + t -= dst_delta; + (void) offtime_u((time_t)t, + -altzone, &_tm); + _tm.tm_isdst = 1; + } else { + (void) offtime_u((time_t)t, + -timezone, &_tm); + } } - } - break; + break; - case ZONERULES_INVALID: - (void) offtime_u((time_t)t, 0L, &_tm); - break; + case ZONERULES_INVALID: + (void) offtime_u((time_t)t, 0L, &_tm); + break; + } + } else if (is_in_dst) { + (void) offtime_u((time_t)t, -altzone, &_tm); + _tm.tm_isdst = 1; + } else { + (void) offtime_u((time_t)t, -timezone, &_tm); } - } else if (is_in_dst) { - (void) offtime_u((time_t)t, -altzone, &_tm); - _tm.tm_isdst = 1; - } else { - (void) offtime_u((time_t)t, -timezone, &_tm); + + } else { /* !usetz, i.e. using UTC */ + overflow = 0; + /* Normalize the TM structure */ + (void) offtime_u((time_t)t, 0, &_tm); } if (overflow || t > LONG_MAX || t < LONG_MIN) { @@ -807,6 +830,19 @@ return ((time_t)t); } +time_t +mktime(struct tm *tmptr) +{ + return (mktime1(tmptr, TRUE)); +} + +time_t +timegm(struct tm *tmptr) +{ + return (mktime1(tmptr, FALSE)); +} + + /* * Sets extern global zone state variables based on the current * time. Specifically, tzname[], timezone, altzone, and daylight
--- a/usr/src/lib/libc/port/locale/strftime.c Fri Dec 03 07:32:02 2010 -0800 +++ b/usr/src/lib/libc/port/locale/strftime.c Sat Dec 04 17:33:07 2010 -0800 @@ -233,12 +233,16 @@ pt, ptlim); continue; - /* - * Note: 's' for seconds since epoch was removed. - * While FreeBSD and Linux appear to support this, - * Sun Solaris does not. Furthermore, the FreeBSD - * implementation was not correct for _LP64. - */ + case 's': + { + struct tm tm; + char *buf; + + tm = *t; + (void) asprintf(&buf, "%ld", mktime(&tm)); + pt = _add(buf, pt, ptlim); + continue; + } case 'T': pt = _fmt("%H:%M:%S", t, pt, ptlim);
--- a/usr/src/lib/libc/port/locale/strptime.c Fri Dec 03 07:32:02 2010 -0800 +++ b/usr/src/lib/libc/port/locale/strptime.c Sat Dec 04 17:33:07 2010 -0800 @@ -43,15 +43,27 @@ #define asizeof(a) (sizeof (a) / sizeof ((a)[0])) +#define F_GMT (1 << 0) +#define F_ZERO (1 << 1) +#define F_RECURSE (1 << 2) + static char * -__strptime(const char *buf, const char *fmt, struct tm *tm) +__strptime(const char *buf, const char *fmt, struct tm *tm, int *flagsp) { char c; const char *ptr; - int i, len; + int i, len, recurse = 0; int Ealternative, Oalternative; struct lc_time_T *tptr = __get_current_time_locale(); + if (*flagsp & F_RECURSE) + recurse = 1; + *flagsp |= F_RECURSE; + + if (*flagsp & F_ZERO) + (void) memset(tm, 0, sizeof (*tm)); + *flagsp &= ~F_ZERO; + ptr = fmt; while (*ptr != 0) { if (*buf == 0) @@ -60,12 +72,11 @@ c = *ptr++; if (c != '%') { - if (isspace((unsigned char)c)) - while (*buf != 0 && - isspace((unsigned char)*buf)) + if (isspace(c)) + while (isspace(*buf)) buf++; else if (c != *buf++) - return (0); + return (NULL); continue; } @@ -77,44 +88,42 @@ case 0: case '%': if (*buf++ != '%') - return (0); + return (NULL); break; case '+': - buf = __strptime(buf, tptr->date_fmt, tm); - if (buf == 0) - return (0); + buf = __strptime(buf, tptr->date_fmt, tm, flagsp); + if (buf == NULL) + return (NULL); break; case 'C': - if (!isdigit((unsigned char)*buf)) - return (0); + if (!isdigit(*buf)) + return (NULL); /* XXX This will break for 3-digit centuries. */ len = 2; - for (i = 0; - len && isdigit((unsigned char)*buf); - buf++) { + for (i = 0; len && isdigit(*buf); buf++) { i *= 10; i += *buf - '0'; len--; } if (i < 19) - return (0); + return (NULL); tm->tm_year = i * 100 - 1900; break; case 'c': - buf = __strptime(buf, tptr->c_fmt, tm); - if (buf == 0) - return (0); + buf = __strptime(buf, tptr->c_fmt, tm, flagsp); + if (buf == NULL) + return (NULL); break; case 'D': - buf = __strptime(buf, "%m/%d/%y", tm); - if (buf == 0) - return (0); + buf = __strptime(buf, "%m/%d/%y", tm, flagsp); + if (buf == NULL) + return (NULL); break; case 'E': @@ -130,71 +139,67 @@ goto label; case 'F': - buf = __strptime(buf, "%Y-%m-%d", tm); - if (buf == 0) - return (0); + buf = __strptime(buf, "%Y-%m-%d", tm, flagsp); + if (buf == NULL) + return (NULL); break; case 'R': - buf = __strptime(buf, "%H:%M", tm); - if (buf == 0) - return (0); + buf = __strptime(buf, "%H:%M", tm, flagsp); + if (buf == NULL) + return (NULL); break; case 'r': - buf = __strptime(buf, tptr->ampm_fmt, tm); - if (buf == 0) - return (0); + buf = __strptime(buf, tptr->ampm_fmt, tm, flagsp); + if (buf == NULL) + return (NULL); break; case 'T': - buf = __strptime(buf, "%H:%M:%S", tm); - if (buf == 0) - return (0); + buf = __strptime(buf, "%H:%M:%S", tm, flagsp); + if (buf == NULL) + return (NULL); break; case 'X': - buf = __strptime(buf, tptr->X_fmt, tm); - if (buf == 0) - return (0); + buf = __strptime(buf, tptr->X_fmt, tm, flagsp); + if (buf == NULL) + return (NULL); break; case 'x': - buf = __strptime(buf, tptr->x_fmt, tm); - if (buf == 0) - return (0); + buf = __strptime(buf, tptr->x_fmt, tm, flagsp); + if (buf == NULL) + return (NULL); break; case 'j': - if (!isdigit((unsigned char)*buf)) - return (0); + if (!isdigit(*buf)) + return (NULL); len = 3; - for (i = 0; - len && isdigit((unsigned char)*buf); - buf++) { + for (i = 0; len && isdigit(*buf); buf++) { i *= 10; i += *buf - '0'; len--; } if (i < 1 || i > 366) - return (0); + return (NULL); tm->tm_yday = i - 1; break; case 'M': case 'S': - if (*buf == 0 || isspace((unsigned char)*buf)) + if (*buf == 0 || isspace(*buf)) break; - if (!isdigit((unsigned char)*buf)) - return (0); + if (!isdigit(*buf)) + return (NULL); len = 2; - for (i = 0; - len && isdigit((unsigned char)*buf); - buf++) { + for (i = 0; len && isdigit(*buf); buf++) { i *= 10; i += *buf - '0'; len--; @@ -202,17 +207,16 @@ if (c == 'M') { if (i > 59) - return (0); + return (NULL); tm->tm_min = i; } else { if (i > 60) - return (0); + return (NULL); tm->tm_sec = i; } - if (*buf != 0 && isspace((unsigned char)*buf)) - while (*ptr != 0 && - !isspace((unsigned char)*ptr)) + if (isspace(*buf)) + while (*ptr != 0 && !isspace(*ptr)) ptr++; break; @@ -228,28 +232,25 @@ * XXX The %l specifier may gobble one too many * digits if used incorrectly. */ - if (!isdigit((unsigned char)*buf)) - return (0); + if (!isdigit(*buf)) + return (NULL); len = 2; - for (i = 0; - len && isdigit((unsigned char)*buf); - buf++) { + for (i = 0; len && isdigit(*buf); buf++) { i *= 10; i += *buf - '0'; len--; } if (c == 'H' || c == 'k') { if (i > 23) - return (0); + return (NULL); } else if (i > 12) - return (0); + return (NULL); tm->tm_hour = i; - if (*buf != 0 && isspace((unsigned char)*buf)) - while (*ptr != 0 && - !isspace((unsigned char)*ptr)) + if (isspace(*buf)) + while (*ptr != 0 && !isspace(*ptr)) ptr++; break; @@ -261,7 +262,7 @@ len = strlen(tptr->am); if (strncasecmp(buf, tptr->am, len) == 0) { if (tm->tm_hour > 12) - return (0); + return (NULL); if (tm->tm_hour == 12) tm->tm_hour = 0; buf += len; @@ -271,14 +272,14 @@ len = strlen(tptr->pm); if (strncasecmp(buf, tptr->pm, len) == 0) { if (tm->tm_hour > 12) - return (0); + return (NULL); if (tm->tm_hour != 12) tm->tm_hour += 12; buf += len; break; } - return (0); + return (NULL); case 'A': case 'a': @@ -292,7 +293,7 @@ break; } if (i == asizeof(tptr->weekday)) - return (0); + return (NULL); tm->tm_wday = i; buf += len; @@ -306,39 +307,35 @@ * point to calculate a real value, so just check the * range for now. */ - if (!isdigit((unsigned char)*buf)) - return (0); + if (!isdigit(*buf)) + return (NULL); len = 2; - for (i = 0; - len && isdigit((unsigned char)*buf); - buf++) { + for (i = 0; len && isdigit(*buf); buf++) { i *= 10; i += *buf - '0'; len--; } if (i > 53) - return (0); + return (NULL); - if (*buf != 0 && isspace((unsigned char)*buf)) - while (*ptr != 0 && - !isspace((unsigned char)*ptr)) + if (isspace(*buf)) + while (*ptr != 0 && !isspace(*ptr)) ptr++; break; case 'w': - if (!isdigit((unsigned char)*buf)) - return (0); + if (!isdigit(*buf)) + return (NULL); i = *buf - '0'; if (i > 6) - return (0); + return (NULL); tm->tm_wday = i; - if (*buf != 0 && isspace((unsigned char)*buf)) - while (*ptr != 0 && - !isspace((unsigned char)*ptr)) + if (isspace(*buf)) + while (*ptr != 0 && !isspace(*ptr)) ptr++; break; @@ -352,25 +349,22 @@ * XXX The %e specifier may gobble one too many * digits if used incorrectly. */ - if (!isdigit((unsigned char)*buf)) - return (0); + if (!isdigit(*buf)) + return (NULL); len = 2; - for (i = 0; - len && isdigit((unsigned char)*buf); - buf++) { + for (i = 0; len && isdigit(*buf); buf++) { i *= 10; i += *buf - '0'; len--; } if (i > 31) - return (0); + return (NULL); tm->tm_mday = i; - if (*buf != 0 && isspace((unsigned char)*buf)) - while (*ptr != 0 && - !isspace((unsigned char)*ptr)) + if (isspace(*buf)) + while (*ptr != 0 && !isspace(*ptr)) ptr++; break; @@ -395,47 +389,62 @@ } } if (i == asizeof(tptr->month)) - return (0); + return (NULL); tm->tm_mon = i; buf += len; break; case 'm': - if (!isdigit((unsigned char)*buf)) - return (0); + if (!isdigit(*buf)) + return (NULL); len = 2; - for (i = 0; - len && isdigit((unsigned char)*buf); - buf++) { + for (i = 0; len && isdigit(*buf); buf++) { i *= 10; i += *buf - '0'; len--; } if (i < 1 || i > 12) - return (0); + return (NULL); tm->tm_mon = i - 1; - if (*buf != 0 && isspace((unsigned char)*buf)) - while (*ptr != 0 && - !isspace((unsigned char)*ptr)) + if (isspace(*buf)) + while (*ptr != NULL && !isspace(*ptr)) ptr++; break; + case 's': + { + char *cp; + int sverrno; + time_t t; + + sverrno = errno; + errno = 0; + t = strtol(buf, &cp, 10); + if (errno == ERANGE) { + errno = sverrno; + return (NULL); + } + errno = sverrno; + buf = cp; + (void) gmtime_r(&t, tm); + *flagsp |= F_GMT; + } + break; + case 'Y': case 'y': - if (*buf == 0 || isspace((unsigned char)*buf)) + if (*buf == NULL || isspace(*buf)) break; - if (!isdigit((unsigned char)*buf)) - return (0); + if (!isdigit(*buf)) + return (NULL); len = (c == 'Y') ? 4 : 2; - for (i = 0; - len && isdigit((unsigned char)*buf); - buf++) { + for (i = 0; len && isdigit(*buf); buf++) { i *= 10; i += *buf - '0'; len--; @@ -445,13 +454,12 @@ if (c == 'y' && i < 69) i += 100; if (i < 0) - return (0); + return (NULL); tm->tm_year = i; - if (*buf != 0 && isspace((unsigned char)*buf)) - while (*ptr != 0 && - !isspace((unsigned char)*ptr)) + if (isspace(*buf)) + while (*ptr != 0 && !isspace(*ptr)) ptr++; break; @@ -460,48 +468,71 @@ const char *cp = buf; char *zonestr; - while (isupper((unsigned char)*cp)) + while (isupper(*cp)) ++cp; if (cp - buf) { zonestr = alloca(cp - buf + 1); (void) strncpy(zonestr, buf, cp - buf); zonestr[cp - buf] = '\0'; tzset(); - /* - * Once upon a time this supported "GMT", - * for GMT, but we removed this as Solaris - * doesn't have it, and we lack the needed - * timegm function. - */ - if (0 == strcmp(zonestr, tzname[0])) { + if (strcmp(zonestr, "GMT") == 0) { + *flagsp |= F_GMT; + } else if (0 == strcmp(zonestr, tzname[0])) { tm->tm_isdst = 0; } else if (0 == strcmp(zonestr, tzname[1])) { tm->tm_isdst = 1; } else { - return (0); + return (NULL); } buf += cp - buf; } } break; - /* - * Note that there used to be support %z and %s, but these - * are not supported by Solaris, so we have removed them. - * They would have required timegm() which is missing. - */ + case 'z': + { + int sign = 1; + + if (*buf != '+') { + if (*buf == '-') + sign = -1; + else + return (NULL); + } + buf++; + i = 0; + for (len = 4; len > 0; len--) { + if (!isdigit(*buf)) + return (NULL); + i *= 10; + i += *buf - '0'; + buf++; + } + + tm->tm_hour -= sign * (i / 100); + tm->tm_min -= sign * (i % 100); + *flagsp |= F_GMT; + } + break; } } + + if (!recurse) { + if (buf && (*flagsp & F_GMT)) { + time_t t = timegm(tm); + (void) localtime_r(&t, tm); + } + } + return ((char *)buf); } char * strptime(const char *buf, const char *fmt, struct tm *tm) { - /* Legacy Solaris strptime clears the incoming tm structure. */ - (void) memset(tm, 0, sizeof (*tm)); + int flags = F_ZERO; - return (__strptime(buf, fmt, tm)); + return (__strptime(buf, fmt, tm, &flags)); } /* @@ -511,5 +542,7 @@ char * __strptime_dontzero(const char *buf, const char *fmt, struct tm *tm) { - return (__strptime(buf, fmt, tm)); + int flags = 0; + + return (__strptime(buf, fmt, tm, &flags)); }
--- a/usr/src/lib/libc/port/mapfile-vers Fri Dec 03 07:32:02 2010 -0800 +++ b/usr/src/lib/libc/port/mapfile-vers Sat Dec 04 17:33:07 2010 -0800 @@ -88,6 +88,11 @@ $add amd64 $endif +SYMBOL_VERSION ILLUMOS_0.1 { # Illumos additions + protected: + timegm; +} SUNW_1.23; + SYMBOL_VERSION SUNW_1.23 { # SunOS 5.11 (Solaris 11) global: _nl_domain_bindings;