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.






Free Web Hosting