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

C# 如何使用节点时间(给定日光)计算指定日期和时间的时区UTC偏移量?,c#,nodatime,C#,Nodatime,我有一个时区Id,需要知道一些日期时间的时区偏移量。 据我所知,以下函数返回DateTime.Now的偏移量(或者没有日光偏移量?) 但我需要 TimeSpan GetTimeZoneOffset(string timeZoneId, DateTime utcInstant) 如何实现它?从根本上说,您需要,它接受一个即时 TimeSpan GetTimeZoneOffset(string timeZoneId, DateTime date) => new DateTimeZon

我有一个时区Id,需要知道一些日期时间的时区偏移量。 据我所知,以下函数返回DateTime.Now的偏移量(或者没有日光偏移量?)

但我需要

TimeSpan GetTimeZoneOffset(string timeZoneId, DateTime utcInstant)
如何实现它?

从根本上说,您需要,它接受一个
即时

TimeSpan GetTimeZoneOffset(string timeZoneId, DateTime date) =>
    new DateTimeZoneCache(TzdbDateTimeZoneSource.Default)
        .GetZoneOrNull(timeZoneId)
        .GetUtcOffset(Instant.FromDateTimeUtc(utcDay))
        .ToTimeSpan();
如果
DateTime
值的
种类始终为
Utc
,则可以使用。如果它可能有不同的
类型
,您需要制定更详细的需求

接下来,您需要一个
IDateTimeZoneProvider
将时区ID映射到
DateTimeZone
。这可能是
DateTimeZoneProviders.Tzdb
,也可能是为了可测试性而注入的

一旦获得了
即时
日期时区
,您就可以调用
GetUtcOffset
来获取
偏移量。您可以将其转换回
TimeSpan
,但我实际上鼓励您在应用程序中尽可能避免使用
DateTime
TimeSpan
,前提是您可以在代码库中的任何位置使用Noda时间类型,并且仅在边界处的这些类型和BCL类型之间进行转换(例如,数据库访问)你会发现你需要做的工作要少得多

但如果您确实需要
时间跨度
,则该方法如下所示:

TimeSpan GetTimeZoneOffset(字符串timeZoneId,DateTime dateTimeUtc)
{
Instant Instant=Instant.FromDateTimeUtc(dateTimeUtc);
DateTimeZone=DateTimeZoneProviders.Tzdb[timeZoneId];
偏移量=zone.GetUtcOffset(即时);
返回offset.ToTimeSpan();
}

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

日期没有偏移量-特定的时间瞬间有偏移量。由于(例如)夏令时转换,单个日期可能跨越多个偏移。请澄清这个问题。好的,所以“对于某个日期时间”更合理一点-这是否总是
DateTime
类型的UTC?您的示例答案假设是这样,但这确实很重要。(如果它是一种未指定的
类型,并且该值被跳过或不明确,该怎么办?作为一个更重要的说明,当您在整个代码库中使用它时,Noda Time提供了最大的价值-我建议您保持在Noda Time的上下文中(因此
Instant
Offset
而不是
DateTime
TimeSpan
)对于尽可能多的代码。如果需要兼容性,请仅在代码边界处转换为BCL类型。@JonSkeet,我正试图用很少的节点时间经验找到解决问题的方法。为此,我使用我已经知道的术语和我已经掌握的知识在谷歌上搜索。这对你有好处在一篇文章中使用正确的术语,但在有疑问的情况下,最好保持问题的原样(为有同样问题的人简化谷歌搜索)。如果我的问题中有错误,可以在回答中更正,并指出我不理解。好吧,我真的不想添加答案,直到问题中的假设得到澄清-主要是
日期时间
类型你需要能够处理。它只会是UTC吗?我会回答推荐每次使用
DateTimeZoneProviders.Tzdb
而不是创建一个新的
DateTimeZoneCache
,如果区域ID未知,这将抛出一个
NullReferenceException
。如果您想因异常而失败,只需使用索引器-这将抛出一个更清晰的异常。@jonsket在我的实际项目中,我将DateTimeZoneCache作为单例注入。此外,我确信timeZoneId是正确的,因为它是在数据流的早期阶段验证的。无论如何,使用索引器是一个很好的观点。
TimeSpan GetTimeZoneOffset(string timeZoneId, DateTime date) =>
    new DateTimeZoneCache(TzdbDateTimeZoneSource.Default)
        .GetZoneOrNull(timeZoneId)
        .GetUtcOffset(Instant.FromDateTimeUtc(utcDay))
        .ToTimeSpan();