Precision loss when converting Variant to DateTime
<< Back
When converting from Variant to DateTime, the millisecond part will get rounded (not truncated, but rounded)
Use UI
// Variant will now have millisecond!
Function DateTimeToVariant Global DateTime dt Returns Variant
Variant vt
Real rTime rMinSec
Boolean bOK
Move (DateGetMilliSecond(dt)) to rMinSec
Move (rMinSec / Real(86400000)) to rMinSec
Move 0 to rTime
Move dt to vt
Move (MemCopy(AddressOf(rTime), AddressOf(vt) + 8, 8)) to bOK
If (rTime < 0) Move (rTime - rMinSec) to rTime
Else Move (rTime + rMinSec) to rTime
Move (MemCopy(AddressOf(vt) + 8, AddressOf(rTime), 8)) to bOK
Function_Return vt
End_Function // DateTimeToVariant
Procedure Test
DateTime dt
Variant vt
Move (CurrentDateTime()) to dt
Move (DateSetMillisecond(dt, 999)) to dt
Showln "Before " dt // Before 9/7/2021 7:17:32.999 AM
Move (DateTimeToVariant(dt)) to vt // Use the new global function from the previous article.
Showln "Rounded (up) " vt // Rounded (up) 9/7/2021 7:17:33 AM
Move (DateSetMillisecond(dt, 499)) to dt
Showln "Before " dt // Before 9/7/2021 7:17:32.499 AM
Move (DateTimeToVariant(dt)) to vt // Use the new global function from the previous article.
Showln "Rounded (down) " vt // Rounded (down) 9/7/2021 7:17:32 AM
InKey FieldIndex
End_Procedure
Send Test
DAW uses the API function
VariantTimeToSystemTime, which rounds the millisecond part. I would consider that a bug, or at least a gotcha, because it is a reasonable
assumption that you don't lose precision when you move data from and to a Variant data type. If the API call doesn't do what is expected, then
DAW should write their own conversion routine for Variant to DateTime (which isn't difficult at all). Anyway, as an academic exercise, how would
you keep the millisecond part when converting a Variant to DateTime?
Function VariantToDateTime Global Variant vt Returns DateTime
Real rTime rDate rDateTime
DateTime dt
Boolean bOK
Integer iTime iDate
Move 0 to rDateTime
// Grab the "REAL" data from the variant
Move (MemCopy(AddressOf(rDateTime), AddressOf(vt) + 8, 8)) to bOK
// Strip out the "time" (the decimal) portion from "REAL" by moving a "REAL" into "Integer"
Move rDateTime to iDate
Move iDate to rDate
Move (Abs(rDateTime - rDate)) to rTime
Move (MemCopy(AddressOf(vt)+8, AddressOf(rDate), 8)) to bOK
// "dt" should only contain the "date" portion.
Move vt to dt
// Do some simple math to convert the time portion to DateTime
Move (Round(rTime * 86400000.0)) to iTime
Move (DateSetMilliSecond(dt, Mod(iTime, 1000))) to dt
Move (iTime / 1000) to iTime
Move (DateSetSecond(dt, Mod(iTime, 60))) to dt
Move (iTime / 60) to iTime
Move (DateSetMinute(dt, Mod(iTime, 60))) to dt
Function_Return (DateSetHour(dt, iTime / 60))
End_Function // VariantToDateTime
This code is purely for academic exercise. In real life, DAW should implement this inside the VDF runtime instead.