将NodaTime DateTimeZone转换为TimeZoneInfo

时间:2013-02-19 22:14:16

标签: nodatime

我正在使用NodaTime,因为它对zoneinfo数据提供了很好的支持,但我有一个案例需要将DateTimeZone转换为TimeZoneInfo以便在Quartz.NET中使用。

这里推荐的方法是什么? IANA在Windows时区和zoneinfo时区之间有一个映射文件,我可以创建一个利用此信息的扩展方法吗?

谢谢, 迪安

4 个答案:

答案 0 :(得分:11)

如果可能,我会避免使用反射。我不想打赌您使用未来版本的方法:)

请为将来的版本提供此功能的功能请求,但目前我会以更稳定的方式构建您的反向字典:

// Note: this version lets you work with any IDateTimeZoneSource, although as the only
// other built-in source is BclDateTimeZoneSource, that may be less useful :)
private static IDictionary<string, string> LoadTimeZoneMap(IDateTimeZoneSource source)
{
    var nodaToWindowsMap = new Dictionary<string, string>();
    foreach (var bclZone in TimeZoneInfo.GetSystemTimeZones())
    {
        var nodaId = source.MapTimeZoneId(bclZone);
        if (nodaId != null)
        {
            nodaToWindowsMap[nodaId] = bclZone.Id;
        }
    }
    return nodaToWindowsMap;
}

当然,这个不会覆盖TZDB中的所有时区。事实上,它甚至不会根据我们使用的CLDR信息提供我们可以提供的所有信息... CLDR为每个Windows ID提供多个映射,我们只将第一个存储在时刻。我们一直试图弄清楚如何揭露更多,但尚未管理。关于Noda Time邮件列表的欢迎:)

另请注意,仅仅因为BCL和TZDB区域之间存在映射并不意味着它们实际上会为所有内容提供相同的结果 - 它只是最接近的可用映射。

答案 1 :(得分:3)

啊哈,我找到了它 - TzdbDateTimeZoneSource有一个MapTimeZoneId方法,我可以使用TimeZoneInfo.FindSystemTimeZoneById

编辑:MapTimeZoneId执行从Windows时区到zoneinfo的映射...我最终依靠反射来执行相反方向的映射:

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

答案 2 :(得分:3)

您可以TimeZoneConverter使用Matt Johnson库。

NodeTime TzdbZoneLocation使用的ZoneId为IANA time zone,因此您可以像这样获取TimeZoneInfo:

string windowsTimeZoneName = TZConvert.IanaToWindows(tzdbZoneLocation.ZoneId);
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(windowsTimeZoneName);

请不要忘记用try-catch包装它,以防万一。[/ p>

另请查看original Matt Johnson solution以便在IANA时区和Windows时区之间进行转换。

答案 3 :(得分:1)

然而,这一切都不适用于PCL,因为大多数工作都是由.NET在.GetSystemTImeZones()和.FindSystemTIemZoneById()方法中完成的 - 这些方法在PCL中不存在。

我很惊讶,你可以从NodaTime获得所有信息,当你已经拥有区域名称“US / Eastern”时,得到像“EST”缩写这样简单的东西似乎已经阻止了我的追踪。