Относительно самого преобразования часового пояса, Википедия говорит (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"
датами с интервалом в полчаса (аналогично тому, что вы показали):
и затем я использую приведенный ниже код для преобразования моих значений в столбец 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
, что дает мне что-то вроде этого:
Следует отметить, что этот подход довольно неэффективен по нескольким причинам:
- Переменные
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>