.NET TimeZoneInfo du fuseau horaire Olson

Comment puis-je convertir les éléments suivants en System.TimeZone ou System.TimeZoneInfo?

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

Ce sont des données que je récupère d’un service Web tiers.

Je suppose que le décalage est la différence avec UTC, et on me dit que “America / Los_Angeles” est un fuseau horaire Olson. Java n’a aucun problème pour parsingr ceci dans un TimeZone Java, mais je dois parsingr ceci dans un object C # TimeZoneInfo.

Cette page Unicode.org contient un tableau ” Fuseau horaire Olson vers Win32″. À partir de là, j’ai créé une jolie petite fonction d’assistance C # pour mapper la chaîne de fuseau horaire Olson dans un .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(ssortingng 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(ssortingng); var windowsTimeZone = default(TimeZoneInfo); if (olsonWindowsTimes.TryGetValue(olsonTimeZoneId, out windowsTimeZoneId)) { try { windowsTimeZone = TimeZoneInfo.FindSystemTimeZoneById(windowsTimeZoneId); } catch (TimeZoneNotFoundException) { } catch (InvalidTimeZoneException) { } } return windowsTimeZone; } 

Voici une fonction de cartographie inverse (tzdb -> windows) utilisant NodaTime :

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

Notez qu’il est possible qu’il y ait plus d’un mappage (auquel cas il utilise simplement le premier trouvé), ou aucun mappage du tout (où cela renvoie null).

Dans les fuseaux horaires les plus couramment utilisés, cela devrait fonctionner correctement. Mais la meilleure solution serait de ne pas utiliser TimeZoneInfo , et d’utiliser simplement NodaTime dans l’application, directement avec la zone TZDB que vous avez.

Voir aussi: Comment traduire entre les fuseaux horaires Windows et IANA?

Vous pourriez vouloir regarder Noda-Time de Jon Skeet et abandonner complètement TimeZoneInfo. Noda-Time utilise les fuseaux horaires Olson afin que votre cartographie soit un jeu d’enfant. Il y a d’autres raisons pour lesquelles vous pourriez vouloir l’utiliser:

Quel est le problème avec DateTime de toute façon?

SO question de moi dont le résultat était d’utiliser Noda-Time

Un petit extrait que j’ai trouvé pour obtenir une liste des mappages de fuseau horaire olson à windows à partir du fichier XML à l’adresse 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.GetAtsortingbute("type", "").Split(' '); var windowsName = node.GetAtsortingbute("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(ssortingng.Format("{{\"{0}\", \"{1}\"}},", key, map[key])); } } } 

UPDATE (Utilisation de 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.Atsortingbute("type")?.Value.Split(' '); if (olsonNames == null) continue; var windowsName = zone.Atsortingbute("other")?.Value; if (ssortingng.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]}\"}},"); } } } 

MISE À JOUR: J’ai supprimé l’URL du script. Veuillez source le fichier manuellement. Ce script n’était pas destiné à être exécuté en permanence en chargeant inutilement unicode.org. Voir les commentaires ci-dessous.

Ce script Powershell peut être utilisé pour générer une déclaration de cas en utilisant le fichier XML actuel de unicode.org. Il génère des mappages entre les noms IANA et 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 ensortinges 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 | %{ [Ssortingng]::Format("case @""{0}"": return @""{1}"";", $_.Type, $_.Other)} 

Après avoir converti les currentOffsetMs en heures et minutes restantes, vous pouvez énumérer les objects TimeZoneInfo définis:

 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; } }