]> git.eshelyaron.com Git - emacs.git/commitdiff
Support sub-second file time-stamps on MS-Windows
authorEli Zaretskii <eliz@gnu.org>
Sun, 11 May 2025 10:33:24 +0000 (13:33 +0300)
committerEshel Yaron <me@eshelyaron.com>
Mon, 12 May 2025 19:55:32 +0000 (21:55 +0200)
* nt/inc/sys/stat.h (struct stat): New members for nsec part of
file times.

* lib-src/ntlib.c (convert_time):
* src/w32.c (convert_time): Accept an additional argument
TIME_NSEC and set it to the sub-second part of time.  All callers
changed.

(cherry picked from commit 3975094f1d96680fe73232ca4216733904ebecd0)

lib-src/ntlib.c
nt/inc/sys/stat.h
src/w32.c

index bcb9a4bbab2b4f47afe42cce19a19889aa61e043..2ba8d09c1ecb82bd3215cd894899514e2035c498 100644 (file)
@@ -30,6 +30,7 @@ along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
 #include <direct.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <math.h>
 #include <errno.h>
 #include <ctype.h>
 #include <sys/timeb.h>
@@ -256,7 +257,7 @@ static long double utc_base;
 static int init = 0;
 
 static time_t
-convert_time (FILETIME ft)
+convert_time (FILETIME ft, int *time_nsec)
 {
   long double ret;
 
@@ -266,7 +267,8 @@ convert_time (FILETIME ft)
   ret = (long double) ft.dwHighDateTime
     * 4096.0L * 1024.0L * 1024.0L + ft.dwLowDateTime;
   ret -= utc_base;
-  return (time_t) (ret * 1e-7L);
+  *time_nsec = (int) fmodl (ret, 1.0e7L) * 100;
+  return (time_t) (ret * 1.0e-7L);
 }
 
 static int
@@ -373,11 +375,19 @@ stat (const char * path, struct stat * buf)
   buf->st_size += wfd.nFileSizeLow;
 
   /* Convert timestamps to Unix format. */
-  buf->st_mtime = convert_time (wfd.ftLastWriteTime);
-  buf->st_atime = convert_time (wfd.ftLastAccessTime);
-  if (buf->st_atime == 0) buf->st_atime = buf->st_mtime;
-  buf->st_ctime = convert_time (wfd.ftCreationTime);
-  if (buf->st_ctime == 0) buf->st_ctime = buf->st_mtime;
+  buf->st_mtime = convert_time (wfd.ftLastWriteTime, &buf->st_mtimensec);
+  buf->st_atime = convert_time (wfd.ftLastAccessTime, &buf->st_atimensec);
+  if (buf->st_atime == 0)
+    {
+      buf->st_atime = buf->st_mtime;
+      buf->st_atimensec = buf->st_mtimensec;
+    }
+  buf->st_ctime = convert_time (wfd.ftCreationTime, &buf->st_ctimensec);
+  if (buf->st_ctime == 0)
+    {
+      buf->st_ctime = buf->st_mtime;
+      buf->st_ctimensec = buf->st_mtimensec;
+    }
 
   /* determine rwx permissions */
   if (wfd.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
@@ -473,11 +483,19 @@ fstat (int desc, struct stat * buf)
   buf->st_size += info.nFileSizeLow;
 
   /* Convert timestamps to Unix format. */
-  buf->st_mtime = convert_time (info.ftLastWriteTime);
-  buf->st_atime = convert_time (info.ftLastAccessTime);
-  if (buf->st_atime == 0) buf->st_atime = buf->st_mtime;
-  buf->st_ctime = convert_time (info.ftCreationTime);
-  if (buf->st_ctime == 0) buf->st_ctime = buf->st_mtime;
+  buf->st_mtime = convert_time (info.ftLastWriteTime, &buf->st_mtimensec);
+  buf->st_atime = convert_time (info.ftLastAccessTime, &buf->st_atimensec);
+  if (buf->st_atime == 0)
+    {
+      buf->st_atime = buf->st_mtime;
+      buf->st_atimensec = buf->st_mtimensec;
+    }
+  buf->st_ctime = convert_time (info.ftCreationTime, &buf->st_ctimensec);
+  if (buf->st_ctime == 0)
+    {
+      buf->st_ctime = buf->st_mtime;
+      buf->st_ctimensec = buf->st_mtimensec;
+    }
 
   /* determine rwx permissions */
   if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
index 3aa70f3052e35e9b086e6e3fdda746f7d8a02de9..c93923fd9e820b2bf9ced5de221942cec4bc86ac 100644 (file)
@@ -108,6 +108,9 @@ struct stat {
   time_t          st_ctime;
   char            st_uname[260];
   char            st_gname[260];
+  int             st_atimensec;
+  int             st_mtimensec;
+  int             st_ctimensec;
 };
 
 /* These are here to avoid compiler warnings when using wchar.h.  */
index d7bf173ce258468f243a767ca1391f3a02979d9d..c504c141b311e6ac9f2a45654659fcfc3acc49dd 100644 (file)
--- a/src/w32.c
+++ b/src/w32.c
@@ -5076,9 +5076,10 @@ initialize_utc_base (void)
 }
 
 static time_t
