diff --git a/NEWS b/NEWS
index 3b9bea25ad..0cd6153322 100644
--- a/NEWS
+++ b/NEWS
@@ -20,15 +20,12 @@ GNU coreutils NEWS -*- outline -*-
stat's %X, %Y, and %Z directives once again print only the integer
part of seconds since the epoch. This reverts a change from
coreutils-8.6, that was deemed unnecessarily disruptive. To obtain
- the nanoseconds portion corresponding to %X, you may now use %:X.
- I.e., to print the floating point number of seconds using maximum
- precision, use this format string: %X.%:X. Likewise for %Y, %Z and %W.
+ a full resolution time stamp for %X, use %.X; if you want (say) just
+ 3 fractional digits, use %.3X. Likewise for %Y and %Z.
stat's new %W format directive would print floating point seconds.
However, with the above change to %X, %Y and %Z, we've made %W work
- the same way: %W now expands to seconds since the epoch (or 0 when
- not supported), and %:W expands to the nanoseconds portion, or to
- 0 if not supported.
+ the same way as the others.
* Noteworthy changes in release 8.6 (2010-10-15) [stable]
diff --git a/bootstrap.conf b/bootstrap.conf
index 18ef914afc..5a6ebaed4e 100644
--- a/bootstrap.conf
+++ b/bootstrap.conf
@@ -85,6 +85,7 @@ gnulib_modules="
freopen
freopen-safer
fseeko
+ fstimeprec
fsusage
fsync
ftello
diff --git a/doc/coreutils.texi b/doc/coreutils.texi
index be5999ffec..8dfb069c0c 100644
--- a/doc/coreutils.texi
+++ b/doc/coreutils.texi
@@ -10707,37 +10707,36 @@ The valid @var{format} directives for files with @option{--format} and
@item %U - User name of owner
@item %w - Time of file birth, or @samp{-} if unknown
@item %W - Time of file birth as seconds since Epoch, or @samp{0}
-@item %:W - Time of file birth: nanoseconds remainder, or @samp{0}
@item %x - Time of last access
@item %X - Time of last access as seconds since Epoch
-@item %:X - Time of last access: nanoseconds remainder
@item %y - Time of last modification
@item %Y - Time of last modification as seconds since Epoch
-@item %:Y - Time of last modification: nanoseconds remainder
@item %z - Time of last change
@item %Z - Time of last change as seconds since Epoch
-@item %:Z - Time of last change: nanoseconds remainder
@end itemize
-Note that each of @samp{%:W}, @samp{%:X}, @samp{%:Y} and @samp{%:Z}
-prints its zero-padded number of nanoseconds on a field of width 9.
-However, if you specify anything between the @samp{%} and @samp{:},
-e.g., @samp{%10:X}, your modifier replaces the default of @samp{09}.
-
-Here are some examples:
+The @samp{%W}, @samp{%X}, @samp{%Y}, and @samp{%Z} formats accept a
+precision preceded by a period to specify the number of digits to
+print after the decimal point. For example, @samp{%.9X} outputs the
+last access time to nanosecond precision. If a period is given but no
+precision, @command{stat} uses the estimated precision of the file
+system. When discarding excess precision, time stamps are truncated
+toward minus infinity.
@example
zero pad:
- $ stat -c '[%015:Y]' /usr
- [000000031367045]
+ $ stat -c '[%015Y]' /usr
+ [000001288929712]
space align:
- $ stat -c '[%15:Y]' /usr
- [ 31367045]
- $ stat -c '[%-15:Y]' /usr
- [31367045 ]
-truncate:
- $ stat -c '[%.3:Y]' /usr
- [031]
+ $ stat -c '[%15Y]' /usr
+ [ 1288929712]
+ $ stat -c '[%-15Y]' /usr
+ [1288929712 ]
+precision:
+ $ stat -c '[%.3Y]' /usr
+ [1288929712.114]
+ $ stat -c '[%.Y]' /usr
+ [1288929712.114951]
@end example
The mount point printed by @samp{%m} is similar to that output
diff --git a/gl/lib/fstimeprec.c b/gl/lib/fstimeprec.c
new file mode 100644
index 0000000000..fd6305476b
--- /dev/null
+++ b/gl/lib/fstimeprec.c
@@ -0,0 +1,178 @@
+/* Determine a file system's time stamp precision.
+
+ Copyright 2010 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see . */
+
+/* written by Paul Eggert */
+
+#include
+#include "fstimeprec.h"
+
+#include "hash.h"
+#include "stat-time.h"
+#include
+#include
+
+/* A hash table that maps device numbers to precisions. */
+struct fstimeprec
+{
+ /* Map device numbers to precisions. */
+ struct hash_table *map;
+
+ /* Cache of the most recently allocated and otherwise-unused storage
+ for probing this table. */
+ struct ent *probe;
+};
+
+/* A pair that maps a device number to a precision. */
+struct ent
+{
+ dev_t dev;
+ int prec;
+};
+
+/* Hash an entry. */
+static size_t
+ent_hash (void const *x, size_t table_size)
+{
+ struct ent const *p = x;
+ dev_t dev = p->dev;
+
+ /* FIXME: This code is duplicated from di-set.c. */
+ /* When DEV is wider than size_t, exclusive-OR the words of DEV into H.
+ This avoids loss of info, without applying % to the wider type,
+ which could be quite slow on some systems. */
+ size_t h = dev;
+ unsigned int i;
+ unsigned int n_words = sizeof dev / sizeof h + (sizeof dev % sizeof h != 0);
+ for (i = 1; i < n_words; i++)
+ h ^= dev >> CHAR_BIT * sizeof h * i;
+
+ return h % table_size;
+}
+
+/* Return true if two entries are the same. */
+static bool
+ent_compare (void const *x, void const *y)
+{
+ struct ent const *a = x;
+ struct ent const *b = y;
+ return a->dev == b->dev;
+}
+
+/* Using the SET table, map a device to an entry that represents
+ the file system precision. Return NULL on error. */
+static struct ent *
+map_device (struct fstimeprec *tab, dev_t dev)
+{
+ /* Find space for the probe, reusing the cache if available. */
+ struct ent *ent;
+ struct ent *probe = tab->probe;
+ if (probe)
+ {
+ /* If repeating a recent query, return the cached result. */
+ if (probe->dev == dev)
+ return probe;
+ }
+ else
+ {
+ tab->probe = probe = malloc (sizeof *probe);
+ if (! probe)
+ return NULL;
+ probe->prec = 0;
+ }
+
+ /* Probe for the device. */
+ probe->dev = dev;
+ ent = hash_insert (tab->map, probe);
+ if (ent == probe)
+ tab->probe = NULL;
+ return ent;
+}
+
+/* Return a newly allocated table of file system time stamp
+ resolutions, or NULL if out of memory. */
+struct fstimeprec *
+fstimeprec_alloc (void)
+{
+ struct fstimeprec *tab = malloc (sizeof *tab);
+ if (tab)
+ {
+ enum { INITIAL_DEV_MAP_SIZE = 11 };
+ tab->map = hash_initialize (INITIAL_DEV_MAP_SIZE, NULL,
+ ent_hash, ent_compare, free);
+ if (! tab->map)
+ {
+ free (tab);
+ return NULL;
+ }
+ tab->probe = NULL;
+ }
+ return tab;
+}
+
+/* Free TAB. */
+void
+fstimeprec_free (struct fstimeprec *tab)
+{
+ hash_free (tab->map);
+ free (tab->probe);
+ free (tab);
+}
+
+/* Using the cached information in TAB, return the estimated precision
+ of time stamps for the file system containing the file whose status
+ info is in ST. The returned value counts the number of decimal
+ fraction digits. Return 0 on failure.
+
+ If TAB is null, the guess is based purely on ST. */
+int
+fstimeprec (struct fstimeprec *tab, struct stat const *st)
+{
+ /* Map the device number to an entry. */
+ struct ent *ent = (tab ? map_device (tab, st->st_dev) : NULL);
+
+ /* Guess the precision based on what has been seen so far. */
+ int prec = (ent ? ent->prec : 0);
+
+ /* If the current file's timestamp resolution is higher than the
+ guess, increase the guess. */
+ if (prec < 9)
+ {
+ struct timespec ats = get_stat_atime (st);
+ struct timespec mts = get_stat_mtime (st);
+ struct timespec cts = get_stat_ctime (st);
+ struct timespec bts = get_stat_birthtime (st);
+ unsigned int ans = ats.tv_nsec;
+ unsigned int mns = mts.tv_nsec;
+ unsigned int cns = cts.tv_nsec;
+ unsigned int bns = bts.tv_nsec < 0 ? 0 : bts.tv_nsec;
+ unsigned int power = 10;
+ int p;
+ for (p = prec + 1; p < 9; p++)
+ power *= 10;
+
+ while (ans % power | mns % power | cns % power | bns % power)
+ {
+ power /= 10;
+ prec++;
+ }
+
+ if (ent)
+ ent->prec = prec;
+ }
+
+ return prec;
+}
diff --git a/gl/lib/fstimeprec.h b/gl/lib/fstimeprec.h
new file mode 100644
index 0000000000..8ec24df765
--- /dev/null
+++ b/gl/lib/fstimeprec.h
@@ -0,0 +1,22 @@
+#include
+
+#ifndef ATTRIBUTE_MALLOC
+# if __GNUC__ >= 3
+# define ATTRIBUTE_MALLOC __attribute__ ((__malloc__))
+# else
+# define ATTRIBUTE_MALLOC
+# endif
+#endif
+
+#ifndef _GL_ARG_NONNULL
+# if (__GNUC__ == 3 && __GNUC_MINOR__ >= 3) || __GNUC__ > 3
+# define _GL_ARG_NONNULL(params) __attribute__ ((__nonnull__ params))
+# else
+# define _GL_ARG_NONNULL(params)
+# endif
+#endif
+
+struct fstimeprec *fstimeprec_alloc (void) ATTRIBUTE_MALLOC;
+void fstimeprec_free (struct fstimeprec *) _GL_ARG_NONNULL ((1));
+int fstimeprec (struct fstimeprec *, struct stat const *)
+ _GL_ARG_NONNULL ((2));
diff --git a/gl/modules/fstimeprec b/gl/modules/fstimeprec
new file mode 100644
index 0000000000..787c2e7a0d
--- /dev/null
+++ b/gl/modules/fstimeprec
@@ -0,0 +1,24 @@
+Description:
+estimate precision of time stamps in file systems
+
+Files:
+lib/fstimeprec.c
+lib/fstimeprec.h
+
+Depends-on:
+hash
+stat-time
+
+configure.ac:
+
+Makefile.am:
+lib_SOURCES += fstimeprec.c fstimeprec.h
+
+Include:
+"fstimeprec.h"
+
+License
+GPL
+
+Maintainer:
+Paul Eggert and Jim Meyering
diff --git a/gl/modules/fstimeprec-tests b/gl/modules/fstimeprec-tests
new file mode 100644
index 0000000000..bf6b49e6b2
--- /dev/null
+++ b/gl/modules/fstimeprec-tests
@@ -0,0 +1,10 @@
+Files:
+tests/test-fstimeprec.c
+
+Depends-on:
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-fstimeprec
+check_PROGRAMS += test-fstimeprec
diff --git a/gl/tests/test-fstimeprec.c b/gl/tests/test-fstimeprec.c
new file mode 100644
index 0000000000..f699139df6
--- /dev/null
+++ b/gl/tests/test-fstimeprec.c
@@ -0,0 +1,74 @@
+/* Test the fstimeprec module.
+ Copyright (C) 2010 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see . */
+
+/* written by Paul Eggert */
+
+#include
+#include
+#include
+#include
+
+#define ASSERT(expr) \
+ do \
+ { \
+ if (!(expr)) \
+ { \
+ fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \
+ fflush (stderr); \
+ abort (); \
+ } \
+ } \
+ while (0)
+
+#include "fstimeprec.h"
+
+static int
+fstimeprec_file (struct fstimeprec *tab, char const *file)
+{
+ struct stat st;
+ if (stat (file, &st) != 0)
+ return 0;
+ return fstimeprec (tab, &st);
+}
+
+int
+main (void)
+{
+ struct fstimeprec *tab = fstimeprec_alloc ();
+ ASSERT (tab);
+
+ int m1 = fstimeprec_file (tab, "/");
+ int m2 = fstimeprec_file (tab, ".");
+ int m3 = fstimeprec_file (tab, "..");
+ ASSERT (0 <= m1 && m1 <= 9);
+ ASSERT (0 <= m2 && m2 <= 9);
+ ASSERT (0 <= m3 && m3 <= 9);
+
+ int n1 = fstimeprec_file (tab, "/");
+ int n2 = fstimeprec_file (tab, ".");
+ int n3 = fstimeprec_file (tab, "..");
+ ASSERT (0 <= n1 && n1 <= 9);
+ ASSERT (0 <= n2 && n2 <= 9);
+ ASSERT (0 <= n3 && n3 <= 9);
+
+ ASSERT (m1 <= n1);
+ ASSERT (m2 <= n2);
+ ASSERT (m3 <= n3);
+
+ fstimeprec_free (tab);
+
+ return 0;
+}
diff --git a/src/stat.c b/src/stat.c
index d05a93b748..99f115bca6 100644
--- a/src/stat.c
+++ b/src/stat.c
@@ -63,6 +63,7 @@
#include "file-type.h"
#include "filemode.h"
#include "fs.h"
+#include "fstimeprec.h"
#include "getopt.h"
#include "mountlist.h"
#include "quote.h"
@@ -70,7 +71,6 @@
#include "stat-time.h"
#include "strftime.h"
#include "find-mount-point.h"
-#include "xstrtol.h"
#include "xvasprintf.h"
#if USE_STATVFS
@@ -183,6 +183,10 @@ static bool interpret_backslash_escapes;
"" for --printf=FMT, "\n" for --format=FMT (-c). */
static char const *trailing_delim = "";
+/* The representation of the decimal point in the current locale. */
+static char const *decimal_point;
+static size_t decimal_point_len;
+
/* Return the type of the specified file system.
Some systems have statfvs.f_basetype[FSTYPSZ] (AIX, HP-UX, and Solaris).
Others have statvfs.f_fstypename[_VFS_NAMELEN] (NetBSD 3.0).
@@ -463,69 +467,23 @@ human_time (struct timespec t)
return str;
}
-/* Return a string representation (in static storage)
- of the number of seconds in T since the epoch. */
-static char * ATTRIBUTE_WARN_UNUSED_RESULT
-epoch_sec (struct timespec t)
-{
- static char str[INT_BUFSIZE_BOUND (time_t)];
- return timetostr (t.tv_sec, str);
-}
-
-/* Output the number of nanoseconds, ARG.tv_nsec, honoring a
- WIDTH.PRECISION format modifier, where PRECISION specifies
- how many leading digits(on a field of 9) to print. */
-static void
-out_ns (char *pformat, size_t prefix_len, struct timespec arg)
-{
- /* If no format modifier is specified, i.e., nothing between the
- "%" and ":" of "%:X", then use the default of zero-padding and
- a width of 9. Otherwise, use the specified modifier(s).
- This is to avoid the mistake of omitting the zero padding on
- a number with fewer digits than the field width: when printing
- nanoseconds after a decimal place, the resulting floating point
- fraction would be off by a factor of 10 or more.
-
- If a precision/max width is specified, i.e., a '.' is present
- in the modifier, then then treat the modifier as operating
- on the default representation, i.e., a zero padded number
- of width 9. */
- unsigned long int ns = arg.tv_nsec;
-
- if (memchr (pformat, '.', prefix_len)) /* precision specified. */
- {
- char tmp[INT_BUFSIZE_BOUND (uintmax_t)];
- snprintf (tmp, sizeof tmp, "%09lu", ns);
- strcpy (pformat + prefix_len, "s");
- printf (pformat, tmp);
- }
- else
- {
- char const *fmt = (prefix_len == 1) ? "09lu" : "lu";
- /* Note that pformat is big enough, as %:X -> %09lu
- and two extra bytes are already allocated. */
- strcpy (pformat + prefix_len, fmt);
- printf (pformat, ns);
- }
-}
-
static void
out_string (char *pformat, size_t prefix_len, char const *arg)
{
strcpy (pformat + prefix_len, "s");
printf (pformat, arg);
}
-static void
+static int
out_int (char *pformat, size_t prefix_len, intmax_t arg)
{
strcpy (pformat + prefix_len, PRIdMAX);
- printf (pformat, arg);
+ return printf (pformat, arg);
}
-static void
+static int
out_uint (char *pformat, size_t prefix_len, uintmax_t arg)
{
strcpy (pformat + prefix_len, PRIuMAX);
- printf (pformat, arg);
+ return printf (pformat, arg);
}
static void
out_uint_o (char *pformat, size_t prefix_len, uintmax_t arg)
@@ -539,6 +497,122 @@ out_uint_x (char *pformat, size_t prefix_len, uintmax_t arg)
strcpy (pformat + prefix_len, PRIxMAX);
printf (pformat, arg);
}
+static int
+out_minus_zero (char *pformat, size_t prefix_len)
+{
+ strcpy (pformat + prefix_len, ".0f");
+ return printf (pformat, -0.25);
+}
+
+/* Output the number of seconds since the Epoch, using a format that
+ acts like printf's %f format. */
+static void
+out_epoch_sec (char *pformat, size_t prefix_len, struct stat const *statbuf,
+ struct timespec arg)
+{
+ char *dot = memchr (pformat, '.', prefix_len);
+ size_t sec_prefix_len = prefix_len;
+ int width = 0;
+ int precision = 0;
+ bool frac_left_adjust = false;
+
+ if (dot)
+ {
+ sec_prefix_len = dot - pformat;
+ pformat[prefix_len] = '\0';
+
+ if (ISDIGIT (dot[1]))
+ {
+ long int lprec = strtol (dot + 1, NULL, 10);
+ precision = (lprec <= INT_MAX ? lprec : INT_MAX);
+ }
+ else
+ {
+ static struct fstimeprec *tab;
+ if (! tab)
+ tab = fstimeprec_alloc ();
+ precision = fstimeprec (tab, statbuf);
+ }
+
+ if (precision && ISDIGIT (dot[-1]))
+ {
+ /* If a nontrivial width is given, subtract the width of the
+ decimal point and PRECISION digits that will be output
+ later. */
+ char *p = dot;
+ *dot = '\0';
+
+ do
+ --p;
+ while (ISDIGIT (p[-1]));
+
+ long int lwidth = strtol (p, NULL, 10);
+ width = (lwidth <= INT_MAX ? lwidth : INT_MAX);
+ if (1 < width)
+ {
+ p += (*p == '0');
+ sec_prefix_len = p - pformat;
+ int w_d = (decimal_point_len < width
+ ? width - decimal_point_len
+ : 0);
+ if (1 < w_d)
+ {
+ int w = w_d - precision;
+ if (1 < w)
+ {
+ char *dst = pformat;
+ for (char const *src = dst; src < p; src++)
+ {
+ if (*src == '-')
+ frac_left_adjust = true;
+ else
+ *dst++ = *src;
+ }
+ sec_prefix_len =
+ (dst - pformat
+ + (frac_left_adjust ? 0 : sprintf (dst, "%d", w)));
+ }
+ }
+ }
+ }
+ }
+
+ int divisor = 1;
+ for (int i = precision; i < 9; i++)
+ divisor *= 10;
+ int frac_sec = arg.tv_nsec / divisor;
+ int int_len;
+
+ if (TYPE_SIGNED (time_t))
+ {
+ bool minus_zero = false;
+ if (arg.tv_sec < 0 && arg.tv_nsec != 0)
+ {
+ int frac_sec_modulus = 1000000000 / divisor;
+ frac_sec = (frac_sec_modulus - frac_sec
+ - (arg.tv_nsec % divisor != 0));
+ arg.tv_sec += (frac_sec != 0);
+ minus_zero = (arg.tv_sec == 0);
+ }
+ int_len = (minus_zero
+ ? out_minus_zero (pformat, sec_prefix_len)
+ : out_int (pformat, sec_prefix_len, arg.tv_sec));
+ }
+ else
+ int_len = out_uint (pformat, sec_prefix_len, arg.tv_sec);
+
+ if (precision)
+ {
+ int prec = (precision < 9 ? precision : 9);
+ int trailing_prec = precision - prec;
+ int ilen = (int_len < 0 ? 0 : int_len);
+ int trailing_width = (ilen < width && decimal_point_len < width - ilen
+ ? width - ilen - decimal_point_len - prec
+ : 0);
+ printf ("%s%.*d%-*.*d", decimal_point, prec, frac_sec,
+ trailing_width, trailing_prec, 0);
+ }
+}
/* Print the context information of FILENAME, and return true iff the
context could not be obtained. */
@@ -853,38 +927,26 @@ print_stat (char *pformat, size_t prefix_len, unsigned int m,
}
break;
case 'W':
- out_string (pformat, prefix_len,
- epoch_sec (neg_to_zero (get_stat_birthtime (statbuf))));
- break;
- case 'W' + 256:
- out_ns (pformat, prefix_len, neg_to_zero (get_stat_birthtime (statbuf)));
+ out_epoch_sec (pformat, prefix_len, statbuf,
+ neg_to_zero (get_stat_birthtime (statbuf)));
break;
case 'x':
out_string (pformat, prefix_len, human_time (get_stat_atime (statbuf)));
break;
case 'X':
- out_string (pformat, prefix_len, epoch_sec (get_stat_atime (statbuf)));
- break;
- case 'X' + 256:
- out_ns (pformat, prefix_len, get_stat_atime (statbuf));
+ out_epoch_sec (pformat, prefix_len, statbuf, get_stat_atime (statbuf));
break;
case 'y':
out_string (pformat, prefix_len, human_time (get_stat_mtime (statbuf)));
break;
case 'Y':
- out_string (pformat, prefix_len, epoch_sec (get_stat_mtime (statbuf)));
- break;
- case 'Y' + 256:
- out_ns (pformat, prefix_len, get_stat_mtime (statbuf));
+ out_epoch_sec (pformat, prefix_len, statbuf, get_stat_mtime (statbuf));
break;
case 'z':
out_string (pformat, prefix_len, human_time (get_stat_ctime (statbuf)));
break;
case 'Z':
- out_string (pformat, prefix_len, epoch_sec (get_stat_ctime (statbuf)));
- break;
- case 'Z' + 256:
- out_ns (pformat, prefix_len, get_stat_ctime (statbuf));
+ out_epoch_sec (pformat, prefix_len, statbuf, get_stat_ctime (statbuf));
break;
case 'C':
fail |= out_file_context (pformat, prefix_len, filename);
@@ -968,21 +1030,9 @@ print_it (char const *format, char const *filename,
{
size_t len = strspn (b + 1, "#-+.I 0123456789");
char const *fmt_char = b + len + 1;
- unsigned int fmt_code;
+ unsigned int fmt_code = *fmt_char;
memcpy (dest, b, len + 1);
- /* The ":" modifier just before the letter in %W, %X, %Y, %Z
- tells stat to print the nanoseconds portion of the date. */
- if (*fmt_char == ':' && strchr ("WXYZ", fmt_char[1]))
- {
- fmt_code = fmt_char[1] + 256;
- ++fmt_char;
- }
- else
- {
- fmt_code = fmt_char[0];
- }
-
b = fmt_char;
switch (fmt_code)
{
@@ -1276,16 +1326,12 @@ The valid format sequences for files (without --file-system):\n\
%U User name of owner\n\
%w Time of file birth, human-readable; - if unknown\n\
%W Time of file birth, seconds since Epoch; 0 if unknown\n\
- %:W Time of file birth, nanoseconds remainder; 0 if unknown\n\
%x Time of last access, human-readable\n\
%X Time of last access, seconds since Epoch\n\
- %:X Time of last access, nanoseconds remainder\n\
%y Time of last modification, human-readable\n\
%Y Time of last modification, seconds since Epoch\n\
- %:Y Time of last modification, nanoseconds remainder\n\
%z Time of last change, human-readable\n\
%Z Time of last change, seconds since Epoch\n\
- %:Z Time of last change, nanoseconds remainder\n\
\n\
"), stdout);
@@ -1330,6 +1376,10 @@ main (int argc, char *argv[])
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
+ struct lconv const *locale = localeconv ();
+ decimal_point = (locale->decimal_point[0] ? locale->decimal_point : ".");
+ decimal_point_len = strlen (decimal_point);
+
atexit (close_stdout);
while ((c = getopt_long (argc, argv, "c:fLt", long_options, NULL)) != -1)
diff --git a/tests/misc/stat-nanoseconds b/tests/misc/stat-nanoseconds
index 314f43e2c5..0f41eb0452 100755
--- a/tests/misc/stat-nanoseconds
+++ b/tests/misc/stat-nanoseconds
@@ -19,18 +19,27 @@
test "$VERBOSE" = yes && stat --version
. "${srcdir=.}/init.sh"; path_prepend_ ../src
-touch -d '2010-10-21 18:43:33.023456789' k || framework_failure_
+# Set this to avoid problems with weird time zones.
+TZ=UTC0
+export TZ
+
+# Use a time stamp near the Epoch to avoid trouble with leap seconds.
+touch -d '1970-01-01 18:43:33.023456789' k || framework_failure_
ls --full-time | grep 18:43:33.023456789 \
|| skip_ this file system does not support sub-second time stamps
-test "$(stat -c %:X k)" = 023456789 || fail=1
-test "$(stat -c %3:X k)" = 23456789 || fail=1
-test "$(stat -c %3.3:X k)" = 023 || fail=1
-test "$(stat -c %.3:X k)" = 023 || fail=1
-test "$(stat -c %03.3:X k)" = 023 || fail=1
-test "$(stat -c %-5.3:X k)" = '023 ' || fail=1
-test "$(stat -c %05.3:X k)" = ' 023' || fail=1
-test "$(stat -c %010.3:X k)" = ' 023' || fail=1
+test "$(stat -c %X k)" = 67413 || fail=1
+test "$(stat -c %.X k)" = 67413.023456789 || fail=1
+test "$(stat -c %.1X k)" = 67413.0 || fail=1
+test "$(stat -c %.3X k)" = 67413.023 || fail=1
+test "$(stat -c %.6X k)" = 67413.023456 || fail=1
+test "$(stat -c %.9X k)" = 67413.023456789 || fail=1
+test "$(stat -c %13.6X k)" = ' 67413.023456' || fail=1
+test "$(stat -c %013.6X k)" = 067413.023456 || fail=1
+test "$(stat -c %-13.6X k)" = '67413.023456 ' || fail=1
+test "$(stat -c %18.10X k)" = ' 67413.0234567890' || fail=1
+test "$(stat -c %018.10X k)" = 0067413.0234567890 || fail=1
+test "$(stat -c %-18.10X k)" = '67413.0234567890 ' || fail=1
Exit $fail
diff --git a/tests/touch/60-seconds b/tests/touch/60-seconds
index d0082967c8..e06c770f29 100755
--- a/tests/touch/60-seconds
+++ b/tests/touch/60-seconds
@@ -30,7 +30,7 @@ echo 60.000000000 > exp || framework_failure
# an `invalid date format'. Specifying 60 seconds *is* valid.
TZ=UTC0 touch -t 197001010000.60 f || fail=1
-stat --p='%Y.%:Y\n' f > out || fail=1
+stat --p='%.9Y\n' f > out || fail=1
compare out exp || fail=1