在任意时区之间转换

时间:2018-01-29 13:47:13

标签: excel vba excel-vba timezone datetime-conversion

我试图找到一种简单而强大的方法来在任意时区之间转换时间。

更新1:我不明白为什么会被投票。我做了我的研究并探索了可能性。这个问题并不像看起来那么简单。如果您认为该函数看起来像bTime = aTime + 3,那么请重新考虑。时区和DST处于不断变化的状态。阅读本文以供参考:list of pending / proposed timezone changes。请注意,某些国家/地区实际上正在更改其时区,而不仅仅是DST设置!巴西将他们改变时钟的日期改为冬季!所有这些更改都会很快打破静态查找表。

更新2:我没有考虑快速而肮脏的黑客攻击,我可以自己想出来,非常感谢你。我没有得到报酬而忘记它的报酬;我想创建一个功能,一旦其他人可以安全地用于不同的内部项目,而不需要维护噩梦。 已知偶尔更改的硬编码常量是一个非常糟糕的软件设计(想想由一段非常非常旧的代码引起的Y2K错误)。

更新3 :此数据库看起来不错(虽然我不确定它是否足够稳定):https://timezonedb.com/api 他们甚至有一个TZ转换呼叫 - 正是我需要的! 我可能会尝试从VBA解析XML并分享我的结果。

这:http://www.cpearson.com/excel/TimeZoneAndDaylightTime.aspx仅解释如何转换我的(当前)TZ和另一个TZ。

这两篇SO文章(Getting Windows Time Zone Information (C++/MFC)How do you get info for an arbitrary time zone in Windows?)谈论从注册表中获取信息。 这听起来有点过于复杂和耗时;此外,Windows似乎将TZ存储在他们的全名" (例如(UTC-08:00) Pacific Time (US & Canada))和我更喜欢使用缩写(例如EDT)来引用TZ。 更新:此外,依赖Windows注册表也可能不安全:不同的用户可能有不同的版本,有些可能不是最新的。这意味着由两个人运行的报告可能会提供两种不同的结果!

还有一种更简单的方法吗?编写查询表可能会有一段时间,但当政府决定取消DST或更改其他任何内容时,它将被破坏。

也许从互联网上获取TZ列表并解析它?..这样安全吗?..

2 个答案:

答案 0 :(得分:4)

https://timezonedb.com/references/convert-time-zone的API确实是在两个位置之间获得正确的全球时间时区时区偏移量的好地方< / strong>,考虑到过去/未来的夏令时变化。

建议的仅指定时区缩写的方法(例如&#34;将PST转换为EST&#34; )存在的问题是此API将您的区域逐字地< / em>,即使它们不正确。

因此,如果多伦多目前在EDT,但您指定了EST,那么您可能会得到错误的时间。使用&#34;全名&#34;像(UTC-08:00) Pacific Time (US & Canada)一样会遇到同样的问题。

一种方法是指定时区名称,例如America/Vancouver(列为here),或者使用适当的参数指定城市,国家和/或地区名称。

我写了一个函数来解决它,但它只适用于某些国家(见下文)。

去年万圣节晚上11点11分在温哥华时间多伦多几点钟?

  

http://api.timezonedb.com/v2/convert-time-zone?key=94RKE4SAXH67&from=America/Vancouver&to=America/Toronto&time=1509516660

结果(默认为XML,但JSON也可用。)

<result>
    <status>OK</status>
    <message/>
    <fromZoneName>America/Vancouver</fromZoneName>
    <fromAbbreviation>PDT</fromAbbreviation>
    <fromTimestamp>1509516660</fromTimestamp>
    <toZoneName>America/Toronto</toZoneName>
    <toAbbreviation>EDT</toAbbreviation>
    <toTimestamp>1509527460</toTimestamp>
    <offset>10800</offset>
</result>

以编程方式获取数据:

您需要决定很多选项和查找方法,但这是使用VBA函数的一个示例:

  

温哥华和温哥华之间的时差是多少?柏林圣诞节那天?

     

输入时间: 2018-12-25 00:00:00 =温哥华本地Unix时间1545724800

Function GetTimeZoneOffsetHours(fromZone As String, _
            toZone As String, UnixTime As Long) As Single

    Const key = "94RKE4SAXH67"
    Const returnField = "<offset>"
    Dim HTML As String, URL As String
    Dim XML As String, pStart As Long, pStop As Long

    URL = "http://api.timezonedb.com/v2/convert-time-zone?key=" & key & _
        "&from=" & fromZone & "&to=" & toZone & "&time=" & UnixTime
    With CreateObject("MSXML2.XMLHTTP")
        .Open "GET", URL, False
        .Send
        XML = .ResponseText
    End With

    pStart = InStr(XML, returnField)
    If pStart = 0 Then
        MsgBox "Something went wrong!"
        Exit Function
    End If

    pStart = pStart + Len(returnField) + 1
    pStop = InStr(pStart, XML, "</") - 1
    GetTimeZoneOffsetHours = Val(Mid(XML, pStart, pStop - pStart)) / 60
End Function


Sub testTZ()
    Debug.Print "Time Zone Offset (Vancouver to Berlin) = " & _
        GetTimeZoneOffsetHours("America/Vancouver", _
        "Europe/Berlin", 1545724800) & " hours"
End Sub
  

Unix / UTC时间戳:

     

Unix时间定义为&#34; 自1970年1月1日星期四00:00:00协调世界时(UTC)以来经过的秒数。&#34; < / p>      

您可以在Unix和/或UTC或本地时间之间转换时间: epochconverter.com ...该网站还包含多种编程语言的转换公式。

     

例如,在Excel中将 Unix时间转换为GMT / UTC 的表格是:

=(A1 / 86400) + 25569

您还可以下载静态文件(SQLCSV格式)here,而不是加密API,该页面也有示例查询。 然而谨慎使用:使用夏令时更容易犯错误(如上所述)。

我做了一个虚拟账户来获得&#34;演示&#34;在示例中使用,但您应该获得自己的(免费)密钥以供长期使用。 (如果它被过度使用锁定,我不负责任!)

一个好的替代时区API是 Google Maps Time Zone API 。不同之处在于您指定了Latitude&amp;经度。 它似乎工作很好没有密钥你需要register来获取密钥。

  

6月1日白宫的时区偏差是什么?

     

https://maps.googleapis.com/maps/api/timezone/json?location=38.8976,-77.0365&timestamp=1527811200&key={YourKeyHere}

<强>结果

{
   "dstOffset" : 0,
   "rawOffset" : -18000,
   "status" : "OK",
   "timeZoneId" : "America/Toronto",
   "timeZoneName" : "Eastern Standard Time"
}
  

偏移量为-18000秒(-5小时)。

确定夏令时何时生效

以下是我放在一起的功能,所以我可以信任&#34;我从不同的API获得的夏令时(DST)值,但是(正如其他人所讨论的)规则没有模式 plus 不断变化的国家/地区,甚至是城镇的某些部分。世界,所以这只适用于以下国家:

  • DST从每年3月的第二个星期日开始
  • DST在每年11月的第一个星期日结束

适用的国家是巴哈马,百慕大,加拿大,古巴,海地,圣皮埃尔和美国。 来源: Daylight saving time by country **)