-convert_time (FILETIME ft)
+convert_time (FILETIME ft, int *time_nsec)
 {
   ULONGLONG tmp;
+  time_t time_sec;
 
   if (!init)
     {
@@ -5090,7 +5091,10 @@ convert_time (FILETIME ft)
     return 0;
 
   FILETIME_TO_U64 (tmp, ft);
-  return (time_t) ((tmp - utc_base) / 10000000L);
+  tmp -= utc_base;
+  time_sec = (time_t) (tmp / 10000000L);
+  *time_nsec = (tmp - (ULONGLONG) time_sec * 10000000L) * 100L;
+  return time_sec;
 }
 
 static void
@@ -5707,11 +5711,19 @@ stat_worker (const char * path, struct stat * buf, int follow_symlinks)
   buf->st_nlink = nlinks;
 
   /* Convert timestamps to Unix format. */
-  buf->st_mtime = convert_time (wtime);
-  buf->st_atime = convert_time (atime);
-  if (buf->st_atime == 0) buf->st_atime = buf->st_mtime;
-  buf->st_ctime = convert_time (ctime);
-  if (buf->st_ctime == 0) buf->st_ctime = buf->st_mtime;
+  buf->st_mtime = convert_time (wtime, &buf->st_mtimensec);
+  buf->st_atime = convert_time (atime, &buf->st_atimensec);
+  if (buf->st_atime == 0)
+    {
+      buf->st_atime = buf->st_mtime;
+      buf->st_atimensec = buf->st_mtimensec;
+    }
+  buf->st_ctime = convert_time (ctime, &buf->st_ctimensec);
+  if (buf->st_ctime == 0)
+    {
+      buf->st_ctime = buf->st_mtime;
+      buf->st_ctimensec = buf->st_mtimensec;
+    }
 
   /* determine rwx permissions */
   if (is_a_symlink && !follow_symlinks)
@@ -5853,11 +5865,19 @@ fstat (int desc, struct stat * buf)
   buf->st_size += info.nFileSizeLow;
 
   /* Convert timestamps to Unix format. */
-  buf->st_mtime = convert_time (info.ftLastWriteTime);
-  buf->st_atime = convert_time (info.ftLastAccessTime);
-  if (buf->st_atime == 0) buf->st_atime = buf->st_mtime;
-  buf->st_ctime = convert_time (info.ftCreationTime);
-  if (buf->st_ctime == 0) buf->st_ctime = buf->st_mtime;
+  buf->st_mtime = convert_time (info.ftLastWriteTime, &buf->st_mtimensec);
+  buf->st_atime = convert_time (info.ftLastAccessTime, &buf->st_atimensec);
+  if (buf->st_atime == 0)
+    {
+      buf->st_atime = buf->st_mtime;
+      buf->st_atimensec = buf->st_mtimensec;
+    }
+  buf->st_ctime = convert_time (info.ftCreationTime, &buf->st_ctimensec);
+  if (buf->st_ctime == 0)
+    {
+      buf->st_ctime = buf->st_mtime;
+      buf->st_ctimensec = buf->st_mtimensec;
+    }
 
   /* determine rwx permissions */
   if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY)