OK, here's some actual code demonstrating the differences I see (there are more than I thought). For this to work, you need two files, one with a date inside the daylight savings period, one outside. I have these, as seen from a command prompt in windows:
Code: Select all
Directory of C:\temp
16/04/2024 10:37 2 BSTFile.txt
08/11/2023 13:43 48 GMTFile.txt
With these files in place, I have this code:
Code: Select all
EnableExplicit
#df$="%yyyy.%mm.%dd %hh:%ii"
Procedure.q FileTimeToDate64(*FT.FILETIME, utc.l=#False)
Protected stUTC.SYSTEMTIME, st.SYSTEMTIME
Protected out.q
FileTimeToSystemTime_(*FT, stUTC)
If utc
; Return the UTC time with no TZ conversion
out=Date(stUTC\wYear, stUTC\wMonth, stUTC\wDay, stUTC\wHour, stUTC\wMinute, stUTC\wSecond)
Else
; Convert to local
SystemTimeToTzSpecificLocalTime_(#Null, stUTC, st)
out=Date(st\wYear, st\wMonth, st\wDay, st\wHour, st\wMinute, st\wSecond)
EndIf
ProcedureReturn out
EndProcedure
Procedure.q GetFileDate64(file$, type.l, utc.l=#False)
Protected.q out
Protected.i hnd, rc
Protected ft.FILETIME
hnd = CreateFile_(@File$, #GENERIC_READ, #FILE_SHARE_READ|#FILE_SHARE_WRITE, 0, #OPEN_EXISTING, #FILE_ATTRIBUTE_NORMAL|#FILE_FLAG_BACKUP_SEMANTICS, 0)
If hnd <> #INVALID_HANDLE_VALUE
Select type
Case #PB_Date_Created : rc = GetFileTime_(hnd, ft, 0, 0)
Case #PB_Date_Accessed : rc = GetFileTime_(hnd, 0, ft, 0)
Case #PB_Date_Modified : rc = GetFileTime_(hnd, 0, 0, ft)
EndSelect
If rc = 0
; error
Else
out = FileTimeToDate64(ft, utc)
EndIf
CloseHandle_(hnd)
EndIf
ProcedureReturn out
EndProcedure
Define.s gmtfile$="C:\Temp\GMTFile.txt"
Define.s bstfile$="C:\Temp\BSTFile.txt"
Debug "File with a timestamp in GMT:"
Debug "PB date local: "+FormatDate(#df$, GetFileDate(gmtfile$, #PB_Date_Modified))
Debug "PB date UTC: "+FormatDate(#df$, ConvertDate(GetFileDate(gmtfile$, #PB_Date_Modified), #PB_Date_UTC))
Debug "API date local: "+FormatDate(#df$, GetFileDate64(gmtfile$, #PB_Date_Modified, #False))
Debug "API date UTC: "+FormatDate(#df$, GetFileDate64(gmtfile$, #PB_Date_Modified, #True))
Debug ""
Debug "File with a timestamp in BST:"
Debug "PB date local: "+FormatDate(#df$, GetFileDate(bstfile$, #PB_Date_Modified))
Debug "PB date UTC: "+FormatDate(#df$, ConvertDate(GetFileDate(bstfile$, #PB_Date_Modified), #PB_Date_UTC))
Debug "API date local: "+FormatDate(#df$, GetFileDate64(bstfile$, #PB_Date_Modified, #False))
Debug "API date UTC: "+FormatDate(#df$, GetFileDate64(bstfile$, #PB_Date_Modified, #True))
This produces this output:
Code: Select all
File with a timestamp in GMT:
PB date local: 2023.11.08 13:43
PB date UTC: 2023.11.08 13:43
API date local: 2023.11.08 12:43
API date UTC: 2023.11.08 12:43
File with a timestamp in BST:
PB date local: 2024.04.16 10:37
PB date UTC: 2024.04.16 09:37
API date local: 2024.04.16 10:37
API date UTC: 2024.04.16 09:37
For the file with a BST timestamp, both methods give the same answers and I think they are right. Whether they would still agree and still be right after daylight savings ends is harder to test.
For the file with the timestamp in GMT, the PB and API methods differ, but there are oddities in both directions. PB gives the local time for both local and UTC times. But the API approach gives the UTC time for both. (Outside of this test program, I only cared about the UTC time, and so had not noticed this discrepancy in the other direction.)
I find it interesting that both the API and PB approaches agree that converting to UTC has no effect on this timestamp. i.e. they agree that, given that the timestamp and system timezone refer to a time when the offset from UTC was 0, there is no adjustment to make. This implies that PB's ConvertDate() is doing the right thing (and maybe using the same API calls). But, they are still 1 hour offset from one another, and this is the cause of the discrepancy I was seeing originally that lead to me poking around at this.
The question then is, who's right here - the PB output or the API? As I understand the API, the UTC timestamp is what is actually stored on disk, and by reading that as-is and not converting it, we should have the UTC timestamp as directly as possible. But, this does not agree with the PB output.
Can anyone shed any light on this?