Конвертировать между произвольными часовыми поясами
Я пытаюсь найти простой, но надежный способ конвертировать время между произвольными часовыми поясами.
ОБНОВЛЕНИЕ 1: я не понимаю, почему это было понижено. Я сделал свое исследование и изучил возможности. Эта проблема не так тривиальна, как может показаться. Если вы думаете, что функция должна выглядеть bTime = aTime + 3
, тогда, пожалуйста, пересмотрите. Часовые пояса и летнее время находятся в состоянии постоянного изменения. Прочитайте это для справки: список ожидающих / предлагаемых изменений часового пояса. Обратите внимание, что некоторые страны фактически меняют свои часовые пояса, а не только настройки DST! И Бразилия изменила дату, когда они переводят свои часы на зимнее время! Все эти изменения очень быстро сломают статическую таблицу поиска.
ОБНОВЛЕНИЕ 2: Я не смотрю на быстрый и грязный взлом, я могу придумать это сам, большое спасибо. Мне не платят, чтобы что-то написать и забыть об этом; Я хотел бы создать функцию, которая могла бы безопасно использоваться другими людьми для различных внутренних проектов без кошмара обслуживания. Жесткое кодирование констант, которые, как известно, время от времени изменяются, является очень плохим дизайном программного обеспечения (подумайте об ошибке Y2K, вызванной очень, очень старой частью кода).
ОБНОВЛЕНИЕ 3: Эта база данных выглядит хорошо (хотя я не уверен, что она достаточно стабильна): https://timezonedb.com/api У них даже есть вызов преобразования TZ - именно то, что мне нужно! Я, вероятно, попытаюсь разобрать XML из VBA и поделиться своими результатами.
Это: http://www.cpearson.com/excel/TimeZoneAndDaylightTime.aspx объясняет только, как конвертировать между моим (текущим) TZ и другим TZ.
В этих двух статьях SO ( Получение информации о часовом поясе Windows (C++/MFC) и Как получить информацию о произвольном часовом поясе в Windows?) Рассказывается о получении информации из реестра. Это звучит немного запутанно и отнимает много времени; более того, похоже, что Windows хранит TZ под своими "полными именами" (такими как (UTC-08:00) Pacific Time (US & Canada)
) и я бы лучше сослался на TZ, используя сокращения (такие как EDT). ОБНОВЛЕНИЕ: более того, полагаться на реестр Windows также может быть небезопасно: разные пользователи могут иметь разные версии, а некоторые могут не обновляться. Это означает, что отчет, подготовленный двумя людьми, может дать два разных результата!
Есть ли более простой способ, который также будет надежным? Написание справочной таблицы может работать некоторое время, но тогда оно будет нарушено, когда правительство решит отменить DST или изменить что-либо еще.
Может быть, получить список TZ из Интернета и проанализировать его?.. Это будет достаточно безопасно?..
2 ответа
API в https://timezonedb.com/references/convert-time-zone
действительно является отличным местом для получения точного всемирного времени, часового пояса и смещения часового пояса между двумя точками с учетом прошлых / будущих изменений перехода на летнее время.
Проблема с предложенным вами методом указания только сокращений часовых поясов (например, "конвертировать PST в EST") заключается в том, что этот API буквально воспринимает ваши зоны, даже если они неверны.
Так что, если Торонто в настоящее время включен EDT
но вы указываете EST
вы, вероятно, получите неправильное время. Используя "полные имена", как (UTC-08:00) Pacific Time (US & Canada)
будет иметь такую же проблему.
Обходной путь - указать имена часовых поясов, такие как America/Vancouver
(как указано здесь), либо укажите название города, страны и / или региона с соответствующими параметрами.
Я написал функцию, чтобы понять это, но это относится только к определенным странам (см. Ниже).
Сколько времени было в Торонто в последний Хэллоуин в 23: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
= Ванкуверское местное время Unix1545724800
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 определяется как " количество секунд, прошедших с 00:00:00 всемирного координированного времени (UTC), четверг, 1 января 1970 года ".
Вы можете конвертировать время между Unix и / или UTC или местным временем по адресу: https://www.epochconverter.com/... на сайте также есть формулы преобразования для нескольких языков программирования.
Например, формула для преобразования времени Unix в GMT/UTC в Excel:
=(A1 / 86400) + 25569
Вы также можете загрузить статические файлы (в SQL
или же CSV
формат) здесь вместо вызова API, и на странице также есть примеры запросов. Однако будьте осторожны: проще делать ошибки при переходе на летнее время (как упоминалось выше).
Я создал фиктивную учетную запись, чтобы использовать "демо" в примерах, но вы должны получить свой собственный (бесплатный) ключ для долгосрочного использования. (Я не несу ответственности, если он заблокирован от чрезмерного использования!)
Хорошей альтернативой API часового пояса является Google Maps Time API. Разница в том, что вы указываете широту и долготу. Вроде работает просто отлично без ключа Вам нужно будет зарегистрироваться для получения ключа.
Каким будет смещение часового пояса 1 июня в Белом доме?
https://maps.googleapis.com/maps/api/timezone/json?location=38.8976,-77.0365×tamp=1527811200&key={YourKeyHere}
Результат:
{
"dstOffset" : 0,
"rawOffset" : -18000,
"status" : "OK",
"timeZoneId" : "America/Toronto",
"timeZoneName" : "Eastern Standard Time"
}
Смещение составит -18000 секунд (-5 часов).
Определение, когда действует летнее время
Ниже приведена функция, которую я собрал, чтобы я мог "доверять" значениям перехода на летнее время (DST), которые я получал от другого API, однако (как обсуждали другие) правила не имеют шаблонов и постоянно меняются в зависимости от страны, даже города. по городам в некоторых частях мира, так что это будет работать только в странах, где:
- Летнее время начинается во второе воскресенье марта каждого года
- Летнее время заканчивается в первое воскресенье ноября каждого года
Применимыми странами являются Багамские острова, Бермудские острова, Канада, Куба, Гаити, Сен-Пьер и США. (Источник: переход на летнее время по странам **)
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
Летнее время и этот день имеет 23 часа, тогда как осенью часы отскакивают назад от последнего момента01:59
Летнее время01:00
стандартное время, повторение этого часа, и этот день имеет 25 часов... но, если кто-то хочет добавить эту функцию для обновления функции, не стесняйтесь! У меня были проблемы с тем, чтобы обернуть голову вокруг этой последней части, и мне сразу не понадобился такой уровень детализации, но я уверен, что другие оценят это!
Наконец, еще одна альтернатива - это API, который я использую для опроса текущих / будущих / исторических данных о погоде для различных целей, а также для обеспечения смещения часового пояса - это DarkSky.
Он запрашивает широту / долготу и является бесплатным (до 1000 звонков в день) и предоставляет "сверхточные данные о погоде" (особенно в США, где он прогнозирует погоду с точностью до минуты и на квадратный ярд! - но довольно точный я видел для непредсказуемого канадского западного побережья канада!)
Ответ представлен только в формате JSON, а последняя строка - смещение часового пояса в зависимости от времени UTC/GMT.
Образец вызова DarkSky:
https://api.darksky.net/forecast/85b57f827eb89bf903b3a796ef53733c/40.70893,-74.00662
В главном офисе Stack Overflow написано, что в течение следующих 60 часов должен идти дождь. ☂
... но я не знаю, похоже, это довольно хороший день! ☀
Боюсь, что-либо связанное с часовыми поясами никогда не бывает простой задачей (спросите любого веб-дизайнера, и он скажет, что это сложная задача)
Есть 2 способа решить вашу проблему
1) Простой способ - создать централизованный список, с которым связаны все другие рабочие книги. Это можно сохранить на SharePoint или на общем диске, тогда все, что вам нужно сделать, это обновить эту таблицу
2) Сложный путь - используйте API веб-сайта для получения последних данных о часовых поясах. https://www.amdoren.com/ - хороший сайт, вы можете получить бесплатный ключ API, зарегистрировавшись. Единственная проблема заключается в том, что вы должны проанализировать файл Json с веб-сайта. Это непросто, но если вы воспользуетесь Google "vba parse json", вы найдете некоторые решения (обычно для этого требуется импортировать некоторые библиотеки и использовать код других людей в качестве отправной точки)
Надеюсь, что вы найдете правильное решение, и если вы все-таки сделаете это, возможно, стоит поделиться им, так как я уверен, что будут другие с такой же проблемой.