将NodaTime DateTimeZone转换为TimeZoneInfo

问题描述 投票:9回答:4

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

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

谢谢,迪恩

nodatime
4个回答
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区域之间存在映射并不意味着它们实际上会为所有内容提供相同的结果 - 它只是最接近的可用映射。


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

3
投票

你可以使用TimeZoneConverterMatt Johnson库。

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

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

不要忘记用try-catch包装它,以防万一。

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


1
投票

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

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

© www.soinside.com 2019 - 2024. All rights reserved.