Обработка летнего времени в DateDiff () в MS Access? - PullRequest
3 голосов
/ 25 марта 2010

Я полностью осознаю неспособность DateDiff () справляться с проблемами перехода на летнее время. Поскольку я часто использую его для сравнения количества часов или дней между двумя датами с интервалом в несколько месяцев, мне нужно написать решение для обработки DST. Это то, что я придумал, функция, которая сначала вычитает 60 минут из значения даты и времени, если оно попадает в диапазоны дат, указанные в локальной таблице (LU_DST). Таким образом, использование будет:

 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. Я посетил и импортировал файл BAS http://www.cpearson.com/excel/TimeZoneAndDaylightTime.aspx. Я потратил на его чтение не менее часа, и, хотя он может хорошо выполнять свою работу, я не могу понять, как его изменить мои потребности. Но если у вас есть ответ с использованием его структур данных, я посмотрю.
  2. Часовые пояса не являются проблемой, поскольку это все местное время.

Редактировать

Спасибо за решения, HansUp и Fenton, но, возможно, я вижу ошибку в WbemScripting:

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

возвращается 7.11.2009 6:15:00

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

возвращается 8.11.2009 19:15:00

(Я живу в GMT-05: 00). Каждый веб-сайт, который я могу найти, говорит, что DST-переключатель в ноябре 2009 года был 1-го, а не 7-го. Почему это говорит иначе?

Ответы [ 4 ]

2 голосов
/ 26 марта 2010

Это уточнение решения @ 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).

1 голос
/ 25 марта 2010

Предупреждение : Приведенный ниже метод не возвращает правильные результаты для переходов на летнее время в США за 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 
0 голосов
/ 16 марта 2017

Преобразование в UTC для работы с датами - это решит вашу проблему летнего времени, а также любую будущую проблему с часовым поясом, которая может у вас возникнуть.

Возьмите код, который InteXX разместил по вопросу ниже, и вставьте его в новый модуль: Как получить текущую дату и время в формате UTC из макроса Excel VBA

Для меня это чудо в Microsoft Project.

Редактировать: Извините, я хотел дать вам это предупреждение и забыл; не используйте GMT, используйте UTC - https://www.timeanddate.com/time/gmt-utc-time.html

0 голосов
/ 26 марта 2010

(Оригинальный постер здесь)

Спасибо за помощь, HansUp и Fenton. Я закончил тем, что использовал оба ваших предложения, чтобы сделать функцию, основанную на Lotus Notes, для конвертации в GMT для меня.

Естественно, он почти такой же глючный, как и API-интерфейс wbemscripting, но я нашел обходной путь для одной ошибки, а другой не подходит (глючит для дат 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, поэтому он будет бесполезен, если у вас его нет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...