Win32 FILETIME to POSIX and back

Share your advanced PureBasic knowledge/code with the community.
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Win32 FILETIME to POSIX and back

Post by Mistrel »

Additional information here:
http://support.microsoft.com/kb/167296
http://frenk.wordpress.com/2009/12/14/c ... timestamp/
http://en.allexperts.com/q/C-1040/time- ... indows.htm
http://msdn.microsoft.com/en-us/library ... 85%29.aspx
http://coding.derkeiler.com/Archive/PHP ... 00307.html

Code: Select all

;/ FILETIME is a 64-bit unsigned integer representing the number of
;/ 100-nanosecond intervals since January 1, 1601 UNIX timestamp is number
;/ of seconds since January 1, 1970.
;/ 116444736000000000 = 10000000 * 60 * 60 * 24 * 365 * 369 + 89 leap days

;/ POSIX time is in milliseconds
Procedure.q Time_FileTimeToPOSIX(FileTime.q, LocalTime.b=#False)
  If LocalTime
    FileTimeToLocalFileTime_(@FileTime.q,@FileTime.q)
  EndIf
  
  FileTime.q=(FileTime.q-116444736000000000)/10000000
  
  ProcedureReturn FileTime.q
EndProcedure

;/ FileTime is in the 100s of nanoseconds
Procedure.q Time_POSIXToFileTime(POSIX.q, LocalTime.b=#False)
  POSIX.q=(POSIX.q*10000000)+116444736000000000
  
  If LocalTime
    LocalFileTimeToFileTime_(@POSIX.q,@POSIX.q)
  EndIf
  
  ProcedureReturn POSIX.q
EndProcedure
Example:

Code: Select all

Time.q=Date()
Debug Time
Time=Time_POSIXToFileTime(Time)
Debug Time
Time=Time_FileTimeToPOSIX(Time)
Debug Time

Debug FormatDate("%mm/%dd/%yyyy %hh:%ii:%ss",Time)
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Re: Win32 FILETIME to POSIX and back

Post by Rescator »

The code is broken.
Try this on x86, then try it on x64:

Code: Select all

EnableExplicit

Procedure.q Time_FileTimeToPOSIX(FileTime.q, LocalTime.b=#False)
  If LocalTime
    FileTimeToLocalFileTime_(@FileTime.q,@FileTime.q)
  EndIf
 
  FileTime.q=(FileTime.q-116444736000000000)/10000000
 
  ProcedureReturn FileTime.q
EndProcedure

Define ft.q,rt.q

ft=16

rt=Time_FileTimeToPOSIX(ft)

Debug rt
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Re: Win32 FILETIME to POSIX and back

Post by Mistrel »

I get the exact same result, "-11644473599" when testing your example on Win7 x64 and WinXP x32. Also, your example doesn't make sense because "16" occurs before the UNIX epoc.
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Re: Win32 FILETIME to POSIX and back

Post by Rescator »

I did that to test the robustness of the code.
Both x64 and x86 should display the same, even if the time/date is extreme, the same error should happen in both.
So the issue here is that the x86 and x64 runtimes are inconsistent in behavior, imagine if that happens with "valid" dates during date comparisons?

I stumbled on this some time ago trying to do the same (I ended up using a different date system), the issue is that 64bit int is handled differently on x64 and x86,
so a program who's calculations seem ok on x86 might be wrong on x64, or vice versa. And thus can easily lead to "phantom bugs".
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Re: Win32 FILETIME to POSIX and back

Post by Mistrel »

I still don't follow you. You're dipping into undefined behavior that neither FILETIME or POSIX time was meant to handle. These are cases that the programmer should check for. Or my functions should return an error if the date is "invalid" according to the epoc.
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Re: Win32 FILETIME to POSIX and back

Post by Rescator »

*sigh* ok. try this then.

Try it in x86 and in x64.
Here the full test fails on both, but not only that, the values are different between the x86 and x64 as well.
So not only is there a flaw in the math, but the math is flawed in different ways depending on which architecture it's compiled on.

PS! On x86 the 64bit int isn't really a "normal" 64bit int, only on x64 is the 64bit int an actual 64bit int.

Code: Select all

;/ FILETIME is a 64-bit unsigned integer representing the number of
;/ 100-nanosecond intervals since January 1, 1601 UNIX timestamp is number
;/ of seconds since January 1, 1970.
;/ 116444736000000000 = 10000000 * 60 * 60 * 24 * 365 * 369 + 89 leap days

;/ POSIX time is in milliseconds
Procedure.q Time_FileTimeToPOSIX(FileTime.q, LocalTime.b=#False)
  If LocalTime
    FileTimeToLocalFileTime_(@FileTime.q,@FileTime.q)
  EndIf

  FileTime.q=(FileTime.q-116444736000000000)/10000000

  ProcedureReturn FileTime.q
EndProcedure

;/ FileTime is in the 100s of nanoseconds
Procedure.q Time_POSIXToFileTime(POSIX.q, LocalTime.b=#False)
  POSIX.q=(POSIX.q*10000000)+116444736000000000

  If LocalTime
    LocalFileTimeToFileTime_(@POSIX.q,@POSIX.q)
  EndIf

  ProcedureReturn POSIX.q
EndProcedure

Time=ParseDate("%mm/%dd/%yyyy %hh:%ii:%ss","01/01/1970 00:00:01")
Debug FormatDate("%mm/%dd/%yyyy %hh:%ii:%ss",Time)
Debug Time

Time=Time_POSIXToFileTime(Time)
Debug Time

Time=Time_FileTimeToPOSIX(Time)
Debug Time

Debug FormatDate("%mm/%dd/%yyyy %hh:%ii:%ss",Time)

Debug ""

Time=ParseDate("%mm/%dd/%yyyy %hh:%ii:%ss","01/19/2038 03:14:07")
Debug FormatDate("%mm/%dd/%yyyy %hh:%ii:%ss",Time)
Debug Time

Time=Time_POSIXToFileTime(Time)
Debug Time

Time=Time_FileTimeToPOSIX(Time)
Debug Time

Debug FormatDate("%mm/%dd/%yyyy %hh:%ii:%ss",Time)
On x64 it gives:
01/01/1970 00:00:01
1
-717324288
1
01/01/1970 00:00:01

01/19/2038 03:14:07
2147483647
21474835752675712
2147483647
01/19/2038 03:14:07
And Time_FileTimeToPOSIX() is clearly broken.

And on x86 it gives:
01/01/1970 00:00:01
1
-707324288
1240428218
04/22/2009 19:23:38

01/19/2038 03:14:07
2147483647
-727324288
1240428216
04/22/2009 19:23:36
Not only is it broken, but it's inconsistent in it's erm. brokenness as I pointed out previously.

And even when trying to improve the example by properly "casting" the test variables like this:

Code: Select all

;/ FILETIME is a 64-bit unsigned integer representing the number of
;/ 100-nanosecond intervals since January 1, 1601 UNIX timestamp is number
;/ of seconds since January 1, 1970.
;/ 116444736000000000 = 10000000 * 60 * 60 * 24 * 365 * 369 + 89 leap days

;/ POSIX time is in milliseconds
Procedure.q Time_FileTimeToPOSIX(FileTime.q, LocalTime.b=#False)
  If LocalTime
    FileTimeToLocalFileTime_(@FileTime.q,@FileTime.q)
  EndIf
 
  FileTime.q=(FileTime.q-116444736000000000)/10000000
 
  ProcedureReturn FileTime.q
EndProcedure

;/ FileTime is in the 100s of nanoseconds
Procedure.q Time_POSIXToFileTime(POSIX.q, LocalTime.b=#False)
  POSIX.q=(POSIX.q*10000000)+116444736000000000
 
  If LocalTime
    LocalFileTimeToFileTime_(@POSIX.q,@POSIX.q)
  EndIf
 
  ProcedureReturn POSIX.q
EndProcedure

Define Timeq.q,Timel.l

Timel=ParseDate("%mm/%dd/%yyyy %hh:%ii:%ss","01/01/1970 00:00:01")
Debug FormatDate("%mm/%dd/%yyyy %hh:%ii:%ss",Timel)
Debug Timel

Timeq=Time_POSIXToFileTime(Time)
Debug Timeq

Timel=Time_FileTimeToPOSIX(Timeq)
Debug Timel

Debug FormatDate("%mm/%dd/%yyyy %hh:%ii:%ss",Timel)

Debug ""

Timel=ParseDate("%mm/%dd/%yyyy %hh:%ii:%ss","01/19/2038 03:14:07")
Debug FormatDate("%mm/%dd/%yyyy %hh:%ii:%ss",Timel)
Debug Timel

Timeq=Time_POSIXToFileTime(Timel)
Debug Timeq

Timel=Time_FileTimeToPOSIX(Timeq)
Debug Timel

Debug FormatDate("%mm/%dd/%yyyy %hh:%ii:%ss",Timel)
The x86 and x64 is still messed up and inconsistent (although making slightly more sense)

On x64 it gives:
01/01/1970 00:00:01
1
-717324288
0
01/01/1970 00:00:00

01/19/2038 03:14:07
2147483647
21474835752675712
2147483647
01/19/2038 03:14:07
On x86 it gives:
01/01/1970 00:00:01
1
116444736000000000
0
01/01/1970 00:00:00

01/19/2038 03:14:07
2147483647
137919572470000000
2147483647
01/19/2038 03:14:07
If it worked properly the code should return the following on both the x64 AND x86:
01/01/1970 00:00:01
1
116444736100000000 (I believe this is correct, I didn't check if the FILEMTIME values should exactly be this though, I might have placed the "1" wrong.)
1
01/01/1970 00:00:01

01/19/2038 03:14:07
2147483647
21474835752675712 (and shouldn't there be an extra digit or something here? Surely2038 should be a larger/longer number 64bit number than 1970, right?
2147483647
01/19/2038 03:14:07
Post Reply