如何使用 NodaTime(给定日光)计算指定日期和时间的时区 UTC 偏移量?

How to calculate time zone UTC offset for specified date and time with NodaTime (given daylight)?

我有 Iana 时区 ID,需要知道某些 DateTime 的时区偏移量。 据我了解,以下函数 returns DateTime.Now 的偏移量(或没有日光偏移量?)。

TimeSpan GetTimeZoneOffset(string timeZoneId) =>
    new DateTimeZoneCache(TzdbDateTimeZoneSource.Default)
        .GetZoneOrNull(timeZoneId)
        .GetUtcOffset(SystemClock.Instance.GetCurrentInstant())
        .ToTimeSpan();

但我需要

TimeSpan GetTimeZoneOffset(string timeZoneId, DateTime utcInstant)

如何实现?

TimeSpan GetTimeZoneOffset(string timeZoneId, DateTime date) =>
    new DateTimeZoneCache(TzdbDateTimeZoneSource.Default)
        .GetZoneOrNull(timeZoneId)
        .GetUtcOffset(Instant.FromDateTimeUtc(utcDay))
        .ToTimeSpan();

从根本上说,您需要 DateTimeZone.GetUtcOffset,它接受 Instant

如果 DateTime 值始终具有 UtcKind,您可以使用 Instant.FromDateTimeUtc。如果它可能有不同的 Kind,您需要制定更详细的要求。

接下来您需要 IDateTimeZoneProvider 将时区 ID 映射到 DateTimeZone。这可能是 DateTimeZoneProviders.Tzdb,或者它可能是您为了可测试性而在某处注入的。

获得 InstantDateTimeZone 后,您可以调用 GetUtcOffset 获得 Offset。您 可以 将其转换回 TimeSpan,但我实际上鼓励您尽可能避免在您的应用程序 - 如果您可以在代码库中的任何地方使用 Noda Time 类型,并且只在边界处(例如数据库访问)在这些类型和 BCL 类型之间进行转换,您会发现您需要做的这项工作要少得多。

但是如果你真的需要一个TimeSpan,方法应该是这样的:

TimeSpan GetTimeZoneOffset(string timeZoneId, DateTime dateTimeUtc)
{
    Instant instant = Instant.FromDateTimeUtc(dateTimeUtc);
    DateTimeZone zone = DateTimeZoneProviders.Tzdb[timeZoneId];
    Offset offset = zone.GetUtcOffset(instant);
    return offset.ToTimeSpan();
}

请注意,如果在该提供程序中找不到时区,IDateTimeZoneProvider[string] 索引器将抛出异常。如果您想以不同的方式处理此问题,请使用 IDateTimeZoneProvider.GetZoneOrNull() 并检查结果是否为空。