【问题标题】:Daylight Savings Handling in DateDiff() in MS Access?MS Access 中 DateDiff() 中的夏令时处理?
【发布时间】:2017-03-17 00:18:20
【问题描述】:

我完全意识到 DateDiff() 无法处理夏令时问题。由于我经常使用它来比较相隔几个月的 2 个日期时间之间的小时数或天数,因此我需要编写一个解决方案来处理 DST。这就是我想出的,如果日期时间值落在本地表 (LU_DST) 中指定的日期范围内,则该函数首先从日期时间值中减去 60 分钟。因此,用法是:

 datediff("n",Conv_DST_to_Local([date1]),Conv_DST_to_Local([date2]))

我的问题是:有没有更好的方法来处理这个问题?我会做出一个疯狂的猜测,我不是第一个提出这个问题的人。这似乎是应该添加到核心参考库之一的东西。有没有办法让我访问我的系统时钟来询问 DST 是否在某个日期和时间生效?

 Function Conv_DST_to_Local(X As Date) As Date
    Dim rst As DAO.Recordset
    Set rst = CurrentDb.OpenRecordset("LU_DST")
    Conv_DST_to_Local = X
    While rst.EOF = False
        If X > rst.Fields(0) And X < rst.Fields(1) Then Conv_DST_to_Local = DateAdd("n", -60, X)
        rst.MoveNext
    Wend
End Function

注意事项

  1. 我访问并导入了http://www.cpearson.com/excel/TimeZoneAndDaylightTime.aspx的BAS文件。我现在至少花了一个小时阅读它,虽然它可能做得很好,但我不知道如何根据我的需要对其进行修改。但是,如果您使用他的数据结构有答案,我会看看。
  2. 时区不是问题,因为这都是当地时间。

编辑

