diff --git a/src/cmd/ksh93/tests/attributes.sh b/src/cmd/ksh93/tests/attributes.sh index eeb05a1106e8..60f773e42e3c 100755 --- a/src/cmd/ksh93/tests/attributes.sh +++ b/src/cmd/ksh93/tests/attributes.sh @@ -362,9 +362,9 @@ $SHELL -c 'builtin date' >/dev/null 2>&1 && # check env var changes against a builtin that uses the env var SEC=1234252800 -ETZ=EST5EDT +ETZ=EST5EDT,M3.2.0/2:00:00,M11.1.0/2:00:00 EDT=03 -PTZ=PST8PDT +PTZ=PST8PDT,M3.2.0/2:00:00,M11.1.0/2:00:00 PDT=00 CMD="date -f%H \\#$SEC" diff --git a/src/cmd/ksh93/tests/printf.sh b/src/cmd/ksh93/tests/printf.sh index cd4ce9fb40bd..40b46bc99ca9 100755 --- a/src/cmd/ksh93/tests/printf.sh +++ b/src/cmd/ksh93/tests/printf.sh @@ -211,14 +211,62 @@ function do_test # 1:LINENO 2:printf-STRING 3:match-string } # The first tests require a time zone with one or more historical changes. -format='%Y-%m-%d %H:%M:%S' -export TZ=Europe/Riga - C='Historical changes (bad time)' # https://github.com/ksh93/ksh/issues/669 +export TZ=Europe/Riga +format='%Y-%m-%d %H:%M:%S' T '#236961303' '1977-07-05 17:35:03' +format='%s' +T '1977-07-05 17:35:03' '236961303' export TZ=Europe/London +format='%Y-%m-%d %H:%M:%S' T '#0' '1970-01-01 01:00:00' +format='%s' +T '1970-01-01 01:00:00' '0' + +C='Before and after a historical change (bad time)' +export TZ=Pacific/Apia +format='%Y-%m-%d %H:%M:%S' +T '#1325239200' '2011-12-31 00:00:00' +T '#1325239199' '2011-12-29 23:59:59' +format='%s' +T '2011-12-31 00:00:00' '1325239200' +T '2011-12-29 23:59:59' '1325239199' + +C='Historical change from one DST zone to another (bad time)' +export TZ=Europe/Chisinau +format='%Y-%m-%d %H:%M:%S' +T '#642286800' '1990-05-10 00:00:00' +format='%s' +T '1990-05-10 00:00:00' '642286800' + +C='Historical change from DST to standard (bad time)' +export TZ=America/Indiana/Knox +format='%Y-%m-%d %H:%M:%S' +T '#688626000' '1991-10-28 00:00:00' +format='%s' +T '1991-10-28 00:00:00' '688626000' + +C='Historical change from standard to DST (bad time)' +export TZ=America/Kentucky/Louisville +format='%Y-%m-%d %H:%M:%S' +T '#126766800' '1974-01-07 00:00:00' +format='%s' +T '1974-01-07 00:00:00' '126766800' + +C='Historical change from standard to standard (bad time)' +export TZ=America/Argentina/San_Juan +format='%Y-%m-%d %H:%M:%S' +T '#1086148800' '2004-06-02 00:00:00' +format='%s' +T '2004-06-02 00:00:00' '1086148800' + +C='POSIX timezone strings without rules (bad time, musl)' #https://github.com/ksh93/ksh/issues/976 +export TZ=EST5EDT +format='%Y-%m-%d %H:%M:%S' +T '#1274252800' '2010-05-19 03:06:40' +format='%s' +T '2010-05-19 03:06:40' '1274252800' format='%Y-%m-%d' export TZ=UTC diff --git a/src/lib/libast/tm/tminit.c b/src/lib/libast/tm/tminit.c index d70ba3c1ee45..9e0dabf6e5ec 100644 --- a/src/lib/libast/tm/tminit.c +++ b/src/lib/libast/tm/tminit.c @@ -196,6 +196,8 @@ tmlocal(time_t now) char* e = NULL; int i; int m; + int o; + int p; int isdst; char* t; struct tm* tp; @@ -237,27 +239,38 @@ tmlocal(time_t now) tm_info.zone = tm_info.local = &local; n = tzwest(&now, &isdst); + p = n; + o = isdst; /* * compute local DST offset by roaming * through the last 12 months until tzwest() changes + * with a system-recognized DST change */ for (i = 0; i < 12; i++) { now -= 31 * 24 * 60 * 60; - if ((m = tzwest(&now, &isdst)) != n) + if ((m = tzwest(&now, &isdst)) != n && ((!isdst && o) || (isdst && !o))) { + m -= (n - p); + n = p; if (!isdst) { - isdst = n; n = m; - m = isdst; + m = p; } - m -= n; break; } + n = m; + if (i == 11) + { + n = p; + m = n; + } } + m -= n; + isdst = o; local.west = (short)n; local.dst = (short)m; diff --git a/src/lib/libast/tm/tmxdate.c b/src/lib/libast/tm/tmxdate.c index 49ef24ef405a..4132839889cb 100644 --- a/src/lib/libast/tm/tmxdate.c +++ b/src/lib/libast/tm/tmxdate.c @@ -172,6 +172,11 @@ tmxdate(const char* s, char** e, Time_t now) int dir; int dst; int zone; + int dst_old; + int west_old; + int isdst_old; + int first = 1; + int good_check = 0; int c; int f; int i; @@ -206,7 +211,13 @@ tmxdate(const char* s, char** e, Time_t now) */ tm = tmxtm(&ts, now, NULL, 1); + if (!first && dst_old == tm->tm_zone->dst && west_old == tm->tm_zone->west && isdst_old == tm->tm_isdst) + good_check = 1; + dst_old = tm->tm_zone->dst; + west_old = tm->tm_zone->west; + isdst_old = tm->tm_isdst; tm_info.date = tm->tm_zone; + first = 0; day = -1; dir = 0; dst = TM_DST; @@ -1779,6 +1790,15 @@ tmxdate(const char* s, char** e, Time_t now) tm->tm_mday += j; } } + tm = tmxtm(tm, tmxtime(tm, zone), tm->tm_zone, 1); + if (!first) + { + int offset = tm->tm_zone->west - west_old; + if (tm->tm_isdst) + offset += tm->tm_zone->dst - dst_old; + tm->tm_min += offset; + tmfix(tm); + } now = tmxtime(tm, zone); if (tm->tm_year <= 70 && tmxsec(now) > 31536000) { @@ -1787,5 +1807,7 @@ tmxdate(const char* s, char** e, Time_t now) } if (e) *e = last; + if (!good_check) + goto reset; return now; }