'Convert NodaTime DateTimeZone into TimeZoneInfo
I'm using NodaTime because of its nice support for zoneinfo data, however I have a case where I need to convert the DateTimeZone into TimeZoneInfo for use in Quartz.NET.
What's the recommended approach here? IANA has a mapping file between Windows time zones and zoneinfo time zones, can I create an extension method that utilises this information?
Solution 1:[1]
Aha, I found it - TzdbDateTimeZoneSource has a MapTimeZoneId method that I can pop into TimeZoneInfo.FindSystemTimeZoneById.
Edit: MapTimeZoneId does the mapping from Windows time zone into zoneinfo... I ended up resorting to reflection to do the mapping in the opposite direction:
using System;
using System.Collections.Generic;
using System.Reflection;
using NodaTime;
using NodaTime.TimeZones;
/// <summary>
/// Extension methods for <see cref="DateTimeZone" />.
/// </summary>
internal static class DateTimeZoneExtensions
{
private static readonly Lazy<IDictionary<string, string>> map = new Lazy<IDictionary<string, string>>(LoadTimeZoneMap, true);
public static TimeZoneInfo ToTimeZoneInfo(this DateTimeZone timeZone)
{
string id;
if (!map.Value.TryGetValue(timeZone.Id, out id))
{
throw new TimeZoneNotFoundException(string.Format("Could not locate time zone with identifier {0}", timeZone.Id));
}
TimeZoneInfo timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(id);
if (timeZoneInfo == null)
{
throw new TimeZoneNotFoundException(string.Format("Could not locate time zone with identifier {0}", timeZone.Id));
}
return timeZoneInfo;
}
private static IDictionary<string, string> LoadTimeZoneMap()
{
TzdbDateTimeZoneSource source = new TzdbDateTimeZoneSource("NodaTime.TimeZones.Tzdb");
FieldInfo field = source.GetType().GetField("windowsIdMap", BindingFlags.Instance | BindingFlags.NonPublic);
IDictionary<string, string> map = (IDictionary<string, string>)field.GetValue(source);
// reverse the mappings
Dictionary<string, string> reverseMap = new Dictionary<string, string>();
foreach (KeyValuePair<string, string> kvp in map)
{
reverseMap.Add(kvp.Value, kvp.Key);
}
return reverseMap;
}
}
Solution 2:[2]
You can use TimeZoneConverter library by Matt Johnson.
ZoneId used by NodeTime TzdbZoneLocation is IANA time zone, so you can get TimeZoneInfo like this:
string windowsTimeZoneName = TZConvert.IanaToWindows(tzdbZoneLocation.ZoneId);
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(windowsTimeZoneName);
Don't forget to wrap it with try-catch with some kind of fallback just in case.
Also look at original Matt Johnson solution for converting between IANA time zone and Windows time zone.
Solution 3:[3]
However none of this works in a PCL because most of the work is done by .NET in the .GetSystemTImeZones() and .FindSystemTIemZoneById() methods - which don't exist in PCL.
I'm stunned that for all the info you can get out of NodaTime, getting something as simple as "EST" abbreviation when you already have the zone name of "US/Eastern" seems to have stopped me in my tracks.
Solution 4:[4]
With NodaTime assembly version 3.0.9.0 the mapping of tzdb time zone ids (NodaTime.DateTimeZone.Id) to dotnet time zone info ids (System.TimeZoneInfo.Id) and the reverse process is achievable via these two string key and value dictionaries: NodaTime.TimeZones.TzdbDateTimeZoneSource.Default.TzdbToWindowsIds, NodaTime.TimeZones.TzdbDateTimeZoneSource.Default.WindowsToTzdbIds.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | tseshevsky |
| Solution 3 | Clint StLaurent |
| Solution 4 | andrei.ciprian |
