From 3e3cbca74cb86cb694a6a95e849daaabcda74a31 Mon Sep 17 00:00:00 2001
From: Kevin B Kenny <kennykb@acm.org>
Date: Tue, 15 Nov 2005 16:41:40 +0000
Subject: Bugs 926106 and 1353840

---
 ChangeLog        |  11 +++++
 tests/cmdAH.test |  14 +++++-
 win/tclWinFile.c | 145 ++++++++++++++++++++++++++++++++-----------------------
 3 files changed, 109 insertions(+), 61 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 5c7b243..23c0fe8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2005-11-15  Kevin B. Kenny  <kennykb@acm.org>
+
+	* tests/cmdAH.test: Backported the fix for [Bug 926016] because of
+	* win/tclWinFile.c: a repeated bug report in 8.4 [Bug 1353840].
+	Windows [file mtime] will now return seconds from the Posix epoch
+	correctly (except for FAT32 file systems after a DST change
+	without a reboot - for which there is no help).  A side effect is
+	that file times will appear different in Tcl from the way they do
+	in Windows Explorer or a 'dir' listing, because the Microsoft
+	tools get the DST state wrong in the listings.
+	
 2005-11-09  Kevin B. Kenny  <kennykb@acm.org>
 
 	* generic/tclTimer.c: Changed [after] so that it behaves correctly
diff --git a/tests/cmdAH.test b/tests/cmdAH.test
index cbd8947..6b0e053 100644
--- a/tests/cmdAH.test
+++ b/tests/cmdAH.test
@@ -10,7 +10,7 @@
 # See the file "license.terms" for information on usage and redistribution
 # of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 #
