12

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

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

4

5 回答 5

13

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

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

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

于 2013-02-23T08:32:40.423 回答
5

啊哈,我找到了 -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;
    }
}
于 2013-02-19T22:21:55.527 回答
3

您可以使用Matt Johnson的TimeZoneConverter库。

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

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

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

另请查看原始的 Matt Johnson 解决方案,用于在 IANA 时区和 Windows 时区之间进行转换。

于 2017-12-21T21:53:46.333 回答
2

但是,这在 PCL 中不起作用,因为大部分工作是由 .NET 在 .GetSystemTImeZones() 和 .FindSystemTIemZoneById() 方法中完成的——这在 PCL 中不存在。

令我震惊的是,对于您可以从 NodaTime 获得的所有信息,当您已经拥有“美国/东部”的区域名称时,获得像“EST”缩写这样简单的东西似乎已经阻止了我的前进。

于 2016-06-21T16:55:14.720 回答
0

对于NodaTime汇编版本3.0.9.0,tzdb time zone ids ( NodaTime.DateTimeZone.Id) 到 dotnet time zone info ids ( System.TimeZoneInfo.Id) 的映射和相反的过程可以通过这两个字符串键和值字典实现:NodaTime.TimeZones.TzdbDateTimeZoneSource.Default.TzdbToWindowsIds, NodaTime.TimeZones.TzdbDateTimeZoneSource.Default.WindowsToTzdbIds.

于 2022-02-28T18:26:20.900 回答