Bug 2110357
| Summary: | glibc: mktime() fails with -EOVERFLOW when tm_isdst=1 and a neighboring DST boundary is far from tm_year | ||
|---|---|---|---|
| Product: | Red Hat Enterprise Linux 9 | Reporter: | Takayuki Nagata <tnagata> |
| Component: | glibc | Assignee: | Arjun Shankar <ashankar> |
| Status: | CLOSED ERRATA | QA Contact: | Sergey Kolosov <skolosov> |
| Severity: | medium | Docs Contact: | |
| Priority: | unspecified | ||
| Version: | 9.0 | CC: | ashankar, codonell, dj, fweimer, markobri, mcermak, mnewsome, pfrankli, sipoyare, skolosov |
| Target Milestone: | rc | Keywords: | Bugfix, Triaged |
| Target Release: | --- | Flags: | pm-rhel:
mirror+
|
| Hardware: | Unspecified | ||
| OS: | Linux | ||
| Whiteboard: | |||
| Fixed In Version: | glibc-2.34-49.el9 | Doc Type: | No Doc Update |
| Doc Text: | Story Points: | --- | |
| Clone Of: | Environment: | ||
| Last Closed: | 2023-05-09 08:16:01 UTC | Type: | Bug |
| Regression: | --- | Mount Type: | --- |
| Documentation: | --- | CRM: | |
| Verified Versions: | Category: | --- | |
| oVirt Team: | --- | RHEL 7.3 requirements from Atomic Host: | |
| Cloudforms Team: | --- | Target Upstream Version: | |
| Embargoed: | |||
| Bug Depends On: | 2141728 | ||
| Bug Blocks: | |||
Does the description of the bug need to be private? Do you know if according to the data, 2023-01-01 is supposed to have DST active? Thanks. (In reply to Florian Weimer from comment #1) > Does the description of the bug need to be private? The description does not contain customer/sensitive data, and can be public. > Do you know if according to the data, 2023-01-01 is supposed to have DST > active? Thanks. It appears to me that 2023-01-01 does not have DST in Asia/Tokyo. (RHEL9: tzdata-2022a-1.el9_0) # zdump -i /usr/share/zoneinfo/Asia/Tokyo TZ="/usr/share/zoneinfo/Asia/Tokyo" - - +091859 LMT 1888-01-01 00 +09 JST 1948-05-02 01 +10 JDT 1 1948-09-12 00 +09 JST 1949-04-03 01 +10 JDT 1 1949-09-11 00 +09 JST 1950-05-07 01 +10 JDT 1 1950-09-10 00 +09 JST 1951-05-06 01 +10 JDT 1 1951-09-09 00 +09 JST (RHEL8: tzdata-2020a-1.el8) # zdump -i /usr/share/zoneinfo/Asia/Tokyo TZ="/usr/share/zoneinfo/Asia/Tokyo" - - +091859 LMT 1888-01-01 00 +09 JST 1948-05-02 01 +10 JDT 1 1948-09-12 00 +09 JST 1949-04-03 01 +10 JDT 1 1949-09-11 00 +09 JST 1950-05-07 01 +10 JDT 1 1950-09-10 00 +09 JST 1951-05-06 01 +10 JDT 1 1951-09-09 00 +09 JST However, If TZ=America/New_York is set, mktime() returns a time value for 2023-01-01 with DST as follows even if it does not have DST. mktime(&tm) = 1503825856 # zdump -i -c 2020,2025 /usr/share/zoneinfo/America/New_York TZ="/usr/share/zoneinfo/America/New_York" - - -05 EST 2020-03-08 03 -04 EDT 1 2020-11-01 01 -05 EST 2021-03-14 03 -04 EDT 1 2021-11-07 01 -05 EST 2022-03-13 03 -04 EDT 1 2022-11-06 01 -05 EST 2023-03-12 03 -04 EDT 1 2023-11-05 01 -05 EST 2024-03-10 03 -04 EDT 1 2024-11-03 01 -05 EST In addition, mktime() for "1951-08-01" with DST in Asia/Tokyo also fails on RHEL9. mktime(&tm) = -1 Value too large for defined data type Takayuki Nagata 1951-08-01 works for me:
#include <time.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void main(void)
{
time_t t;
struct tm tm;
setenv("TZ", "Asia/Tokyo", 1);
memset(&tm, 0, sizeof(tm));
tm.tm_mday = 1;
tm.tm_mon = 7;
tm.tm_year = 51;
tm.tm_isdst = 1;
t = mktime(&tm);
printf("mktime(&tm) = %d\n", t);
if (t < 0)
perror(NULL);
}
mktime(&tm) = -581335200
Success
The test case from comment 0 actually tests 3923-02-01, not 2023-01-01. I had forgotten about the offsets from tm_mon and tm_year.
The current implementation treats tm_isdst > 0 as a strong indicator that only time information with the DST flag is valid as a result, and EOVERFLOW is used as an indicator that the search was unsuccessful (presumably because that's the only error code specified by POSIX).
(In reply to Florian Weimer from comment #3) > 1951-08-01 works for me: > <snip> > > The test case from comment 0 actually tests 3923-02-01, not 2023-01-01. I > had forgotten about the offsets from tm_mon and tm_year. I had also forgotten about the offsets... Thanks, it also works for me with 1951-08-01. > The current implementation treats tm_isdst > 0 as a strong indicator that > only time information with the DST flag is valid as a result, and EOVERFLOW > is used as an indicator that the search was unsuccessful (presumably because > that's the only error code specified by POSIX). However, 2023-01-01 with tm_isdst = 1 in America/New_york is successful. It does not appear to me that DST is valid from the results of zdump. #include <time.h> #include <stdio.h> #include <string.h> #include <stdlib.h> void main(void) { time_t t; struct tm tm; setenv("TZ", "America/New_York", 1); memset(&tm, 0, sizeof(tm)); tm.tm_mday = 1; tm.tm_mon = 1 - 1; tm.tm_year = 2023 - 1900; tm.tm_isdst = 1; t = mktime(&tm); printf("mktime(&tm) = %d\n", t); if (t < 0) perror(NULL); } mktime(&tm) = 1672545600 It appears to me that -EOVERFLOW errors depend on the following commit. https://sourceware.org/git/?p=glibc.git;a=commit;h=86aece3bfbd44538ba4fdc947872c81d4c5e6e61 In addition, it appears to me that the issue where 2023-01-01 and tm_isdst=1 fails in Asia/Tokyo but succeeds in America/New_York depend on the following duration_max. mktime.c ~~~ 444 /* The longest period of DST in tzdata2003a is 536454000 seconds 445 (e.g., America/Jujuy starting 1946-10-01 01:00). The longest 446 period of non-DST is much longer, but it makes no real sense 447 to search for more than a year of non-DST, so use the DST 448 max. */ 449 int duration_max = 536454000; ~~~ So I think that the duration_max is not enough for Asia/Tokyo in this case. (In reply to Takayuki Nagata from comment #5) > It appears to me that -EOVERFLOW errors depend on the following commit. > > > https://sourceware.org/git/?p=glibc.git;a=commit; > h=86aece3bfbd44538ba4fdc947872c81d4c5e6e61 > > In addition, it appears to me that the issue where 2023-01-01 and tm_isdst=1 > fails in Asia/Tokyo but succeeds in America/New_York depend on the following > duration_max. > > mktime.c > ~~~ > 444 /* The longest period of DST in tzdata2003a is 536454000 seconds > 445 (e.g., America/Jujuy starting 1946-10-01 01:00). The longest > 446 period of non-DST is much longer, but it makes no real sense > 447 to search for more than a year of non-DST, so use the DST > 448 max. */ > 449 int duration_max = 536454000; > ~~~ > > So I think that the duration_max is not enough for Asia/Tokyo in this case. Thanks for digging into this. The search algorithm really isn't great. This has to be fixed upstream. It's not going to be an easy fix. How critical is this to the customer? The answer will help us to prioritize this work. (In reply to Takayuki Nagata from comment #5) > In addition, it appears to me that the issue where 2023-01-01 and tm_isdst=1 > fails in Asia/Tokyo but succeeds in America/New_York depend on the following > duration_max. It does indeed depend on duration_max. I have a test build I was experimenting with here I expand the search heuristic, but this slows down the API in this corner case and still doesn't return the exact result you had in RHEL8 (some of the semantics of the search and forward calculation of the time value have changed slightly). I think the root cause of the problem is that you set tm_isdst to 1. Setting it to 1 tells the algorithm "I know DST is active." And the algorithm tries its best to convert your broken down time while complying with the requirement you provided. It would be better if you set tm_isdst to -1 as is specified in C99 and POSIX and future standards, which allows mktime to tell you what the DST value was that it found, rather than to use it as a requirement. https://pubs.opengroup.org/onlinepubs/9699919799/functions/mktime.html ~~~ A negative value for tm_isdst shall cause mktime() to attempt to determine whether Daylight Savings Time is in effect for the specified time. ~~~ ISO C99 (N1256) ~~~ page 340, footnote 256) Thus, a positive or zero value for tm_isdst causes the mktime function to presume initially that Daylight Saving Time, respectively, is or is not in effect for the specified time. A negative value causes it to attempt to determine whether Daylight Saving Time is in effect for the specified time. ~~~ Is setting tm_isdst to -1 a possible workaround for the customer? ~~~ setenv("TZ", "Asia/Tokyo", 1); memset(&tm, 0, sizeof(tm)); tm.tm_mday = 1; tm.tm_mon = 1 - 1; tm.tm_year = 2023 - 1900; tm.tm_isdst = -1; t = mktime(&tm); printf("mktime(&tm) = %ld\n", (long int)t); if (t < 0) perror(NULL); ~~~ Example RHEL7: mktime(&tm) = 1672498800 Example RHEL8: mktime(&tm) = 1672498800 Example RHEL9: mktime(&tm) = 1672498800 Setting tm_isdst to -1 should result in the same values as before and avoid the EOVERFLOW scenario. I have put a repo that includes the proposed upstream patch for this bug here: https://people.redhat.com/dj/bz2110357/glibc-2.34-40.0.0.testfix.1.bz2110357.el9-scratch.repo With this patch, mktime() in zones that don't have a findable DST will assume a one hour DST offset instead of returning -1 Quality Engineering Management has reviewed and declined this request. You may appeal this decision by reopening this request. The flip to CLOSED/WONTFIX was not intentional. It was a consequence of a mis-click. Apologies. Let me try to fix the bug status now.https://bugzilla.redhat.com/show_bug.cgi?id=2110357 I will sync from ustream release/2.34/master via bug 2141728 to get this fix in. Since the problem described in this bug report should be resolved in a recent advisory, it has been closed with a resolution of ERRATA. For information on the advisory (glibc bug fix and enhancement update), and where to find the updated files, follow the link below. If the solution does not work for you, open a new bug report. https://access.redhat.com/errata/RHBA-2023:2481 |
Description of problem: Since RHEL9, the following mktime() fails with -EOVERFLOW. setenv("TZ", "Asia/Tokyo", 1); tm.tm_mday = 1; tm.tm_mon = 1; tm.tm_year = 2023; tm.tm_isdst = 1; mktime(&tm); On previous versions (RHEL8 and RHEL7), it returns 1503779056. If the tm_isdst is 0, it returns 1503779056 on RHEL9. Version-Release number of selected component (if applicable): glibc-2.34-28.el9_0 How reproducible: Always Steps to Reproduce: 1. Run the following program. ~~~ #include <time.h> #include <stdio.h> #include <string.h> #include <stdlib.h> void main(void) { time_t t; struct tm tm; setenv("TZ", "Asia/Tokyo", 1); memset(&tm, 0, sizeof(tm)); tm.tm_mday = 1; tm.tm_mon = 1; tm.tm_year = 2023; tm.tm_isdst = 1; t = mktime(&tm); printf("mktime(&tm) = %d\n", t); if (t < 0) perror(NULL); } ~~~ 2. 3. Actual results: mktime(&tm) = -1 Value too large for defined data type Expected results: mktime(&tm) = 1503779056 Additional info: