From 0cffbd5d808dd2c4fa6f8a24cb90e8a99c6ebb8d Mon Sep 17 00:00:00 2001 From: Gene Carlson Date: Thu, 16 Jul 2026 15:23:04 +0900 Subject: [PATCH 1/9] tm/tminit.c: Fix local.dst handling. --- src/cmd/ksh93/tests/attributes.sh | 4 ++-- src/cmd/ksh93/tests/printf.sh | 13 +++++++++++++ src/lib/libast/tm/tminit.c | 8 ++++++-- 3 files changed, 21 insertions(+), 4 deletions(-) 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..256f84d1d338 100755 --- a/src/cmd/ksh93/tests/printf.sh +++ b/src/cmd/ksh93/tests/printf.sh @@ -220,6 +220,19 @@ T '#236961303' '1977-07-05 17:35:03' export TZ=Europe/London T '#0' '1970-01-01 01:00:00' +C='Proper offset within one year of a zone change (bad time)' +export TZ=America/Indiana/Knox +T '#1150126200' '2006-06-12 10:30:00' + +C='Before and after a zone change (bad time)' +export TZ=Pacific/Apia +T '#1325239200' '2011-12-31 00:00:00' +T '#1325239199' '2011-12-29 23:59:59' + +C='POSIX timezone strings without rules (bad time, musl)' #https://github.com/ksh93/ksh/issues/976 +export TZ=EST5EDT +T '#1274252800' '2010-05-19 03:06:40' + 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..31e976582268 100644 --- a/src/lib/libast/tm/tminit.c +++ b/src/lib/libast/tm/tminit.c @@ -196,6 +196,7 @@ tmlocal(time_t now) char* e = NULL; int i; int m; + int o; int isdst; char* t; struct tm* tp; @@ -237,16 +238,18 @@ tmlocal(time_t now) tm_info.zone = tm_info.local = &local; n = tzwest(&now, &isdst); + o = isdst; /* * compute local DST offset by roaming * through the last 12 months until tzwest() changes + * with a system-recofgnized 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))) { if (!isdst) { @@ -254,10 +257,11 @@ tmlocal(time_t now) n = m; m = isdst; } - m -= n; break; } } + m -= n; + isdst = o; local.west = (short)n; local.dst = (short)m; From b3c6637fd63a0cb91e765ba326fbc3d961882298 Mon Sep 17 00:00:00 2001 From: Gene Carlson Date: Thu, 16 Jul 2026 23:00:36 +0900 Subject: [PATCH 2/9] tm/tminit.c: Fix historical DST-DST zone changes. --- src/cmd/ksh93/tests/printf.sh | 18 +++++++++++++----- src/lib/libast/tm/tminit.c | 5 +++++ 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/cmd/ksh93/tests/printf.sh b/src/cmd/ksh93/tests/printf.sh index 256f84d1d338..600f079e9a3a 100755 --- a/src/cmd/ksh93/tests/printf.sh +++ b/src/cmd/ksh93/tests/printf.sh @@ -220,15 +220,23 @@ T '#236961303' '1977-07-05 17:35:03' export TZ=Europe/London T '#0' '1970-01-01 01:00:00' -C='Proper offset within one year of a zone change (bad time)' -export TZ=America/Indiana/Knox -T '#1150126200' '2006-06-12 10:30:00' - -C='Before and after a zone change (bad time)' +C='Before and after a historical change (bad time)' export TZ=Pacific/Apia T '#1325239200' '2011-12-31 00:00:00' T '#1325239199' '2011-12-29 23:59:59' +C='Historical change from one DST zone to another (bad time)' +export TZ=Europe/Chisinau +T '#642286800' '1990-05-10 00:00:00' + +C='Historical change from DST to standard (bad time)' +export TZ=America/Indiana/Knox +T '#688626000' '1991-10-28 00:00:00' + +C='Historical change from standard to DST (bad time)' +export TZ=America/Kentucky/Louisville +T '#126766800' '1974-01-07 00:00:00' + C='POSIX timezone strings without rules (bad time, musl)' #https://github.com/ksh93/ksh/issues/976 export TZ=EST5EDT T '#1274252800' '2010-05-19 03:06:40' diff --git a/src/lib/libast/tm/tminit.c b/src/lib/libast/tm/tminit.c index 31e976582268..22f7226ffce2 100644 --- a/src/lib/libast/tm/tminit.c +++ b/src/lib/libast/tm/tminit.c @@ -197,6 +197,7 @@ tmlocal(time_t now) int i; int m; int o; + int p; int isdst; char* t; struct tm* tp; @@ -238,6 +239,7 @@ tmlocal(time_t now) tm_info.zone = tm_info.local = &local; n = tzwest(&now, &isdst); + p = n; o = isdst; /* @@ -251,6 +253,7 @@ tmlocal(time_t now) now -= 31 * 24 * 60 * 60; if ((m = tzwest(&now, &isdst)) != n && ((!isdst && o) || (isdst && !o))) { + n = p; if (!isdst) { isdst = n; @@ -259,6 +262,8 @@ tmlocal(time_t now) } break; } + else if (m != n) + n = m; } m -= n; isdst = o; From 6f2823f9a38fa5622484fe8df6857d245d402800 Mon Sep 17 00:00:00 2001 From: Gene Carlson Date: Fri, 17 Jul 2026 00:23:58 +0900 Subject: [PATCH 3/9] tm/tminit.c: Historical zone changes, no DST in previous year. --- src/cmd/ksh93/tests/printf.sh | 4 ++++ src/lib/libast/tm/tminit.c | 2 ++ 2 files changed, 6 insertions(+) diff --git a/src/cmd/ksh93/tests/printf.sh b/src/cmd/ksh93/tests/printf.sh index 600f079e9a3a..75a22535ff0e 100755 --- a/src/cmd/ksh93/tests/printf.sh +++ b/src/cmd/ksh93/tests/printf.sh @@ -237,6 +237,10 @@ C='Historical change from standard to DST (bad time)' export TZ=America/Kentucky/Louisville T '#126766800' '1974-01-07 00:00:00' +C='Historical change from standard to standard (bad time)' +export TZ=America/Argentina/San_Juan +T '#1086148800' '2004-06-02 00:00:00' + C='POSIX timezone strings without rules (bad time, musl)' #https://github.com/ksh93/ksh/issues/976 export TZ=EST5EDT T '#1274252800' '2010-05-19 03:06:40' diff --git a/src/lib/libast/tm/tminit.c b/src/lib/libast/tm/tminit.c index 22f7226ffce2..f3e5d9069935 100644 --- a/src/lib/libast/tm/tminit.c +++ b/src/lib/libast/tm/tminit.c @@ -264,6 +264,8 @@ tmlocal(time_t now) } else if (m != n) n = m; + if (i == 11) + n = p; } m -= n; isdst = o; From 537dc03011e09b365948eca7b222c829f7ad2ed4 Mon Sep 17 00:00:00 2001 From: Gene Carlson Date: Fri, 17 Jul 2026 00:27:28 +0900 Subject: [PATCH 4/9] tm/tminit.c: Typo; unneeded DST offset from standard. --- src/lib/libast/tm/tminit.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib/libast/tm/tminit.c b/src/lib/libast/tm/tminit.c index f3e5d9069935..c309a2d01deb 100644 --- a/src/lib/libast/tm/tminit.c +++ b/src/lib/libast/tm/tminit.c @@ -245,7 +245,7 @@ tmlocal(time_t now) /* * compute local DST offset by roaming * through the last 12 months until tzwest() changes - * with a system-recofgnized DST change + * with a system-recognized DST change */ for (i = 0; i < 12; i++) @@ -265,7 +265,10 @@ tmlocal(time_t now) else if (m != n) n = m; if (i == 11) + { n = p; + m = n; + } } m -= n; isdst = o; From e974a27c487a6babb7675be7c8c4546705ec10f1 Mon Sep 17 00:00:00 2001 From: Gene Carlson Date: Fri, 17 Jul 2026 07:55:01 +0900 Subject: [PATCH 5/9] tm/tminit.c: Replace confusing use of variable. --- src/lib/libast/tm/tminit.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib/libast/tm/tminit.c b/src/lib/libast/tm/tminit.c index c309a2d01deb..56ebef9e5388 100644 --- a/src/lib/libast/tm/tminit.c +++ b/src/lib/libast/tm/tminit.c @@ -256,9 +256,8 @@ tmlocal(time_t now) n = p; if (!isdst) { - isdst = n; n = m; - m = isdst; + m = p; } break; } From cbf911df61720d3a5318f28d29a28cc3dd471896 Mon Sep 17 00:00:00 2001 From: Gene Carlson Date: Fri, 17 Jul 2026 08:04:55 +0900 Subject: [PATCH 6/9] tm/tminit.c: Remove useless conditional. --- src/lib/libast/tm/tminit.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib/libast/tm/tminit.c b/src/lib/libast/tm/tminit.c index 56ebef9e5388..c8db0de8a40f 100644 --- a/src/lib/libast/tm/tminit.c +++ b/src/lib/libast/tm/tminit.c @@ -261,8 +261,7 @@ tmlocal(time_t now) } break; } - else if (m != n) - n = m; + n = m; if (i == 11) { n = p; From b358a2b23fbe482d010790f099e84515651224ca Mon Sep 17 00:00:00 2001 From: Gene Carlson Date: Sat, 18 Jul 2026 09:32:50 +0900 Subject: [PATCH 7/9] tm/tminit.c: More consistent DST offset. --- src/lib/libast/tm/tminit.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/libast/tm/tminit.c b/src/lib/libast/tm/tminit.c index c8db0de8a40f..9e0dabf6e5ec 100644 --- a/src/lib/libast/tm/tminit.c +++ b/src/lib/libast/tm/tminit.c @@ -253,6 +253,7 @@ tmlocal(time_t now) now -= 31 * 24 * 60 * 60; if ((m = tzwest(&now, &isdst)) != n && ((!isdst && o) || (isdst && !o))) { + m -= (n - p); n = p; if (!isdst) { From 75379f836c05d23df4548b598ff4fd959d2d6e00 Mon Sep 17 00:00:00 2001 From: Gene Carlson Date: Sat, 18 Jul 2026 10:20:55 +0900 Subject: [PATCH 8/9] tests/printf.sh: Fix whitespace. --- src/cmd/ksh93/tests/printf.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cmd/ksh93/tests/printf.sh b/src/cmd/ksh93/tests/printf.sh index 75a22535ff0e..81096d7b0fe1 100755 --- a/src/cmd/ksh93/tests/printf.sh +++ b/src/cmd/ksh93/tests/printf.sh @@ -227,15 +227,15 @@ T '#1325239199' '2011-12-29 23:59:59' C='Historical change from one DST zone to another (bad time)' export TZ=Europe/Chisinau -T '#642286800' '1990-05-10 00:00:00' +T '#642286800' '1990-05-10 00:00:00' C='Historical change from DST to standard (bad time)' export TZ=America/Indiana/Knox -T '#688626000' '1991-10-28 00:00:00' +T '#688626000' '1991-10-28 00:00:00' C='Historical change from standard to DST (bad time)' export TZ=America/Kentucky/Louisville -T '#126766800' '1974-01-07 00:00:00' +T '#126766800' '1974-01-07 00:00:00' C='Historical change from standard to standard (bad time)' export TZ=America/Argentina/San_Juan From 8b003db531e300f383f699ce76de696a999c3580 Mon Sep 17 00:00:00 2001 From: Gene Carlson Date: Sat, 18 Jul 2026 18:01:42 +0900 Subject: [PATCH 9/9] tm/tmxdate.c: Fix non-epoch time lookup for zones with changes. --- src/cmd/ksh93/tests/printf.sh | 37 ++++++++++++++++++++++++++++------- src/lib/libast/tm/tmxdate.c | 22 +++++++++++++++++++++ 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/src/cmd/ksh93/tests/printf.sh b/src/cmd/ksh93/tests/printf.sh index 81096d7b0fe1..40b46bc99ca9 100755 --- a/src/cmd/ksh93/tests/printf.sh +++ b/src/cmd/ksh93/tests/printf.sh @@ -211,39 +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 -T '#1325239200' '2011-12-31 00:00:00' -T '#1325239199' '2011-12-29 23:59:59' +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 -T '#1086148800' '2004-06-02 00:00:00' +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 -T '#1274252800' '2010-05-19 03:06:40' +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/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; }