Время конвертирования из UTC в PST / PDT / CET и CEST с VBA - PullRequest
0 голосов
/ 27 апреля 2019

У меня есть клоун (Col. A) времени и даты в течение одного года на основе времени UTC в качестве следующего формата (фактически у меня есть 30-минутные интервалы) с первого года до конца года:

01.01.2019 00:00
01.01.2019 00:30
.
.
.

31.12.2019 11:00
31.12.2019 11:30

Теперь я хочу преобразовать время из UTC в CET, когда это зимнее время, и CEST, когда это летнее время (я хочу получить информацию в Col.B). Я мог бы сделать это с помощью формулы Excel легко, но так как мой файл достаточно большой, я должен использовать код VBA. Есть ли предложение? Как я могу это сделать? Моя проблема заключается в переключении летнего времени на зимнее через наоборот.

1 Ответ

0 голосов
/ 27 апреля 2019

Относительно самого преобразования часового пояса, Википедия говорит (https://en.wikipedia.org/wiki/Central_European_Summer_Time#Period_of_observation):

С 1996 года европейское летнее время наблюдается с 01:00 UTC. (02:00 CET и 03:00 CEST) в последнее воскресенье марта и 01:00 UTC в последнее воскресенье октября

Если я правильно читаю, дата и время могут быть CET, только если они:

  • до (<) последнего воскресенья марта в 01:00 UTC (т.е. когда начинается CEST)
  • или включен или ((1014 *)) в последнее воскресенье октября в 01:00 UTC (т.е. когда начинается CET)

Мне не ясно, на что похожи ваши исходные данные. (Похоже, что два datetime разделены пробелом, что означает, что каждая строка на самом деле содержит строку. Если это так, вам, вероятно, придется разделить строку на две строки вдоль разделительного пространства, а затем преобразовать две строки в два datetime. Вероятно, это можно сделать с помощью функций LEFT, RIGHT, DATEVALUE в VBA.)

Чтобы привести пример, я заполнил диапазон A1:A17520 моего рабочего листа "Sheet1" датами с интервалом в полчаса (аналогично тому, что вы показали):

Before code

и затем я использую приведенный ниже код для преобразования моих значений в столбец A (и помещаю преобразованные значения в столбец B того же листа):

Option Explicit

Private Sub ConvertUtcDatesInColumnAToCETorCEST()
    Dim sourceSheet As Worksheet
    Set sourceSheet = ThisWorkbook.Worksheets("Sheet1")

    Dim lastSourceRow As Long
    lastSourceRow = sourceSheet.Cells(sourceSheet.Rows.Count, "A").End(xlUp).Row

    Dim datesFromSheet() As Variant
    datesFromSheet = sourceSheet.Range("A1", sourceSheet.Cells(lastSourceRow, "A"))

    Dim outputArray() As Date
    ReDim outputArray(1 To UBound(datesFromSheet, 1), 1 To 1)

    Dim rowIndex As Long
    For rowIndex = LBound(datesFromSheet, 1) To UBound(datesFromSheet, 1)
        outputArray(rowIndex, 1) = ConvertUtcDateTimeToCETorCEST(datesFromSheet(rowIndex, 1))
    Next rowIndex

    sourceSheet.Range("B1").Resize(UBound(outputArray, 1), UBound(outputArray, 2)).Value = outputArray
End Sub

Private Function LastSundayOfSomeMonthAndYear(ByVal someMonth As Long, ByVal someYear As Long) As Date
    Dim lastDateInMonth As Date
    lastDateInMonth = Application.EoMonth(DateSerial(someYear, someMonth, 1), 0)

    Dim lastSundayInMonth As Date
    LastSundayOfSomeMonthAndYear = lastDateInMonth - Weekday(lastDateInMonth, vbSunday) + 1
End Function

Private Function ConvertUtcDateTimeToCETorCEST(ByVal someUtcDateTime As Date) As Date
    Dim startOfCESTinUTC As Date
    startOfCESTinUTC = LastSundayOfSomeMonthAndYear(someMonth:=3, someYear:=Year(someUtcDateTime)) + TimeSerial(1, 0, 0)

    Dim endOfCESTinUTC As Date
    endOfCESTinUTC = LastSundayOfSomeMonthAndYear(someMonth:=10, someYear:=Year(someUtcDateTime)) + TimeSerial(1, 0, 0)

    Dim isCET As Boolean
    isCET = someUtcDateTime < startOfCESTinUTC Or someUtcDateTime >= endOfCESTinUTC ' Not sure if end is inclusive or exclusive

    Dim hoursToAdd As Date
    If isCET Then hoursToAdd = TimeSerial(2, 0, 0) Else hoursToAdd = TimeSerial(3, 0, 0)

    ConvertUtcDateTimeToCETorCEST = someUtcDateTime + hoursToAdd
End Function

, что дает мне что-то вроде этого:

After code


Следует отметить, что этот подход довольно неэффективен по нескольким причинам:

  • Переменные startOfCESTinUTC и endOfCESTinUTC пересчитываются каждый раз, когда вызывается функция ConvertUtcDateTimeToCETorCEST (в моем случае ~ 17,5k раз). Поскольку все наши даты и время находятся в одном и том же году, значения startOfCESTinUTC и endOfCESTinUTC не изменятся (поэтому на самом деле переменные startOfCESTinUTC и endOfCESTinUTC должны быть определены только один раз, перед циклом For).
  • Если вы знаете заранее и уверены, что ваши данные отсортированы с интервалом в полчаса, вы можете выполнить некоторые математические расчеты и определить, какой ряд CEST должен начинаться и заканчиваться. С этим знанием вам больше не нужно проверять, является ли datetime CET или CEST внутри цикла For. Если вы используете три цикла For (цикл 1: первая строка to строка CET заканчивается, цикл 2: строка CEST начинается с to строка CEST заканчивается, цикл 3: строка CET начинается с to последняя строка) Вы можете преобразовать все значения в CET (цикл 1, цикл 3) и CEST (цикл 2) без фактической проверки значения. Это делает код более специализированным (и более быстрым), но также менее пригодным для повторного использования.

Современные компьютеры (относительно) быстры. Приведенный выше код преобразовал 17.5k для меня за <2 секунды на моем компьютере. Вы также можете проверить, правильно ли код обрабатывает границы CET и CEST. </p>

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