感谢 HansUp 和 Fenton 的解决方案,但我可能在 WbemScripting 中看到了一个错误:

 Debug.Print Conv_date_to_Utc(#11/07/2009 02:15 PM#)

返回 2009 年 11 月 7 日下午 6:15:00

 Debug.Print Conv_date_to_Utc(#11/08/2009 02:15 PM#)

返回 2009 年 11 月 8 日晚上 7:15:00

(我住在 GMT-05:00)。我能找到的每个网站都说 2009 年 11 月的夏令时转换是在 1 日,而不是 7 日。为什么会这样说?

【问题讨论】:

  • 还要考虑日期/时间都在 DST 事件两侧的情况。也就是同年的一月和十二月。或者相隔两年的一月和十二月。
  • 好主意,但我的解决方案和 HansUp 的解决方案都绕过了这个问题,分别处理 2 个日期时间,根据需要从两者中减去额外的小时,或从两者中减去一个小时,或两者都不减去。
  • WbemScripting 似乎也认为 DST 于 2009 年 3 月 15 日开始,而不是 3 月 8 日。很抱歉,它不可靠。
  • MS Access 并不适合专业开发人员。抱歉.. 它没有这个功能,SQL Server 2012 刚刚有了很多新的时区功能!

标签: ms-access vba


【解决方案1】:

这是对@HansUp 解决方案的改进,但如果您多次调用它会更有效,因为它不会在每次调用时重新初始化和关闭外部库:

  Public Function VT_DATE_to_UTC(ByVal pDate As Date, _
       Optional bolCleanup As Boolean) As Date
    Static odateTime As Object

    If odateTime Is Nothing Then
       Set odateTime = CreateObject("WbemScripting.SWbemDateTime")
    ElseIf bolCleanup
       Set odateTime = Nothing
       Exit Function
    End If
    odateTime.SetVarDate pDate, True
    VT_DATE_to_UTC = odateTime.GetVarDate(False)
  End Function

这将在第一次调用静态变量时对其进行初始化,然后在每次调用时重新使用该对象,直到您使用设置为 True 的可选参数调用它,这将关闭外部对象。

如果您在多个函数中使用此对象,则定义一个返回该对象的独立函数并使用它会更有意义:

  Public Function odateTime(Optional bolCleanup As Boolean) As Object
    Static objDateTime As Object

    If objDateTime Is Nothing Then
       Set objDateTime = CreateObject("WbemScripting.SWbemDateTime")
    ElseIf bolCleanup
       Set objDateTime = Nothing
       Exit Function
    End If
    Set odateTime = objDateTime 
  End Function

  Public Function VT_DATE_to_UTC(ByVal pDate As Date) As Date
    odateTime.SetVarDate pDate, True
    VT_DATE_to_UTC = odateTime.GetVarDate(False)
  End Function

使用此代码,当您第一次调用 UTC 函数时,它将初始化日期/时间对象(如果尚未初始化)。并且您可以使用 Call odateTime(True) 清理应用程序关闭例程中的对象。

【讨论】:

  • 是否包装在一个类中取决于你使用它的目的。如果您需要对一个对象进行大量初始化以使用它,则将其包装在一个类中(使用其 Initialize 事件)是一种方法。对于这样的事情,我会认为类包装器是矫枉过正的。
【解决方案2】:

警告:对于 2009 年美国的夏令时转换,以下方法不会返回正确的结果。

考虑将您的日期/时间值转换为 UTC,然后再将它们提供给 DateDiff()

Public Function VT_DATE_to_UTC(ByVal pDate As Date) As Date
    Dim odateTime As Object
    Set odateTime = CreateObject("WbemScripting.SWbemDateTime")

    odateTime.SetVarDate pDate, True

    VT_DATE_to_UTC = odateTime.GetVarDate(False)
    Set odateTime = Nothing
End Function

立即窗口中的示例:

? DateDiff("n", #2010/03/14 01:00#, #2010/03/14 03:00#)
 120 

? DateDiff("n", VT_DATE_to_UTC(#2010/03/14 01:00#), _
    VT_DATE_to_UTC(#2010/03/14 03:00#))
 60 

【讨论】:

  • +1 简明扼要。它比标准的 DateDiff 慢得多,但我可以使用它。
  • 如果你每次调用函数时不初始化外部库,它可能会加快速度——使你的对象变量 STATIC: Static odateTime As Object——只有当它是 Nothing (您可能还需要完整的拆解代码,即一个可选参数,告诉函数将对象变量设置为 Nothing)。
【解决方案3】:

转换为 UTC 以处理日期 - 这将解决您的 DST 问题以及您可能遇到的任何未来时区问题。

获取 InteXX 在以下问题中发布的代码并将其粘贴到新模块中: How to get the current datetime in UTC from an Excel VBA macro

这在 Microsoft Project 中为我创造了奇迹。

编辑:对不起,我本来想给你这个警告的,但忘了;不要使用 GMT,使用 UTC - https://www.timeanddate.com/time/gmt-utc-time.html

【讨论】:

  • 当时我发现了一个基于 Lotus Notes 的解决方案对我有用,但我希望这对其他人也有用。并感谢 GMT 与 UTC 的 FYI。
【解决方案4】:

(此处为原始海报)

感谢 HansUp 和 Fenton 的帮助。我最终使用了您的两个建议来创建一个依赖 Lotus Notes 为我转换为 GMT 的函数。

当然,它几乎与 wbemscripting API 一样存在错误,但我针对一个错误制定了解决方法,而另一个错误则不相关(在 2006 年及更早的日期出现错误)。

'Note: 1 minute is added to the NtDt to ensure the time component is created.  It is later subtracted.
Function ToGMT(ByVal X As Date) As Date
   Static NtSession As NotesSession
   If NtSession Is Nothing Then
       Set NtSession = New NotesSession
       NtSession.Initialize
   End If

   'Adjust for midnight
   Dim MidAdj As Boolean
   If TimeValue(X) = "12:00:00 AM" Then
       X = DateAdd("n", 1, X)
       MidAdj = vbTrue
   End If

   Dim NtDt As New NotesDateTime
   Set NtDt = NtSession.CreateDateTime(X)
   NtDt.ConvertToZone 5, True
   If MidAdj Then
      ToGMT = DateAdd("n", -1, CDate(NtDt.LSGMTTime))
   Else
      ToGMT = CDate(NtDt.LSGMTTime)
   End If
End Function

编辑:这依赖于 Lotus Notes 库,因此如果您没有它,它将没有用处。

【讨论】:

    猜你喜欢
    • 2019-12-16
    • 2015-08-14
    • 2013-03-18
    • 2022-07-20
    • 2019-09-29
    • 2012-11-21
    • 2018-04-18
    • 2021-03-09
    • 2015-07-16
    相关资源
    最近更新 更多