.NET TimeZoneInfo из часового пояса Олсона

Как преобразовать следующее в System.TimeZone или System.TimeZoneInfo?

{ "timeZone": "America/Los_Angeles", "currentOffsetMs": -25200000 } 

Это данные, которые я получаю от стороннего веб-сервиса.

Я предполагаю, что смещение – это разница с UTC, и мне говорят, что «America / Los_Angeles» является часовым поясом Олсона. У Java нет никаких проблем, анализируя это в Java TimeZone, но мне нужно проанализировать это на объект CZ TimeZoneInfo.

На этой странице Unicode.org есть таблица времени часового пояса «Олсон для Win32». Оттуда я создал симпатичную небольшую вспомогательную функцию C # для отображения из строки часового пояса Олсона в .NET TimeZoneInfo:

 ///  /// Converts an Olson time zone ID to a Windows time zone ID. ///  /// An Olson time zone ID. See http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/zone_tzid.html.  ///  /// The TimeZoneInfo corresponding to the Olson time zone ID, /// or null if you passed in an invalid Olson time zone ID. ///  ///  /// See http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/zone_tzid.html ///  public static TimeZoneInfo OlsonTimeZoneToTimeZoneInfo(string olsonTimeZoneId) { var olsonWindowsTimes = new Dictionary() { { "Africa/Bangui", "W. Central Africa Standard Time" }, { "Africa/Cairo", "Egypt Standard Time" }, { "Africa/Casablanca", "Morocco Standard Time" }, { "Africa/Harare", "South Africa Standard Time" }, { "Africa/Johannesburg", "South Africa Standard Time" }, { "Africa/Lagos", "W. Central Africa Standard Time" }, { "Africa/Monrovia", "Greenwich Standard Time" }, { "Africa/Nairobi", "E. Africa Standard Time" }, { "Africa/Windhoek", "Namibia Standard Time" }, { "America/Anchorage", "Alaskan Standard Time" }, { "America/Argentina/San_Juan", "Argentina Standard Time" }, { "America/Asuncion", "Paraguay Standard Time" }, { "America/Bahia", "Bahia Standard Time" }, { "America/Bogota", "SA Pacific Standard Time" }, { "America/Buenos_Aires", "Argentina Standard Time" }, { "America/Caracas", "Venezuela Standard Time" }, { "America/Cayenne", "SA Eastern Standard Time" }, { "America/Chicago", "Central Standard Time" }, { "America/Chihuahua", "Mountain Standard Time (Mexico)" }, { "America/Cuiaba", "Central Brazilian Standard Time" }, { "America/Denver", "Mountain Standard Time" }, { "America/Fortaleza", "SA Eastern Standard Time" }, { "America/Godthab", "Greenland Standard Time" }, { "America/Guatemala", "Central America Standard Time" }, { "America/Halifax", "Atlantic Standard Time" }, { "America/Indianapolis", "US Eastern Standard Time" }, { "America/Indiana/Indianapolis", "US Eastern Standard Time" }, { "America/La_Paz", "SA Western Standard Time" }, { "America/Los_Angeles", "Pacific Standard Time" }, { "America/Mexico_City", "Mexico Standard Time" }, { "America/Montevideo", "Montevideo Standard Time" }, { "America/New_York", "Eastern Standard Time" }, { "America/Noronha", "UTC-02" }, { "America/Phoenix", "US Mountain Standard Time" }, { "America/Regina", "Canada Central Standard Time" }, { "America/Santa_Isabel", "Pacific Standard Time (Mexico)" }, { "America/Santiago", "Pacific SA Standard Time" }, { "America/Sao_Paulo", "E. South America Standard Time" }, { "America/St_Johns", "Newfoundland Standard Time" }, { "America/Tijuana", "Pacific Standard Time" }, { "Antarctica/McMurdo", "New Zealand Standard Time" }, { "Atlantic/South_Georgia", "UTC-02" }, { "Asia/Almaty", "Central Asia Standard Time" }, { "Asia/Amman", "Jordan Standard Time" }, { "Asia/Baghdad", "Arabic Standard Time" }, { "Asia/Baku", "Azerbaijan Standard Time" }, { "Asia/Bangkok", "SE Asia Standard Time" }, { "Asia/Beirut", "Middle East Standard Time" }, { "Asia/Calcutta", "India Standard Time" }, { "Asia/Colombo", "Sri Lanka Standard Time" }, { "Asia/Damascus", "Syria Standard Time" }, { "Asia/Dhaka", "Bangladesh Standard Time" }, { "Asia/Dubai", "Arabian Standard Time" }, { "Asia/Irkutsk", "North Asia East Standard Time" }, { "Asia/Jerusalem", "Israel Standard Time" }, { "Asia/Kabul", "Afghanistan Standard Time" }, { "Asia/Kamchatka", "Kamchatka Standard Time" }, { "Asia/Karachi", "Pakistan Standard Time" }, { "Asia/Katmandu", "Nepal Standard Time" }, { "Asia/Kolkata", "India Standard Time" }, { "Asia/Krasnoyarsk", "North Asia Standard Time" }, { "Asia/Kuala_Lumpur", "Singapore Standard Time" }, { "Asia/Kuwait", "Arab Standard Time" }, { "Asia/Magadan", "Magadan Standard Time" }, { "Asia/Muscat", "Arabian Standard Time" }, { "Asia/Novosibirsk", "N. Central Asia Standard Time" }, { "Asia/Oral", "West Asia Standard Time" }, { "Asia/Rangoon", "Myanmar Standard Time" }, { "Asia/Riyadh", "Arab Standard Time" }, { "Asia/Seoul", "Korea Standard Time" }, { "Asia/Shanghai", "China Standard Time" }, { "Asia/Singapore", "Singapore Standard Time" }, { "Asia/Taipei", "Taipei Standard Time" }, { "Asia/Tashkent", "West Asia Standard Time" }, { "Asia/Tbilisi", "Georgian Standard Time" }, { "Asia/Tehran", "Iran Standard Time" }, { "Asia/Tokyo", "Tokyo Standard Time" }, { "Asia/Ulaanbaatar", "Ulaanbaatar Standard Time" }, { "Asia/Vladivostok", "Vladivostok Standard Time" }, { "Asia/Yakutsk", "Yakutsk Standard Time" }, { "Asia/Yekaterinburg", "Ekaterinburg Standard Time" }, { "Asia/Yerevan", "Armenian Standard Time" }, { "Atlantic/Azores", "Azores Standard Time" }, { "Atlantic/Cape_Verde", "Cape Verde Standard Time" }, { "Atlantic/Reykjavik", "Greenwich Standard Time" }, { "Australia/Adelaide", "Cen. Australia Standard Time" }, { "Australia/Brisbane", "E. Australia Standard Time" }, { "Australia/Darwin", "AUS Central Standard Time" }, { "Australia/Hobart", "Tasmania Standard Time" }, { "Australia/Perth", "W. Australia Standard Time" }, { "Australia/Sydney", "AUS Eastern Standard Time" }, { "Etc/GMT", "UTC" }, { "Etc/GMT+11", "UTC-11" }, { "Etc/GMT+12", "Dateline Standard Time" }, { "Etc/GMT+2", "UTC-02" }, { "Etc/GMT-12", "UTC+12" }, { "Europe/Amsterdam", "W. Europe Standard Time" }, { "Europe/Athens", "GTB Standard Time" }, { "Europe/Belgrade", "Central Europe Standard Time" }, { "Europe/Berlin", "W. Europe Standard Time" }, { "Europe/Brussels", "Romance Standard Time" }, { "Europe/Budapest", "Central Europe Standard Time" }, { "Europe/Dublin", "GMT Standard Time" }, { "Europe/Helsinki", "FLE Standard Time" }, { "Europe/Istanbul", "GTB Standard Time" }, { "Europe/Kiev", "FLE Standard Time" }, { "Europe/London", "GMT Standard Time" }, { "Europe/Minsk", "E. Europe Standard Time" }, { "Europe/Moscow", "Russian Standard Time" }, { "Europe/Paris", "Romance Standard Time" }, { "Europe/Sarajevo", "Central European Standard Time" }, { "Europe/Warsaw", "Central European Standard Time" }, { "Indian/Mauritius", "Mauritius Standard Time" }, { "Pacific/Apia", "Samoa Standard Time" }, { "Pacific/Auckland", "New Zealand Standard Time" }, { "Pacific/Fiji", "Fiji Standard Time" }, { "Pacific/Guadalcanal", "Central Pacific Standard Time" }, { "Pacific/Guam", "West Pacific Standard Time" }, { "Pacific/Honolulu", "Hawaiian Standard Time" }, { "Pacific/Pago_Pago", "UTC-11" }, { "Pacific/Port_Moresby", "West Pacific Standard Time" }, { "Pacific/Tongatapu", "Tonga Standard Time" } }; var windowsTimeZoneId = default(string); var windowsTimeZone = default(TimeZoneInfo); if (olsonWindowsTimes.TryGetValue(olsonTimeZoneId, out windowsTimeZoneId)) { try { windowsTimeZone = TimeZoneInfo.FindSystemTimeZoneById(windowsTimeZoneId); } catch (TimeZoneNotFoundException) { } catch (InvalidTimeZoneException) { } } return windowsTimeZone; } 

