Thanks, I installed the attached more-ambitious patch into GNU Tar. On
platforms lacking POSIX.1-2024's tm_gmtoff and tm_zone, when TZ is
invalid it falls back on numeric timestamps rather than possibly
incorrectly reporting localtime results as if they were Universal Time.
Also, when falling back on numeric timestamps it reports nanosecond
precision. Finally, instead of using fprintf (or fprintftime) it uses
fwrite, so that partial writes are correctly counted.From 2bbc58bf0b5b54b77bb40b336760f62531f12748 Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email protected]>
Date: Sun, 23 Nov 2025 09:44:03 -0800
Subject: [PATCH] Support gnulib-style timestamps in checkpoint logs
* gnulib.modules: Add nstrftime-limited, time_rz. Sort.
* src/checkpoint.c: Include <strftime.h>.
(format_checkpoint_string): Use nstrftime instead of strftime.
Also fix an obscure bug on platforms that lack tm_gmtoff+tm_zone by
calling tzalloc on those platforms; if it fails, fall back on gmtime.
Also, use fwrite instead of fprintf, since we typically know the
length already and this gives us a more-accurate byte count
in case there are partial writes.
---
gnulib.modules | 10 ++++++----
src/checkpoint.c | 42 +++++++++++++++++++++++++++++++-----------
2 files changed, 37 insertions(+), 15 deletions(-)
diff --git a/gnulib.modules b/gnulib.modules
index 93c88934..fbfc251b 100644
--- a/gnulib.modules
+++ b/gnulib.modules
@@ -37,8 +37,8 @@ dup2
errno-h
error
exclude
-extern-inline
exitfail
+extern-inline
faccessat
fchmodat
fchownat
@@ -52,6 +52,7 @@ fnmatch-gnu
free-posix
fseeko
fstatat
+full-read
full-write
futimens
gendocs
@@ -81,6 +82,7 @@ mkdirat
mkdtemp
mkfifoat
modechange
+nstrftime-limited
obstack
openat
openat2
@@ -94,7 +96,6 @@ reallocarray
renameat
root-uid
rpmatch
-full-read
safe-read
same-inode
savedir
@@ -105,22 +106,23 @@ std-gnu23
stdcountof-h
stddef-h
stdint-h
-stpcpy
stdopen
+stpcpy
strdup-posix
strerror
stringeq
strnlen
symlinkat
sys_stat-h
+time_rz
timespec
timespec-sub
unlinkat
unlinkdir
unlocked-io
utimensat
-version-etc-fsf
verror
+version-etc-fsf
xalignalloc
xalloc
xalloc-die
diff --git a/src/checkpoint.c b/src/checkpoint.c
index 534e0d5b..9bd9fa05 100644
--- a/src/checkpoint.c
+++ b/src/checkpoint.c
@@ -22,6 +22,7 @@
#include <wordsplit.h>
#include <flexmember.h>
+#include <strftime.h>
#include <sys/ioctl.h>
#include <termios.h>
@@ -306,22 +307,41 @@ format_checkpoint_string (FILE *fp, intmax_t len,
{
struct timespec ts = current_timespec ();
struct tm *tm = localtime (&ts.tv_sec);
- char const *tmstr = NULL;
+#if HAVE_STRUCT_TM_TM_GMTOFF && HAVE_STRUCT_TM_TM_ZONE
+ /* struct tm has POSIX.1-2024 tm_gmtoff and tm_zone,
+ so nstrftime ignores tz and any tz value will do. */
+ timezone_t tz = 0;
+#else
+ static timezone_t tz;
+ if (tm && !tz)
+ {
+ tz = tzalloc (getenv ("TZ"));
+ if (!tz)
+ tm = NULL;
+ }
+#endif
/* Keep BUF relatively small, as any text timestamp
not fitting into BUF is likely a DoS attack. */
- char buf[max (SYSINT_BUFSIZE, 256)];
-
- if (tm)
+ char buf[max (TIMESPEC_STRSIZE_BOUND, 256)];
+ ptrdiff_t buflen =
+ (tm
+ ? nstrftime (buf, sizeof buf, arg ? arg : "%c",
+ tm, tz, ts.tv_nsec)
+ : -1);
+ char const *tmstr;
+ idx_t tmstrlen;
+ if (buflen < 0)
+ {
+ tmstr = code_timespec (ts, buf);
+ tmstrlen = strlen (tmstr);
+ }
+ else
{
- buf[0] = '\0';
- char const *fmt = arg ? arg : "%c";
- if (strftime (buf, sizeof buf, fmt, tm) != 0 || !buf[0])
- tmstr = buf;
+ tmstr = buf;
+ tmstrlen = buflen;
}
- if (!tmstr)
- tmstr = timetostr (ts.tv_sec, buf);
- len = add_printf (len, fprintf (fp, "%s", tmstr));
+ len = add_printf (len, fwrite (tmstr, 1, tmstrlen, stdout));
}
break;
--
2.51.0