Function IsDST(dateTime As Date) As Boolean

    'Returns TRUE if Daylight Savings is in effect during the [dateTime]
    'DST Start (adjust clocks forward) Second Sunday March at 02:00am
    'DST end (adjust clocks backward) First Sunday November at 02:00am

    Dim DSTStart As Date, DSTstop As Date

    DSTStart = DateSerial(Year(dateTime), 3, _
        (14 - Weekday(DateSerial(Year(dateTime), 3, 1), 3))) + (2 / 24)
    DSTstop = DateSerial(Year(dateTime), 11, _
        (7 - Weekday(DateSerial(Year(dateTime), 11, 1), 3))) + (2 / 24)
    IsDST = (dateTime >= DSTStart) And (dateTime < DSTstop)

End Function

以及我如何使用函数IsDST *:

的几个例子
Public Function UTCtoPST(utcDateTime As Date) As Date
    'Example for 'PST' time zone, where Offset = -7 during DST, otherwise if -8

    If IsDST(utcDateTime) Then
        UTCtoPST = utcDateTime - (7 / 24)
    Else
        UTCtoPST = utcDateTime - (8 / 24)
    End If

End Function


Function UTCtimestampMStoPST(ByVal ts As String) As Date
    'Example for 'PST', to convert a UTC Unix Time Stamp to 'PST' Time Zone

    UTCtimestampMStoPST = UTCtoPST((CLng(Left(ts, 10)) / 86400) + 25569)

End Function
  

* 请注意,函数IsDST不完整:它没有考虑IsDST在凌晨2点实际生效之前/之后的小时数。特别是在春天,时钟从01:59标准时间的最后一刻向前跳跃到03:00 DST,那天有23个小时,而在秋天,时钟从01:59夏令时的最后时刻向后跳跃到01:00标准时间,重复那个小时,那天有25个小时 ... 但是 ,如果有人想要添加该功能来更新功能,请随意!我在最后一部分缠绕我的头部时遇到了麻烦,并没有立即需要那么详细的水平,但我确信其他人会很感激!

最后,还有一个替代方案是我用来为各种目的轮询当前/未来/历史天气数据的API - 并且恰好提供时区偏移 - 是DarkSky。

按纬度/经度查询并且是免费的(每天最多1000个电话),并提供&#34;超准确的天气数据&#34; (更多信息在美国,它预测天气下降到分钟平方码! - 但我已经看到加拿大西海岸加拿大不可预测的非常准确!)

响应仅限JSON,最后一行是时区偏移与UTC / GMT 时间。

  

DarkSky示例调用:

     

https://api.darksky.net/forecast/85b57f827eb89bf903b3a796ef53733c/40.70893,-74.00662

     

它说它应该在Stack Overflow的总部接下来的60个小时下雨。 ☂

     

...但我不知道,到目前为止,这看起来像是一个非常美好的一天! ☀

     

SO Head Office w/ Flag   <子> (flag)

答案 1 :(得分:2)

我担心与时区有任何关系绝不是一项简单的任务(询问任何网页设计师,他们会说这是一项巨大的挑战)

有两种方法可以解决您的问题

1)简单方法 - 创建一个所有其他工作簿链接到的中央列表。这可以保存在SharePoint或共享驱动器上,然后您只需更新此表

即可

2)困难的方法 - 使用网站API获取最新的时区数据。 https://www.amdoren.com/是一个很好的网站,您可以通过注册获得免费的API密钥。唯一的问题是你必须从网站解析Json文件。这并不容易,但如果你google&#34; vba parse json&#34;你会发现一些解决方案(它通常需要导入一些库并使用其他人的代码作为起点)

希望您找到合适的解决方案,如果您这样做可能值得分享,因为我确定会有其他人遇到同样的问题。