Вот функция обратного отображения (tzdb -> windows) с использованием NodaTime :

 using NodaTime; using NodaTime.TimeZones; ... public TimeZoneInfo GetTimeZoneInfoForTzdbId(string tzdbId) { var mappings = TzdbDateTimeZoneSource.Default.WindowsMapping.MapZones; var map = mappings.FirstOrDefault(x => x.TzdbIds.Any(z => z.Equals(tzdbId, StringComparison.OrdinalIgnoreCase))); return map == null ? null : TimeZoneInfo.FindSystemTimeZoneById(map.WindowsId); } 

Обратите внимание: возможно, что существует более одного сопоставления (в этом случае это просто использует первый найденный) или вообще не отображает (где это возвращает null).

В наиболее часто используемых часовых поясах это должно работать достаточно хорошо. Но лучшим решением было бы пропустить использование TimeZoneInfo вообще и просто использовать NodaTime в приложении, непосредственно в зоне TZDB.

См. Также: Как перевести между часовыми поясами Windows и IANA?

Возможно, вам захочется заглянуть в « Нода-время» Джона Скита и полностью отказаться от TimeZoneInfo. Noda-Time использует часы Олсона, поэтому ваше картирование будет куском пирога. Существуют и другие причины, по которым вы можете использовать его:

