// // Original Authors: Jonathan M. Gilligan, Tony M. Hoyle // // // This program is free software; you can redistribute it and/or modify it // under the terms of the GNU General Public License as published by the // Free Software Foundation; either version 2 of the License, or (at your // option) any later version. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // // Modification History: // 18 May 2001, JMG -- First version #include <windows.h> #include <tchar.h> #include <time.h> #include <stdbool.h> #include "JmgStat.h" /* Tony Hoyle's function for testing whether a given volume uses UTC or * local time to record file modification times * * Reproduced here with permission of Tony Hoyle. * * This code is copyright by Tony Hoyle and is licensed under the Gnu * Public License. (See above) * * NTFS, HPFS, and OWFS store file times as UTC times. * FAT stores file times as local time. * * INPUTS: * LPCSTR name: fully qualified path * * OUTPUTS: * Return true if the file system on the volume in question * stores file times as UTC */ bool IsUTCVolume ( LPCTSTR name ) { _TCHAR szDrive[_MAX_DRIVE + 1] = _T(""); _TCHAR szFs[32]=_T(""); _tsplitpath(name, szDrive, NULL, NULL, NULL); _tcscat(szDrive, _T("\\")); GetVolumeInformation( szDrive, NULL, 0, NULL, NULL, NULL, szFs, 32 ); return ! ( _tcsicmp( szFs, _T("NTFS") ) && _tcsicmp( szFs, _T("HPFS") ) && _tcsicmp( szFs, _T("OWFS") ) ); } /* Convert a file time to a Unix time_t structure. This function is as * complicated as it is because it needs to ask what time system the * filetime describes. * * INPUTS: * const FILETIME * ft: A file time. It may be in UTC or in local * time (see local_time, below, for details). * * time_t * ut: The destination for the converted time. * * bool local_time: TRUE if the time in *ft is in local time * and I need to convert to a real UTC time. * * OUTPUTS: * time_t * ut: Store the result in *ut. */ static bool FileTimeToUnixTime ( const FILETIME* ft, time_t* ut, bool local_time ) { bool success = FALSE; if ( local_time ) { struct tm atm; SYSTEMTIME st; success = FileTimeToSystemTime ( ft, &st ); /* Important: mktime looks at the tm_isdst field to determine * whether to apply the DST correction. If this field is zero, * then no DST is applied. If the field is one, then DST is * applied. If the field is minus one, then DST is applied * if the United States rule calls for it (DST starts at * 02:00 on the first Sunday in April and ends at 02:00 on * the last Sunday in October. * * If you are concerned about time zones that follow different * rules, then you must either use GetTimeZoneInformation() to * get your system's TIME_ZONE_INFO and use the information * therein to figure out whether the time in question was in * DST or not, or else use SystemTimeToTzSpecifiedLocalTime() * to do the same. * * I haven't tried playing with SystemTimeToTzSpecifiedLocalTime() * so I am nor sure how well it handles funky stuff. */ atm.tm_sec = st.wSecond; atm.tm_min = st.wMinute; atm.tm_hour = st.wHour; atm.tm_mday = st.wDay; /* tm_mon is 0 based */ atm.tm_mon = st.wMonth - 1; /* tm_year is 1900 based */ atm.tm_year = st.wYear>1900?st.wYear - 1900:st.wYear; atm.tm_isdst = -1; /* see notes above */ *ut = mktime ( &atm ); } else { /* FILETIME = number of 100-nanosecond ticks since midnight * 1 Jan 1601 UTC. time_t = number of 1-second ticks since * midnight 1 Jan 1970 UTC. To translate, we subtract a * FILETIME representation of midnight, 1 Jan 1970 from the * time in question and divide by the number of 100-ns ticks * in one second. */ /* One second = 10,000,000 * 100 nsec */ const ULONGLONG second = 10000000L; SYSTEMTIME base_st = { 1970, /* wYear */ 1, /* wMonth */ 0, /* wDayOfWeek */ 1, /* wDay */ 0, /* wHour */ 0, /* wMinute */ 0, /* wSecond */ 0 /* wMilliseconds */ }; ULARGE_INTEGER itime; FILETIME base_ft; success = SystemTimeToFileTime ( &base_st, &base_ft ); if (success) { itime.QuadPart = ((ULARGE_INTEGER *)ft)->QuadPart; itime.QuadPart -= ((ULARGE_INTEGER *)&base_ft)->QuadPart; itime.QuadPart /= second; *ut = itime.LowPart; } } if (!success) { *ut = -1; /* error value used by mktime() */ } return success; } /* Get file modification time using FileTimeToUnixTime() * * INPUTS: * LPCTSTR name: the file name */ bool GetUTCFileModTime ( LPCTSTR name, time_t * utc_mod_time ) { WIN32_FIND_DATA find_buf; FILETIME mod_time; HANDLE find_handle; bool success = FALSE; * utc_mod_time = 0L; find_handle = FindFirstFile ( name, &find_buf ); success = ( find_handle != INVALID_HANDLE_VALUE ); if (success) { /* Originally I thought that I needed to apply a correction * LocalTimeToFileTime() to files from FAT volumes, but the * FindFirstFile() system call thoughtfully applies this * correction itself. * * Thus, the file time returned is allegedly in UTC. * * However, the correction from local to UTC is applied * incorrectly (Thanks a lot, Microsoft!). As documented in the * Win32 API (see MSDN or the PSDK), DST is applied if and only * if the computer's system time is in DST at the time we call * FindFirstFile(), irrespective or whether DST applied at the * time the file was modified! * * Thus, we have to call FileTimeToLocalFileTime() to undo * Windows's good intentions. We correctly translate the time * In FileTimeToUnixTime(). * */ if ( IsUTCVolume ( name ) ) { mod_time = find_buf.ftLastWriteTime; success = FileTimeToUnixTime ( &mod_time, utc_mod_time, FALSE ); } else { // See notes above... success = FileTimeToLocalFileTime ( &find_buf.ftLastWriteTime, &mod_time ); success = success && FileTimeToUnixTime ( &mod_time, utc_mod_time, TRUE ); } } FindClose ( find_handle ); return success; }