GetFileDate returns wrong time after DaylightSaving change

Windows specific forum
nalor
Enthusiast
Enthusiast
Posts: 115
Joined: Thu Apr 02, 2009 9:48 pm

GetFileDate returns wrong time after DaylightSaving change

Post by nalor »

Hi!

Yesterday I came accross a strange bug (at least I think it's a bug as I can't see any logic in the current behaviour).

When I use 'GetFileDate' to read the '#PB_Date_Modified' Date of a file I get different results depending on the DaylightSaving status.

For example the 5.20 x86 purebasic.exe:
GetFileDate returns >1379419754< >2013.09.17 12:09:14< when run with a systemdate BEFORE the daylight-saving change on 27.10.2013 (at least here in Austria), and when I change the systemdate to the current date (after the daylight saving change) I get a different result: >1379416154< >2013.09.17 11:09:14<

Personally I think 'GetFileDate' should always return the same value - regardless of the current daylight saving state.
When I check the modified date directly in windows explorer I'll get always the same result, independent of the current system date (always with 12:09).

So I've wrote a small workaround procedure 'GetFileDateTZ':
(based on the 'Remarks' here: http://msdn.microsoft.com/en-us/library ... 85%29.aspx)

Code: Select all

EnableExplicit

Procedure.i GetFileDateTZ(Filename.s, Datetype.i, UseUTC.b=#False)
	Protected FileHdl.i
	Protected Create.FILETIME
	Protected Access.FILETIME
	Protected Write.FILETIME
	Protected SystemTime.SYSTEMTIME
	Protected LocalTime.SYSTEMTIME
	
	FileHdl = ReadFile(#PB_Any, Filename)
	If FileHdl
		If Not GetFileTime_(FileID(FileHdl), @Create, @Access, @Write)
			Debug "GetFileDateTZ - GetFileTime Error!!"
			ProcedureReturn 0
		EndIf
		
		CloseFile(FileHdl)
		
		Select Datetype
			Case #PB_Date_Created
				If Not FileTimeToSystemTime_(@Create, @SystemTime)
					Debug "GetFileDateTZ - FileTimeToSystemTime Error!!"
				EndIf
				
			Case #PB_Date_Accessed
				If Not FileTimeToSystemTime_(@Access, @SystemTime)
					Debug "GetFileDateTZ - FileTimeToSystemTime Error!!"
				EndIf
				
			Case #PB_Date_Modified
				If Not FileTimeToSystemTime_(@Write, @SystemTime)
					Debug "GetFileDateTZ - FileTimeToSystemTime Error!!"
				EndIf
				
			Default
				Debug "GetFileDateTZ - Invalid Datetype >"+Str(Datetype)+"<"
				ProcedureReturn 0
		EndSelect
		
		If UseUTC
			LocalTime=SystemTime
		Else
			If Not SystemTimeToTzSpecificLocalTime_(#Null, @SystemTime, @LocalTime)
				Debug "GetFileDateTZ - SystemTimeToTzSpecificLocalTime Error!!"
				ProcedureReturn 0
			EndIf
		EndIf
		
		ProcedureReturn Date(LocalTime\wYear, LocalTime\wMonth, LocalTime\wDay, LocalTime\wHour, LocalTime\wMinute, LocalTime\wSecond)

	Else
		Debug "GetFileDateTZ - ReadFile Error!!"
		ProcedureReturn 0
	EndIf

EndProcedure

Define FileTime.i
Define sFile.s=#PB_Compiler_Home+"\purebasic.exe"


FileTime=GetFileDate(sFile, #PB_Date_Modified)
Debug "GetFileDate:         >"+Str(FileTime)+"< >"+FormatDate("%yyyy.%mm.%dd %hh:%ii:%ss", FileTime)+"<"

FileTime=GetFileDateTZ(sFile, #PB_Date_Modified)
Debug "GetFileDateTZ:       >"+Str(FileTime)+"< >"+FormatDate("%yyyy.%mm.%dd %hh:%ii:%ss", FileTime)+"<"

FileTime=GetFileDateTZ(sFile, #PB_Date_Modified, #True)
Debug "GetFileDateTZ: (UTC) >"+Str(FileTime)+"< >"+FormatDate("%yyyy.%mm.%dd %hh:%ii:%ss", FileTime)+"<"
When searching the forum for known problems with 'GetFileDate' I found this one: http://www.purebasic.fr/english/viewtopic.php?t=50902 and so I've included also a parameter to my 'GetFileDateTZ' procedure that allows to get the date as UTC - this way it not even changes in case the local timezone is changed.

Until it's fixed I'll use my workaround :)

Edit: Changed the procedure slightly, removed the check for filesize, this almost doubled the speed (it's still slower than the original GetFileDate - but fast enough for all my purposes)
Last edited by nalor on Mon Oct 28, 2013 11:30 pm, edited 1 time in total.
Little John
Addict
Addict
Posts: 4519
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: GetFileDate returns wrong time after DaylightSaving chan

Post by Little John »

This is a known problem, caused by the NTFS file system: Files created in summer (when daylight saving time is in effect) show a wrong time stamp in winter (when daylight saving time is not in effect), and vice versa. When using FAT32, then you'll not encounter this problem.
I did not check the details what PB exactly does in this context, but it probably just retrieves the time stamp from the file system.
nalor
Enthusiast
Enthusiast
Posts: 115
Joined: Thu Apr 02, 2009 9:48 pm

Re: GetFileDate returns wrong time after DaylightSaving chan

Post by nalor »

Hi LittleJohn!

I tried what you've suggested and copied the purebasic.exe to a FAT32 volume and now I'll get always a constant date: 2013.09.17 11:09:14

but my windows explorer still tells me it has been changed at 12:09 ;)

So to be honest I came to the conclusion that I cannot trust the return value of GetFileDate... it's neither the time I see in my explorer nor the UTC time - so I don't know what time it should be...
Little John
Addict
Addict
Posts: 4519
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: GetFileDate returns wrong time after DaylightSaving chan

Post by Little John »

Hi nalor,

unfortunately I currently don't have the time to go into details. E.g. this article provides some background information which might be of interest for you.
User avatar
utopiomania
Addict
Addict
Posts: 1655
Joined: Tue May 10, 2005 10:00 pm
Location: Norway

Re: GetFileDate returns wrong time after DaylightSaving chan

Post by utopiomania »

I use this code:

Code: Select all

  protected time.FILETIME, locTime.FILETIME, sysTime.SYSTEMTIME

  id = readFile(0, path)
  
  select when
    case 0 ;created    
      getFileTime_(id, time, 0, 0)
    case 1 ;accessed
      getFileTime_(Id, 0, time, 0)
    case 2 ;modified/written
      getFileTime_(id, 0, 0, time)            
  endSelect
  ;correct for daylight savings in effect
  fileTimeToLocalFileTime_(time, locTime)
  fileTimeToSystemTime_(locTime, sysTime)

  closeFile(0)
nalor
Enthusiast
Enthusiast
Posts: 115
Joined: Thu Apr 02, 2009 9:48 pm

Re: GetFileDate returns wrong time after DaylightSaving chan

Post by nalor »

Hi again!

@utopiomania:
I've tried your code - but I think it does the same as the internal 'GetFileDate' function because this is my result:
[18:40:37] GetFileDate: >1379416154< >2013.09.17 11:09:14<
[18:40:37] GetFileDateTZ: >1379419754< >2013.09.17 12:09:14<
[18:40:37] GetFileDateTZ: (UTC) >1379412554< >2013.09.17 10:09:14<
[18:40:37] GetFileDateTZ2: >1379416154< >2013.09.17 11:09:14<

Your version is the one labelled 'GetFileDateTZ2' - it shows exactly the same (wrong) time as the internal function.

The link that posted LittleJohn gives the explanation for this incorrect behaviour:
To convert a file time to local time, use the FileTimeToLocalFileTime function. However, FileTimeToLocalFileTime uses the current settings for the time zone and daylight saving time. Therefore, if it is daylight saving time, it takes daylight saving time into account, even if the file time you are converting is in standard time.
So using 'FileTimeToLocalTime' is never reliable when used in conjunction with daylight saving...
Fred
Administrator
Administrator
Posts: 16617
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Re: GetFileDate returns wrong time after DaylightSaving chan

Post by Fred »

As it seems to be a Windows glitch, I moved this topic out of the bug forum
User avatar
utopiomania
Addict
Addict
Posts: 1655
Joined: Tue May 10, 2005 10:00 pm
Location: Norway

Re: GetFileDate returns wrong time after DaylightSaving chan

Post by utopiomania »

nalor, the code I posted from copies files from cameras, and renames them according to their write date/time stamp onto pc's with or without daylight savings in effect, and does it correctly.
Without the conversions below the filenames was one hour wrong half of the year, but maybe that is a different problem than yours. :)

Code: Select all

;correct for daylight savings in effect
  fileTimeToLocalFileTime_(time, locTime)
  fileTimeToSystemTime_(locTime, sysTime)
nalor
Enthusiast
Enthusiast
Posts: 115
Joined: Thu Apr 02, 2009 9:48 pm

Re: GetFileDate returns wrong time after DaylightSaving chan

Post by nalor »

To be honest - I have a solution that works for me and so I can live with it - but I'd still expect that 'GetFileDate' returns the same timestamp as I can see directly in the explorer in windows.
Everything else is wrong in my eyes.

@Fred : And so I still think it's a bug in your GetFileDate implementation and not a glitch in windows because Microsoft clearly defines how it should be done correct (in the remarks section: http://msdn.microsoft.com/en-us/library ... 85%29.aspx ).
And my small workaround procedure does it as microsoft says and I get the correct value.

@utopiomania : nothing against your code, if it works for you it's fine, but when use it I get wrong results.
My Windows Explorer tells me the purebasic.exe of 5.20 has been changed on >2013.09.17 12:09:14<, so this is the result I'd expect, but the only reliable way is as I got it from the MS page (GetFileDateTZ):

Summertime on my system (2013.10.01 systemdate)
NTFS volume:
[19:44:08] GetFileDate: >1379419754< >2013.09.17 12:09:14<
[19:44:08] GetFileDateTZ: >1379419754< >2013.09.17 12:09:14<
[19:44:08] GetFileDateTZ2: >1379419754< >2013.09.17 12:09:14<

FAT32 volume:
[19:43:29] GetFileDate: >1379419754< >2013.09.17 12:09:14<
[19:43:29] GetFileDateTZ: >1379419754< >2013.09.17 12:09:14<
[19:43:29] GetFileDateTZ2: >1379419754< >2013.09.17 12:09:14<


Wintertime on my system (2013.10.30 systemdate):
NTFS volume:
[19:44:56] GetFileDate: >1379416154< >2013.09.17 11:09:14<
[19:44:56] GetFileDateTZ: >1379419754< >2013.09.17 12:09:14<
[19:44:56] GetFileDateTZ2: >1379416154< >2013.09.17 11:09:14<

FAT32 volume:
[19:37:38] GetFileDate: >1379419754< >2013.09.17 12:09:14<
[19:37:38] GetFileDateTZ: >1379419754< >2013.09.17 12:09:14<
[19:37:38] GetFileDateTZ2: >1379416154< >2013.09.17 11:09:14<

As last time 'GetFileDateTZ2' is your implementation, as you can see during wintertime it's still 1 hour off...

Finally I can say: GetFileDate returns wrong results during wintertime on NTFS volumes.
Last edited by nalor on Sun Aug 09, 2020 7:07 pm, edited 1 time in total.
camille
User
User
Posts: 66
Joined: Tue Nov 19, 2019 12:52 pm

Re: GetFileDate returns wrong time after DaylightSaving chan

Post by camille »

Does anyone know how to change the code from the first post so that it works for folders instead of files?
nalor
Enthusiast
Enthusiast
Posts: 115
Joined: Thu Apr 02, 2009 9:48 pm

Re: GetFileDate returns wrong time after DaylightSaving chan

Post by nalor »

camille wrote:Does anyone know how to change the code from the first post so that it works for folders instead of files?
I haven't tried - but I think it should with folders out of the box - simply because the windows api command 'getfiledate' is also designed to work with folders:

https://docs.microsoft.com/en-us/window ... etfiletime

Does it return something wrong for you? Can you post an example what is wrong?
camille
User
User
Posts: 66
Joined: Tue Nov 19, 2019 12:52 pm

Re: GetFileDate returns wrong time after DaylightSaving chan

Post by camille »

Code: Select all

Define sFile.s="D:\Temp\1.txt"
Output:

Code: Select all

GetFileDate:         >1460658816< >2016.04.14 18:33:36<
GetFileDateTZ:       >1460658816< >2016.04.14 18:33:36<
GetFileDateTZ: (UTC) >1460651616< >2016.04.14 16:33:36<
Now with the belonging folder:

Code: Select all

Define sFile.s="D:\Temp"
Output:

Code: Select all

GetFileDate:         >1592905274< >2020.06.23 09:41:14<
GetFileDateTZ - ReadFile Error!!
GetFileDateTZ:       >0< >1970.01.01 00:00:00<
GetFileDateTZ - ReadFile Error!!
GetFileDateTZ: (UTC) >0< >1970.01.01 00:00:00<
I've found a way that works with both files and folders but this way has its own problems.
GetFileDate() is more reliable (though not 100%) on files in the %TEMP% folder and only reports 0 for some files / folders
while the api way reports 0 for more (maybe 10%) files / folders...

If you want to try it, replace this:

Code: Select all

   FileHdl = ReadFile(#PB_Any, Filename)
   If FileHdl
      If Not GetFileTime_(FileID(FileHdl), @Create, @Access, @Write)
         ProcedureReturn 0
      EndIf

      CloseFile(FileHdl)
with this:

Code: Select all

      FileHdl = CreateFile_(path,
                            #GENERIC_READ|#GENERIC_WRITE,
                            #FILE_SHARE_READ|#FILE_SHARE_DELETE,
                            0,
                            #OPEN_EXISTING,
                            #FILE_FLAG_BACKUP_SEMANTICS,
                            0)
      If FileHdl
        If Not GetFileTime_(FileHdl, @Create, @Access, @Write)
          ProcedureReturn 0
        EndIf

        CloseHandle_(FileHdl)
User avatar
Pierre Bellisle
User
User
Posts: 35
Joined: Wed Jun 27, 2018 5:12 am

Re: GetFileDate returns wrong time after DaylightSaving chan

Post by Pierre Bellisle »

On my side I do not see any Windows glitch. Either on NTFS or FAT.
Use the Microsoft recommend way. See File Times - File Times and Daylight Saving Time

FileTimeToSystemTime, SystemTimeToTzSpecificLocalTime and SystemTimeToFileTime

Code: Select all

;Read "File Times and Daylight Saving Time" at https://docs.microsoft.com/en-us/windows/win32/sysinfo/file-times
;_____________________________________________________________________________

Procedure.s WinError(ErrorCode.L) 
 Protected *BStr
 Protected *BStrData.String 
 Protected.s ErrorMessage
 Protected.l ErrorLen

 ErrorLen = FormatMessage_(#FORMAT_MESSAGE_FROM_SYSTEM | #FORMAT_MESSAGE_ALLOCATE_BUFFER, 
                           #Null$, ErrorCode, #Null$, @*BStr , #Null$, #Null$) 
 If ErrorLen 
   *BStrData.String = @*BStr
   ErrorMessage = *BStrData\s
   LocalFree_(*BStr)
   ProcedureReturn("Error " + Str(ErrorCode) + " (0x" + Hex(ErrorCode) + ") : " + Left(ErrorMessage, ErrorLen - 2))
 Else
   ProcedureReturn("Unknown error " + Str(ErrorCode) + " (0x" + Hex(ErrorCode) + ")")
 EndIf

EndProcedure
;_____________________________________________________________________________

Procedure.s SelfExeName() ;GetExeNameGetFileNameFullFilename
 Protected zExeName.s{#MAX_PATH}

 GetModuleFileName_(GetModuleHandle_(0), @zExeName, #MAX_PATH) ;Get exe full name
 ProcedureReturn(zExeName) ;

EndProcedure
;_____________________________________________________________________________

Procedure.s FileTimeToInternationalTime(File_Time) 
 Protected Sys_Time.SystemTime

 FileTimeToSystemTime_(File_Time, @Sys_Time)
 ProcedureReturn(Str(Sys_Time\wyear)                 + "-" + RSet(Str(Sys_Time\wMonth), 2, "0") + "-" + 
                 RSet(Str(Sys_Time\wDay) ,   2, "0") + " " + RSet(Str(Sys_Time\wHour),  2, "0") + ":" + 
                 RSet(Str(Sys_Time\wMinute), 2, "0") + ":" + RSet(Str(Sys_Time\wSecond), 2, "0")) 
 
EndProcedure
;______________________________________________________________________________

 Define zExeName.s{#MAX_PATH}
 Define zFileSystem.s{20}
 Define zDrive.s{4}
 Define.s sBuffer
 Define.i hFile 
 Define.l LastError
 Define.l FolderWanted
 Define.FILETIME LastWriteTime
 Define.FILETIME FileTimeLocal
 Define.SYSTEMTIME SystemTimeLocal
 Define.TIME_ZONE_INFORMATION TimeZoneInfo
 
 FolderWanted = 001
 If FolderWanted
   sBuffer = "FolderWanted is TRUE" + #CRLF$ + #CRLF$ 
 Else
    sBuffer = "FolderWanted is FALSE" + #CRLF$ + #CRLF$ 
 EndIf

 If GetTimeZoneInformation_(TimeZoneInfo) = #TIME_ZONE_ID_DAYLIGHT 
     sBuffer = sBuffer + "Daylight saving is on"
 Else ;#TIME_ZONE_ID_STANDARD
     sBuffer = sBuffer + "Daylight saving is off"
 EndIf
 sBuffer = sBuffer + #CRLF$ + #CRLF$ + "Daylight saving offset is " + Str(TimeZoneInfo\DaylightBias) + " minutes"  + #CRLF$ 
 
 GetModuleFileName_(GetModuleHandle_(0), @zExeName, #MAX_PATH) ;Get exe full name ;Also Result$ = ProgramFilename()
 If FolderWanted
   zExeName = GetPathPart(zExeName) ;To get folder time
 EndIf
 ;;For test... zExeName = zExeName + ".Error"
 sBuffer = sBuffer + #CRLF$ + "File name is " + zExeName + #CRLF$ + #CRLF$

 If FolderWanted
   hFile = CreateFile_(zExeName, #FILE_LIST_DIRECTORY, #FILE_SHARE_READ, 0, #OPEN_EXISTING, #FILE_FLAG_BACKUP_SEMANTICS, 0)   
 Else
   hFile = CreateFile_(zExeName, #GENERIC_READ, #FILE_SHARE_READ, 0, #OPEN_EXISTING, #FILE_ATTRIBUTE_NORMAL, 0)
 EndIf

 If hFile = #INVALID_HANDLE_VALUE ;An error occured
   LastError = GetLastError_() ;Get error id
   sBuffer   = sBuffer + "CreateFile error: " + WinError(LastError) + #CRLF$ + #CRLF$ ;Shot it in readable text
 Else ;Handle is good
   If GetFileTime_(hFile, 0, 0, @LastWriteTime) = 0 ;Some error occured
     LastError = GetLastError_() ;Get error id
     ;;LastError = 5 ;Test error id
     sBuffer = sBuffer + "GetFileTime error: " + WinError(LastError) + #CRLF$ + #CRLF$ ;Shot it in readable text
   EndIf
   CloseHandle_(hFile)
 EndIf

 zDrive = Left(zExeName, 3)
 GetVolumeInformation_(zDrive, 0, 0, 0, 0, 0, @zFileSystem, SizeOf(zFileSystem))
 sBuffer = sBuffer + "File system for " + Left(zExeName, 3) + " is " + zFileSystem + #CRLF$ 

 FileTimeToSystemTime_(LastWriteTime, @SystemTimeLocal)                     ;Convert to local time part A
 SystemTimeToTzSpecificLocalTime_(#Null, SystemTimeLocal, @SystemTimeLocal) ;Convert to local time part B
 SystemTimeToFileTime_(SystemTimeLocal, @FileTimeLocal)                     ;Convert to local time part C

 sBuffer = sBuffer + #CRLF$ + "Local last write file time is " + FileTimeToInternationalTime(FileTimeLocal) 

 MessageBox_(#HWND_DESKTOP, sBuffer + #CRLF$, "File time", #MB_OK | #MB_TOPMOST)
;______________________________________________________________________________
;
Last edited by Pierre Bellisle on Tue Aug 11, 2020 7:51 pm, edited 2 times in total.
User avatar
Pierre Bellisle
User
User
Posts: 35
Joined: Wed Jun 27, 2018 5:12 am

Re: GetFileDate returns wrong time after DaylightSaving chan

Post by Pierre Bellisle »

Note to use the code on a folder instead of a filename.

As example, REM out
;GetModuleFileName_(GetModuleHandle_(0), @zExeName, #MAX_PATH) ;Get exe full name
and add just after...
zExeName = "C:\Windows" ;Use what you want...

REM out
;hFile = CreateFile_(zExeName, #GENERIC_READ, #FILE_SHARE_READ, 0, #OPEN_EXISTING, #FILE_ATTRIBUTE_NORMAL, 0)
And add just after...
hFile = CreateFile_(zExeName, #FILE_LIST_DIRECTORY, #FILE_SHARE_READ, 0, #OPEN_EXISTING, #FILE_FLAG_BACKUP_SEMANTICS, 0)
camille
User
User
Posts: 66
Joined: Tue Nov 19, 2019 12:52 pm

Re: GetFileDate returns wrong time after DaylightSaving chan

Post by camille »

Hi Pierre,

Code: Select all

Daylight saving is on
Daylight saving offset is -60 minutes
File name is R:\TEMP\~DF4393CAED6853E7CB.TMP
File system for R:\ is NTFS
Local last write file time is 1601-01-01 01:00:00

Code: Select all

Daylight saving is on
Daylight saving offset is -60 minutes
File name is R:\TEMP\~DFE9BD699FAE1B9233.TMP
File system for R:\ is NTFS
Local last write file time is 2020-08-11 09:33:02
So at least on my side there are glitches...
Btw, the real last write time for the first file is: 2020-08-11 09:40:31

My guess is that there is some lock on the first file but "LockHunter" (https://lockhunter.com/) doesn't report one.
Don't know why the code shows 1601-01-01 01:00:00 for that file. Windows Explorer and all other file manager show the correct date + time.
Post Reply