-# RCS: @(#) $Id: cmdAH.test,v 1.30.2.4 2004/04/06 08:47:01 dkf Exp $
+# RCS: @(#) $Id: cmdAH.test,v 1.30.2.5 2005/11/15 16:41:41 kennykb Exp $
 
 if {[lsearch [namespace children] ::tcltest] == -1} {
     package require tcltest 2.1
@@ -1437,6 +1437,18 @@ test cmdAH-24.11 {Tcl_FileObjCmd: mtime touch with non-ascii chars} winOnly {
 removeFile touch.me
 rename waitForEvenSecondForFAT {}
 
+test cmdAH-24.12 {Tcl_FileObjCmd: mtime and daylight savings} {
+    set name [file join [temporaryDirectory] clockchange]
+
+    file delete -force $name
+    close [open $name w]
+    set time [clock scan "21:00:00 October 30 2004 GMT"]
+    file mtime $name $time
+    set newmtime [file mtime $name]
+    file delete $name
+    expr {$newmtime == $time ? 1 : "$newmtime != $time"}
+} {1}
+
 # owned
 
 test cmdAH-25.1 {Tcl_FileObjCmd: owned} {
diff --git a/win/tclWinFile.c b/win/tclWinFile.c
index d58fa35..4171e67 100644
--- a/win/tclWinFile.c
+++ b/win/tclWinFile.c
@@ -11,7 +11,7 @@
  * See the file "license.terms" for information on usage and redistribution
  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  *
- * RCS: @(#) $Id: tclWinFile.c,v 1.44.2.12 2005/06/22 21:23:33 dgp Exp $
+ * RCS: @(#) $Id: tclWinFile.c,v 1.44.2.13 2005/11/15 16:41:41 kennykb Exp $
  */
 
 //#define _WIN32_WINNT  0x0500
@@ -23,6 +23,13 @@
 #include <lmaccess.h>		/* For TclpGetUserHome(). */
 
 /*
+ * The number of 100-ns intervals between the Windows system epoch (1601-01-01
+ * on the proleptic Gregorian calendar) and the Posix epoch (1970-01-01).
+ */
+
+#define POSIX_EPOCH_AS_FILETIME		116444736000000000
+
+/*
  * Declarations for 'link' related information.  This information
  * should come with VC++ 6.0, but is not in some older SDKs.
  * In any case it is not well documented.
@@ -151,6 +158,7 @@ typedef enum _FINDEX_SEARCH_OPS {
 /* Other typedefs required by this code */
 
 static time_t		ToCTime(FILETIME fileTime);
+static void		FromCTime(time_t posixTime, FILETIME *fileTime);
 
 typedef NET_API_STATUS NET_API_FUNCTION NETUSERGETINFOPROC
 	(LPWSTR servername, LPWSTR username, DWORD level, LPBYTE *bufptr);
@@ -1860,57 +1868,57 @@ NativeStatMode(DWORD attr, int checkLinks, int isExec)
     return (unsigned short)mode;
 }
 
+/*
+ *------------------------------------------------------------------------
+ *
+ * ToCTime --
+ *
+ *	Converts a Windows FILETIME to a time_t in UTC.
+ *
+ * Results:
+ *	Returns the count of seconds from the Posix epoch.
+ *
+ *------------------------------------------------------------------------
+ */
+
 static time_t
 ToCTime(
-    FILETIME fileTime)		/* UTC Time to convert to local time_t. */
+    FILETIME fileTime)		/* UTC time */
 {
-    FILETIME localFileTime;
-    SYSTEMTIME systemTime;
-    struct tm tm;
-
-    if (FileTimeToLocalFileTime(&fileTime, &localFileTime) == 0) {
-	return 0;
-    }
-    if (FileTimeToSystemTime(&localFileTime, &systemTime) == 0) {
-	return 0;
-    }
-    tm.tm_sec = systemTime.wSecond;
-    tm.tm_min = systemTime.wMinute;
-    tm.tm_hour = systemTime.wHour;
-    tm.tm_mday = systemTime.wDay;
-    tm.tm_mon = systemTime.wMonth - 1;
-    tm.tm_year = systemTime.wYear - 1900;
-    tm.tm_wday = 0;
-    tm.tm_yday = 0;
-    tm.tm_isdst = -1;
-
-    return mktime(&tm);
-}
-
-#if 0
-
-    /*
-     * Borland's stat doesn't take into account localtime.
-     */
-
-    if ((result == 0) && (buf->st_mtime != 0)) {
-	TIME_ZONE_INFORMATION tz;
-	int time, bias;
-
-	time = GetTimeZoneInformation(&tz);
-	bias = tz.Bias;
-	if (time == TIME_ZONE_ID_DAYLIGHT) {
-	    bias += tz.DaylightBias;
-	}
-	bias *= 60;
-	buf->st_atime -= bias;
-	buf->st_ctime -= bias;
-	buf->st_mtime -= bias;
-    }
+    LARGE_INTEGER convertedTime;
 
-#endif
+    convertedTime.LowPart = fileTime.dwLowDateTime;
+    convertedTime.HighPart = (LONG) fileTime.dwHighDateTime;
 
+    return (time_t) ((convertedTime.QuadPart
+	    - (Tcl_WideInt) POSIX_EPOCH_AS_FILETIME) / (Tcl_WideInt) 10000000);
+}
+
+/*
+ *------------------------------------------------------------------------
+ *
+ * FromCTime --
+ *
+ *	Converts a time_t to a Windows FILETIME
+ *
+ * Results:
+ *	Returns the count of 100-ns ticks seconds from the Windows epoch.
+ *
+ *------------------------------------------------------------------------
+ */
 
+static void
+FromCTime(
+    time_t posixTime,
+    FILETIME* fileTime)		/* UTC Time */
+{
+    LARGE_INTEGER convertedTime;
+    convertedTime.QuadPart = ((LONGLONG) posixTime) * 10000000
+	+ POSIX_EPOCH_AS_FILETIME;
+    fileTime->dwLowDateTime = convertedTime.LowPart;
+    fileTime->dwHighDateTime = convertedTime.HighPart;
+}
+
 #if 0
 /*
  *-------------------------------------------------------------------------
@@ -2443,6 +2451,7 @@ TclpObjNormalizePath(interp, pathPtr, nextCheckpoint)
     return nextCheckpoint;
 }
 
+
 /*
  *---------------------------------------------------------------------------
  *
@@ -2454,25 +2463,41 @@ TclpObjNormalizePath(interp, pathPtr, nextCheckpoint)
  *	0 on success, -1 on error.
  *
  * Side effects:
- *	None.
+ *	Sets errno to a representation of any Windows problem that's observed
+ *	in the process.
  *
  *---------------------------------------------------------------------------
  */
+
 int
-TclpUtime(pathPtr, tval)
-    Tcl_Obj *pathPtr;      /* File to modify */
-    struct utimbuf *tval;  /* New modification date structure */
+TclpUtime(
+    Tcl_Obj *pathPtr,		/* File to modify */
+    struct utimbuf *tval)	/* New modification date structure */
 {
-    int res;
-    /* 
-     * Windows uses a slightly different structure name and, possibly,
-     * contents, so we have to copy the information over
+    int res = 0;
+    HANDLE fileHandle;
+    FILETIME lastAccessTime, lastModTime;
+
+    FromCTime(tval->actime, &lastAccessTime);
+    FromCTime(tval->modtime, &lastModTime);
+
+    /*
+     * We use the native APIs (not 'utime') because there are some daylight
+     * savings complications that utime gets wrong.
      */
-    struct _utimbuf buf;
-    
-    buf.actime = tval->actime;
-    buf.modtime = tval->modtime;
-    
-    res = (*tclWinProcs->utimeProc)(Tcl_FSGetNativePath(pathPtr),&buf);
+
+    fileHandle = (tclWinProcs->createFileProc) (
+	    (CONST TCHAR *) Tcl_FSGetNativePath(pathPtr),
+	    FILE_WRITE_ATTRIBUTES, 0, NULL, OPEN_EXISTING,
+	    FILE_ATTRIBUTE_NORMAL, NULL);
+
+    if (fileHandle == INVALID_HANDLE_VALUE ||
+	    !SetFileTime(fileHandle, NULL, &lastAccessTime, &lastModTime)) {
+	TclWinConvertError(GetLastError());
+	res = -1;
+    }
+    if (fileHandle != INVALID_HANDLE_VALUE) {
+	CloseHandle(fileHandle);
+    }
     return res;
 }
-- 
cgit v0.12