Что случилось с DateTime?

Мой вопрос, результатом которого было использование Noda-Time

Небольшой fragment, который я придумал, чтобы получить список olson для сопоставлений часовых поясов Windows из xml по адресу http://unicode.org/repos/cldr/trunk/common/supplemental/windowsZones.xml

 private static void LoadMappingsO() { var file = new FileInfo("windowsZones.xml"); if (!file.Exists) { return; } var map = new Dictionary(); using (var reader = file.OpenText()) { var readerSettings = new XmlReaderSettings { XmlResolver = null, ProhibitDtd = false }; using (var xmlReader = XmlReader.Create(reader, readerSettings)) { var document = new XPathDocument(xmlReader); var navigator = document.CreateNavigator(); var nodes = navigator.Select("/supplementalData/windowsZones/mapTimezones/mapZone"); while (nodes.MoveNext()) { var node = nodes.Current; if (node == null) continue; var olsonNames = node.GetAttribute("type", "").Split(' '); var windowsName = node.GetAttribute("other", ""); foreach (var olsonName in olsonNames) { if (!map.ContainsKey(olsonName)) { map.Add(olsonName, windowsName); } } } } } using (TextWriter tw = new StreamWriter("dict.txt", false)) { foreach (var key in map.Keys) { tw.WriteLine(string.Format("{{\"{0}\", \"{1}\"}},", key, map[key])); } } } 

UPDATE (с использованием Linq Xml):

 private static void LoadMappings() { var map = new Dictionary(); var xdoc = XDocument.Load("windowsZones.xml"); var zones = xdoc.XPathSelectElements("/supplementalData/windowsZones/mapTimezones/mapZone"); foreach (var zone in zones) { var olsonNames = zone.Attribute("type")?.Value.Split(' '); if (olsonNames == null) continue; var windowsName = zone.Attribute("other")?.Value; if (string.IsNullOrWhiteSpace(windowsName)) continue; foreach (var olsonName in olsonNames) { map[olsonName] = windowsName; } } using (TextWriter tw = new StreamWriter("dict.txt", false)) { foreach (var key in map.Keys) { tw.WriteLine($"{{\"{key}\", \"{map[key]}\"}},"); } } } 

UPDATE: я удалил URL-адрес из сценария. Пожалуйста, отправьте файл вручную. Этот скрипт не предназначен для постоянной загрузки ненужной нагрузки на unicode.org. См. Комментарии ниже.

Этот сценарий Powershell можно использовать для создания оператора case с использованием текущего файла XML из unicode.org. Он генерирует отображения из имен IANA в TimeZoneInfoId.

  # Download the xml file. $xml = [Xml] /// Load the XML content here # Parse the fields we want from the XML. $mappings1 = $xml.supplementalData.windowsZones.mapTimezones.mapZone | select Type,Other # Extrapolate extra rows for entries that contain more than one IANA name seperated by spaces. # Example: | $mappings2 = $mappings1 | %{ $mapping = $_ $_.Type.Split(" ") | %{ New-Object PSObject -Property @{type = $_; other = $mapping.other} } } # Remove dup's $mappings3 = $mappings2 | sort type -Unique # Generate the case statements. $mappings3 | %{ [String]::Format("case @""{0}"": return @""{1}"";", $_.Type, $_.Other)} 

После преобразования currentOffsetMs в часы и оставшиеся минуты вы можете перечислить определенные объекты TimeZoneInfo:

 foreach (TimeZoneInfo nextZone in TimeZoneInfo.GetSystemTimeZones()) { int nextHours = nextZone.BaseUtcOffset.Hours + 24; // To prevent negative numbers int nextMinutes = nextZone.BaseUtcOffset.Minutes; if (tzHours == nextHours && tzMinutes == nextMinutes) { myTimeZoneInfo = nextZone; break; } } 
  • Веб-сервис - текущий часовой пояс для города?
  • Временные зоны MySQL
  • Как справиться с летней экономией, используя TimeZone в Java
  • Изменение часового пояса MySQL?
  • Как правильно установить JVM TimeZone
  • Как хранить дату и время и временные метки в часовом поясе UTC с JPA и Hibernate
  • Как преобразовать время в часовой пояс устройства iPhone?
  • Получение даты с на несколько часов
  • Названия часовых поясов с одинаковыми свойствами дают разные результаты при применении к метке времени
  • Как конвертировать дату с одного часового пояса в другой часовой пояс
  • Сопоставление почтового индекса США с часовым поясом
  • Давайте будем гением компьютера.