summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog11
-rw-r--r--tests/cmdAH.test14
-rw-r--r--win/tclWinFile.c145
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;
}