我有一个基于UTC时间的一年时间和日期的休整号(Col. A
),其格式为从一年的第一年到年底的以下格式(实际上,我有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 Formula完成此操作,但是由于文件足够大,因此必须使用VBA
代码。
有任何建议吗?我的问题是如何通过相反方式将夏季时间切换为冬季时间。
答案 0 :(得分:0)
关于时区转换本身,维基百科说(https://en.wikipedia.org/wiki/Central_European_Summer_Time#Period_of_observation):
自1996年以来,欧洲夏令时已在UTC 01:00之间进行观测 (3月的最后一个星期日,欧洲中部时间02:00和CEST 03:00),世界标准时间01:00 在十月的最后一个星期日
如果我没看错,则日期时间只能是CET:
<
)10月的最后一个星期日(世界标准时间开始时)(即CET开始时)我不清楚您的源数据是什么样的。 (看起来两个日期时间之间用空格分隔,这意味着每一行实际上都包含一个字符串。如果为true,则可能需要将字符串沿分隔空间拆分为两个字符串,然后将两个字符串转换为两个日期时间。这可以通过VBA中的>=
,LEFT
,RIGHT
函数来完成。)
举一个例子,我在工作表DATEVALUE
的范围A1:A17520
中以半小时为间隔的日期时间填充了日期时间(类似于您所显示的内容):
然后,我使用下面的代码将我的值转换为A列(并将转换后的值放在同一张纸的B列中):
"Sheet1"
这给了我这样的东西:
需要注意的是,由于以下几个原因,这种方法效率很低:
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
(在我的情况下约为17.5k次)。由于我们所有的日期时间都是同一年,因此ConvertUtcDateTimeToCETorCEST
和startOfCESTinUTC
的值不会改变(因此实际上变量endOfCESTinUTC
和startOfCESTinUTC
应该只确定一次,在endOfCESTinUTC
循环之前)。For
循环内的日期时间是CET还是CEST。如果您使用三个For
循环(循环1:第一行For
行CET结束,循环2:行CEST开始于to
行CEST结束,循环3:行CET开始于to
最后一行),您可以将所有值转换为CET(循环1,循环3)和CEST(循环2),而无需实际检查该值是什么。这使代码更专业(更快),但重用性也较低。(相对)现代计算机速度较快。上面的代码在我的计算机上用不到2秒的时间为我转换了17.5k日期时间。您可能还需要检查代码是否正确处理了CET和CEST的边界。