.NET TimeZoneInfo из часового пояса Олсон - PullRequest
62 голосов
/ 13 мая 2011

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

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

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

Я предполагаю, что смещение отличается от UTC, и мне сказали, что "America / Los_Angeles" - это часовой пояс Олсона. У Java нет проблем с анализом этого в Java TimeZone, но мне нужно разобрать это в объект C # TimeZoneInfo.

Ответы [ 6 ]

94 голосов
/ 26 мая 2011

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

/// <summary>
/// Converts an Olson time zone ID to a Windows time zone ID.
/// </summary>
/// <param name="olsonTimeZoneId">An Olson time zone ID. See http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/zone_tzid.html. </param>
/// <returns>
/// The TimeZoneInfo corresponding to the Olson time zone ID, 
/// or null if you passed in an invalid Olson time zone ID.
/// </returns>
/// <remarks>
/// See http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/zone_tzid.html
/// </remarks>
public static TimeZoneInfo OlsonTimeZoneToTimeZoneInfo(string olsonTimeZoneId)
{
    var olsonWindowsTimes = new Dictionary<string, string>()
    {
        { "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;
}
26 голосов
/ 13 апреля 2013

Вот функция обратного отображения (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);
}

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

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

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

11 голосов
/ 02 марта 2012

Небольшой фрагмент, который я придумал, чтобы получить список сопоставлений часовых поясов в разделе «Олсон - 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<string, string>();
    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]));
        }
    }
}

ОБНОВЛЕНИЕ (с использованием Linq Xml):

private static void LoadMappings()
{
    var map = new Dictionary<string, string>();
    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]}\"}},");
        }
    }
}
11 голосов
/ 10 ноября 2011

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

Что не так с DateTime в любом случае?

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

4 голосов
/ 04 января 2013

ОБНОВЛЕНИЕ: я удалил 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: |<mapZone other="Alaskan Standard Time" territory="US" type="America/Anchorage America/Juneau America/Nome America/Sitka America/Yakutat"/>
    $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)}
2 голосов
/ 13 мая 2011

После преобразования 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;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...