【问题标题】:Using NodaTime to convert invalid (skipped) datetime values to UTC使用 NodaTime 将无效(跳过的)日期时间值转换为 UTC
【发布时间】:2013-03-04 23:34:00
【问题描述】:

我想要完成的是将 DateTime(从假定为 EST/EDT 的字符串解析)转换为 UTC。我使用 NodaTime 是因为我需要使用 Olson 时区。

使用 NodaTime 的 ZoneLocalMappingResolver 将无效(跳过的)DateTime 转换为 UTC 不会转换输入的分钟和秒部分,因为我已将 CustomResolver 配置为在间隙后返回间隔的开始。 NodaTime 似乎没有 TimeZoneInfo.IsInvalidTime 的等价物。

如何使用 NodaTime 将跳过的日期时间值转换为 UTC 并匹配下面 Utils 类中 GetUtc() 方法的结果? (Utils.GetUtc 方法使用 System.TimeZoneInfo 而不是 NodaTime)

这是测试用例:

[TestMethod]
public void Test_Invalid_Date()
{
    var ts = new DateTime(2013, 3, 10, 2, 15, 45);

    // Convert to UTC using System.TimeZoneInfo
    var utc = Utils.GetUtc(ts).ToString(Utils.Format);

    // Convert to UTC using NodaTime (Tzdb/Olson dataabase)
    var utcNodaTime = Utils.GetUtcTz(ts).ToString(Utils.Format);

    Assert.AreEqual(utc, utcNodaTime);
}

这就是我得到的:

Assert.AreEqual 失败。预期:。实际:。

这里是 Utils 类(也在github):

using System;

using NodaTime;
using NodaTime.TimeZones;

/// <summary>
/// Functions to Convert To and From UTC
/// </summary>
public class Utils
{
    /// <summary>
    /// The date format for display/compare
    /// </summary>
    public const string Format = "yyyy-MM-dd HH:mm:ss.ffffff";

    /// <summary>
    /// The eastern U.S. time zone
    /// </summary>
    private static readonly NodaTime.DateTimeZone BclEast = NodaTime.DateTimeZoneProviders.Bcl.GetZoneOrNull("Eastern Standard Time");


    private static readonly TimeZoneInfo EasternTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");

    private static readonly NodaTime.DateTimeZone TzEast = NodaTime.DateTimeZoneProviders.Tzdb.GetZoneOrNull("America/New_York");

    private static readonly ZoneLocalMappingResolver CustomResolver = Resolvers.CreateMappingResolver(Resolvers.ReturnLater, Resolvers.ReturnStartOfIntervalAfter);

    public static DateTime GetUtc(DateTime ts)
    {
        return TimeZoneInfo.ConvertTimeToUtc(EasternTimeZone.IsInvalidTime(ts) ? ts.AddHours(1.0) : ts, EasternTimeZone);
    }

    public static DateTime GetUtcTz(DateTime ts)
    {
        var local = LocalDateTime.FromDateTime(ts);
        var zdt = TzEast.ResolveLocal(local, CustomResolver);
        return zdt.ToDateTimeUtc();            
    }

    public static DateTime GetUtcBcl(DateTime ts)
    {
        var local = LocalDateTime.FromDateTime(ts);
        var zdt = BclEast.ResolveLocal(local, CustomResolver);
        return zdt.ToDateTimeUtc();
    }
}

【问题讨论】:

    标签: c# datetime timezone dst nodatime


    【解决方案1】:

    NodaTime 似乎没有等效的 TimeZoneInfo.IsInvalidTime。

    好吧,您可以使用DateTimeZone.MapLocal,而不是只问一个问题 - 然后不得不问后续问题。这为您提供了有关本地到 UTC 映射的所有信息:无论是明确的、不明确的还是无效的。

    或者,使用 ResolveLocal,但使用您自己的自定义 SkippedTimeResolver 委托。

    例如,进行此更改会使您的代码为我工作:

    private static readonly ZoneLocalMappingResolver CustomResolver = 
        Resolvers.CreateMappingResolver(Resolvers.ReturnLater, AddGap);
    
    // SkippedTimeResolver which adds the length of the gap to the
    // local date and time.
    private static ZonedDateTime AddGap(LocalDateTime localDateTime,
                                        DateTimeZone zone,
                                        ZoneInterval intervalBefore,
                                        ZoneInterval intervalAfter)        
    {
        long afterMillis = intervalAfter.WallOffset.Milliseconds;
        long beforeMillis = intervalBefore.WallOffset.Milliseconds;
        Period gap = Period.FromMilliseconds(afterMillis - beforeMillis);
        return zone.AtStrictly(localDateTime + gap);
    }
    

    (当然还有其他等效的方法。)

    我个人建议尽量避免与DateTime 相互转换,除非你真的必须这样做 - 我会尽可能在野田时间做。

    【讨论】:

    • 我已更新此答案中的参考链接,以引导您在 nodatime.org 上托管的较新 API 文档,而不是旧的、丢失的谷歌代码站点。
    猜你喜欢
    • 1970-01-01
    • 2023-03-31
    • 2015-02-26
    • 1970-01-01
    • 2013-08-29
    • 2014-06-13
    • 1970-01-01
    • 2012-10-07
    • 2021-12-31
    相关资源
    最近更新 更多