Error in Date Diff in .net

前端 未结 1 1106
南旧
南旧 2021-01-26 04:37

I am using the following code snippet

lxDate= #1/1/1970#
GetUnixDate = CType(DateDiff(\"S\", lxDate, pDate), Int32)

where pDate is the date ent

相关标签:
1条回答
  • 2021-01-26 05:05

    Yes, this is technically possible. DateDiff() uses Math.Round() to produce the return value from TimeSpan.TotalSeconds. Floating point math like that is vulnerable to misbehaving code that runs on the machine which alters the FPU control word. Two settings in the control word can cause an off-by-one bug like this, the precision setting and the rounding mode.

    There are various ways such code can infect your program. The usual troublemakers are a printer driver, some Hewlett Packard drivers are known to have this bug. Or a shell extension, they get loaded into your process with, say, an OpenFileDialog. More obscure ways are DirectX, it can alter the precision setting. A version of the Microsoft ACE data provider is known to screw up the rounding mode.

    By far the best way to tackle this problem is to re-image the machine. The DLL can be pretty hard to find if you don't have a debugger available on the machine. And it can cause other programs to misbehave in very hard to diagnose ways. This post has debugging tips if you do have a debugger.

    But you probably just want to get the bug off your plate. You can do so by avoiding floating point math with this code, put it in a Module:

    Public Function UnixTimestamp(ByVal dt As DateTime) As Integer
        Dim span = dt.ToUniversalTime() - New DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)
        Return CInt(span.Ticks \ 10000000)
    End Function
    

    A sample program that demonstrates this problem:

    Imports System.Runtime.InteropServices
    
    Module Module1
        Sub Main()
            SetFpuRoundingMode(RoundingMode.Down)  '' or Chop
            Dim lxDate = #1/1/1970#
            Dim pDate = #12/24/2014#
            Dim unix = CType(DateDiff("S", lxDate, pDate), Int32)
            System.Diagnostics.Debug.Assert(unix = 1419379200)
        End Sub
    
        Enum RoundingMode
            Near
            Down
            Up
            Chop
        End Enum
    
        Sub SetFpuRoundingMode(mode As RoundingMode)
            _controlfp(mode << 8, &H300)
        End Sub
    
        <DllImport("msvcrt.dll", CallingConvention:=CallingConvention.Cdecl)> _
        Friend Function _controlfp(bits As Integer, mask As Integer) As Integer
        End Function
    End Module
    

    Which gives you another workaround, you could use SetFpuRoundingMode(RoundingMode.Near) to restore the FPU. Where exactly to put that call is however what you'd have to figure out.

    0 讨论(0)
提交回复
热议问题