From 3e3cbca74cb86cb694a6a95e849daaabcda74a31 Mon Sep 17 00:00:00 2001 From: Kevin B Kenny 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 + + * 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 * 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 /* 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