stat: print timestamps to full resolution
authorEric Blake <eblake@redhat.com>
Thu, 30 Sep 2010 22:42:13 +0000 (16:42 -0600)
committerEric Blake <eblake@redhat.com>
Fri, 1 Oct 2010 16:43:41 +0000 (10:43 -0600)
* src/stat.c (epoch_time): New function.
(print_stat): Use it for %[WXYZ].
* NEWS: Document this.
* tests/touch/60-seconds: Adjust test to match.
* tests/misc/stat-birthtime: Likewise.

NEWS
src/stat.c
tests/misc/stat-birthtime
tests/touch/60-seconds

diff --git a/NEWS b/NEWS
index 0c7cc38..2ce03e3 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -75,6 +75,11 @@ GNU coreutils NEWS                                    -*- outline -*-
   merely accepted and ignored, for compatibility.  Starting two years
   ago, with coreutils-7.0, its use evoked a warning.
 
+  stat now outputs the full sub-second resolution for the atime,
+  mtime, and ctime values since the Epoch, when using the %X, %Y, and
+  %Z directives of the --format option.  This matches the fact that
+  %x, %y, and %z were already doing so for the human-readable variant.
+
   touch's --file option is no longer recognized.  Use --reference=F (-r)
   instead.  --file has not been documented for 15 years, and its use has
   elicited a warning since coreutils-7.1.
index e1d68a5..6b2e99b 100644 (file)
@@ -461,6 +461,26 @@ human_time (struct timespec t)
   return str;
 }
 
+static char * ATTRIBUTE_WARN_UNUSED_RESULT
+epoch_time (struct timespec t)
+{
+  static char str[INT_STRLEN_BOUND (time_t) + sizeof ".NNNNNNNNN"];
+  /* Note that time_t can technically be a floating point value, such
+     that casting to [u]intmax_t could lose a fractional value or
+     suffer from overflow.  However, most porting targets have an
+     integral time_t; also, we know of no file systems that store
+     valid time values outside the bounds of intmax_t even if that
+     value were represented as a floating point.  Besides, the cost of
+     converting to struct tm just to use nstrftime (str, len, "%s.%N",
+     tm, 0, t.tv_nsec) is pointless, since nstrftime would have to
+     convert back to seconds as time_t.  */
+  if (TYPE_SIGNED (time_t))
+    sprintf (str, "%" PRIdMAX ".%09ld", (intmax_t) t.tv_sec, t.tv_nsec);
+  else
+    sprintf (str, "%" PRIuMAX ".%09ld", (uintmax_t) t.tv_sec, t.tv_nsec);
+  return str;
+}
+
 static void
 out_string (char *pformat, size_t prefix_len, char const *arg)
 {
@@ -802,38 +822,27 @@ print_stat (char *pformat, size_t prefix_len, char m,
         struct timespec t = get_stat_birthtime (statbuf);
         if (t.tv_nsec < 0)
           out_string (pformat, prefix_len, "-");
-        else if (TYPE_SIGNED (time_t))
-          out_int (pformat, prefix_len, t.tv_sec);
         else
-          out_uint (pformat, prefix_len, t.tv_sec);
+          out_string (pformat, prefix_len, epoch_time (t));
       }
       break;
     case 'x':
       out_string (pformat, prefix_len, human_time (get_stat_atime (statbuf)));
       break;
     case 'X':
-      if (TYPE_SIGNED (time_t))
-        out_int (pformat, prefix_len, statbuf->st_atime);
-      else
-        out_uint (pformat, prefix_len, statbuf->st_atime);
+      out_string (pformat, prefix_len, epoch_time (get_stat_atime (statbuf)));
       break;
     case 'y':
       out_string (pformat, prefix_len, human_time (get_stat_mtime (statbuf)));
       break;
     case 'Y':
-      if (TYPE_SIGNED (time_t))
-        out_int (pformat, prefix_len, statbuf->st_mtime);
-      else
-        out_uint (pformat, prefix_len, statbuf->st_mtime);
+      out_string (pformat, prefix_len, epoch_time (get_stat_mtime (statbuf)));
       break;
     case 'z':
       out_string (pformat, prefix_len, human_time (get_stat_ctime (statbuf)));
       break;
     case 'Z':
-      if (TYPE_SIGNED (time_t))
-        out_int (pformat, prefix_len, statbuf->st_ctime);
-      else
-        out_uint (pformat, prefix_len, statbuf->st_ctime);
+      out_string (pformat, prefix_len, epoch_time (get_stat_ctime (statbuf)));
       break;
     case 'C':
       fail |= out_file_context (filename, pformat, prefix_len);
index b54268c..38e5d59 100755 (executable)
@@ -28,8 +28,7 @@ ctime=$(stat --format %Z a) || fail=1
 
 case $(stat --format %x a) in
   *.000000000*) sleep 2;; # worst case file system is FAT
-  *) # FIXME: sleep .1 would be sufficient if %X is fixed to show nanoseconds
- sleep 1;;
+  *) sleep .02;; # should be adequate for any system with subsecond resolution
 esac
 
 touch a || fail=1
index f858a30..f98f0c5 100755 (executable)
@@ -23,7 +23,7 @@ fi
 
 . $srcdir/test-lib.sh
 
-echo 60 > exp || framework_failure
+echo 60.000000000 > exp || framework_failure
 
 
 # Before coreutils-7.7, this would fail, complaining of