使用VBA从UTC到PST / PDT / CET和CEST的转换时间

时间:2019-04-27 11:39:58

标签: vba

我有一个基于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代码。 有任何建议吗?我的问题是如何通过相反方式将夏季时间切换为冬季时间。

1 个答案:

答案 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:

  • 在3月的最后一个星期日({@ {1}})世界标准时间01:00(即CEST开始的时间)之前
  • 或在(<)10月的最后一个星期日(世界标准时间开始时)(即CET开始时)

我不清楚您的源数据是什么样的。 (看起来两个日期时间之间用空格分隔,这意味着每一行实际上都包含一个字符串。如果为true,则可能需要将字符串沿分隔空间拆分为两个字符串,然后将两个字符串转换为两个日期时间。这可以通过VBA中的>=LEFTRIGHT函数来完成。)

举一个例子,我在工作表DATEVALUE的范围A1:A17520中以半小时为间隔的日期时间填充了日期时间(类似于您所显示的内容):

Before code

然后,我使用下面的代码将我的值转换为A列(并将转换后的值放在同一张纸的B列中):

"Sheet1"

这给了我这样的东西:

After code


需要注意的是,由于以下几个原因,这种方法效率很低:

  • 每次调用函数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 时,都会重新计算变量startOfCESTinUTCendOfCESTinUTC(在我的情况下约为17.5k次)。由于我们所有的日期时间都是同一年,因此ConvertUtcDateTimeToCETorCESTstartOfCESTinUTC的值不会改变(因此实际上变量endOfCESTinUTCstartOfCESTinUTC应该只确定一次,在endOfCESTinUTC循环之前)。
  • 如果您事先知道并确定您的数据是按半小时间隔排序的,则可以进行一些数学运算,并确定CEST必须在哪一行开始和结束。有了这些知识,您不再需要检查For循环内的日期时间是CET还是CEST。如果您使用三个For循环(循环1:第一行For行CET结束,循环2:行CEST开始于to行CEST结束,循环3:行CET开始于to最后一行),您可以将所有值转换为CET(循环1,循环3)和CEST(循环2),而无需实际检查该值是什么。这使代码更专业(更快),但重用性也较低。

(相对)现代计算机速度较快。上面的代码在我的计算机上用不到2秒的时间为我转换了17.5k日期时间。您可能还需要检查代码是否正确处理了CET和CEST的边界。