lib/60452: gmtime, gmtime_r put wrong time zone into result
Thomas Klausner <[email protected]>
| Newsgroups | gmane.os.netbsd.bugs |
|---|---|
| Message-ID | <[email protected]> |
This is now PR 60452, but I hadn't set the Category correctly, so it wasn't sent to the mailing list. Thomas ----- Forwarded message from [email protected] ----- >Submitter-Id: net >Originator: Thomas Klausner >Organization: >Confidential: no >Synopsis: gmtime, gmtime_r put TZ into struct tm >Severity: serious >Priority: medium >Category: >Class: sw-bug >Release: NetBSD 11.99.7/x86_64 >Environment: Architecture: x86_64 Machine: amd64 >Description: gmtime() and gmtime_r() are documented to return Coordinated Universal Time (UTC), but they put the time zone offset of the current timezone (from the system, or from the TZ environment variable) into the resulting struct tm, while the actual time value _is_ UTC. I found this while debugging a jq test failure. >How-To-Repeat: Save this to jq.c (based on the actual code in jq, but heavily simplified): --- begin --- #include <err.h> #include <stdio.h> #include <string.h> #include <time.h> void f_gmtime(time_t secs, struct tm *tmp) { memset(tmp, 0, sizeof(*tmp)); if (gmtime_r(&secs, tmp) == NULL) err(1, "gmtime_r failed"); } char *f_strftime(time_t secs) { char buf[1000]; struct tm tm; memset(&tm, 0, sizeof(struct tm)); f_gmtime(secs, &tm); // struct tm *tmp; // tmp = gmtime(&secs); const char *fmt = "%FT%T%Z"; size_t n = strftime(buf, sizeof(buf), fmt, &tm); // size_t n = strftime(buf, sizeof(buf), fmt, tmp); /* POSIX doesn't provide errno values for strftime() failures; weird */ if ((n == 0) || n > sizeof(buf)) err(1, "strftime failed"); return strdup(buf); } void f_localtime(time_t secs, struct tm *tmp) { memset(tmp, 0, sizeof(*tmp)); if (localtime_r(&secs, tmp) == NULL) err(1, "localtime_r failed"); } char *f_strflocaltime(time_t secs) { struct tm tm; char buf[1000]; f_localtime(secs, &tm); const char *fmt = "%FT%T%z"; size_t n = strftime(buf, sizeof(buf), fmt, &tm); /* POSIX doesn't provide errno values for strftime() failures; weird */ if ((n == 0) || n > sizeof(buf)) err(1, "strftime failed"); return strdup(buf); } int main(int argc, char *argv[]) { uint64_t epoch_secs = 1731627341; printf("%s\n", f_strftime(epoch_secs)); printf("%s\n", f_strflocaltime(epoch_secs)); } --- end --- and run this script: --- begin --- #!/bin/sh make jq ./jq TZ=Etc/GMT+7 ./jq --- end --- On my system this reports: --- begin --- 2024-11-14T23:35:41CET 2024-11-15T00:35:41+0100 2024-11-14T23:35:41-07 2024-11-14T16:35:41-0700 --- end --- The second and fourth lines are the correct result. The first and third lines are wrong. They report the (correct) time in UTC, but the reported time zones are CET (my system default) or -7 (from TZ) respectively. >Fix: Yes, please. ----- End forwarded message -----