【问题标题】:Error in Date Diff in .net.net 中的日期差异错误
【发布时间】:2015-02-22 09:53:58
【问题描述】:

我使用下面的代码sn-p

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

其中 pDate 是用户输入的日期及其格式 mm/dd/yyyy 例如#12/24/2014# 这会正确检索 unix 日期。但是在一台特定机器上,输出比所需日期少一秒。 那是转换结果在前一个日期时的 unix 时间戳。 例如,2004 年 12 月 18 日星期六 23:59:59 GMT 当所需结果为
2004 年 12 月 19 日星期日 00:00:00 GMT

【问题讨论】:

  • 我个人建议从另一个中减去一个DateTime,并使用生成的TimeSpanTotalSeconds 属性。 DateDiff 之类的东西更多是为了向后兼容 VB6,而不是作为 .NET 中的现代处理方式。也就是说,你的问题有点不清楚——结果不是DateTime,它是一个整数......那么你怎么能说结果是一个特定的DateTime?如果您能展示一个简短但完整的程序来演示问题(在那台机器上),那将会很有帮助。
  • 你是对的,输出是一个整数,它给出了 unixtimestamp。对于特定情况,检索到的 unix 时间戳为 1418860799,这可以转换为日期。您可以使用以下链接 onlineconversion.com/unix_time.htm 它导致 17-dec-2014 23:59:59 但是所需的时间戳是 1418860800 它导致 18- dec-2014 00:00:00 因此观察到 1 秒的延迟
  • 对。这更有意义,您应该在问题中包含该信息,以及显示它的简短但完整的程序。

标签: .net


【解决方案1】:

是的,这在技术上是可行的。 DateDiff() 使用 Math.Round() 从 TimeSpan.TotalSeconds 生成返回值。像这样的浮点数学很容易受到机器上运行的行为不端代码的影响,这些代码会改变 FPU 控制字。控制字中的两个设置会导致这样的错误,即精度设置和舍入模式。

此类代码可以通过多种方式感染您的程序。通常的麻烦制造者是打印机驱动程序,一些 Hewlett Packard 驱动程序已知有这个错误。或者是一个 shell 扩展,它们会通过 OpenFileDialog 加载到您的进程中。更隐蔽的方式是 DirectX,它可以改变精度设置。众所周知,Microsoft ACE 数据提供程序的某个版本会破坏舍入模式。

到目前为止,解决此问题的最佳方法是重新映像机器。如果机器上没有可用的调试器,则很难找到 DLL。并且它可能导致其他程序以非常难以诊断的方式行为异常。如果您有调试器,This post 有调试提示。

但您可能只是想摆脱这个错误。您可以通过使用此代码避免浮点数学来做到这一点,将其放入模块中:

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

演示此问题的示例程序:

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

这为您提供了另一种解决方法,您可以使用 SetFpuRoundingMode(RoundingMode.Near) 来恢复 FPU。但是,您必须弄清楚该调用的确切位置。

【讨论】:

  • 有没有办法检查FPU控件是否被改动过?
  • .NET程序中_controlfp(0, 0)的返回值应该是&H8001F。
  • 请注意,只有 Direct3D 9 和之前的版本会更改 x87 FP 控制字。 Direct3D 10 及更高版本从不这样做。在 Direct3D 9 中,您可以使用 D3DCREATE_FPU_PRESERVE 来防止这种行为。全局更改 fp 控制字是一般的不良行为,很可能会导致问题。有关详细信息,请参阅此article
猜你喜欢
  • 1970-01-01
  • 2014-01-17
  • 2013-03-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多