6

私が達成したいのは、(EST/EDT にあると想定される文字列から解析された) DateTime を UTC に変換することです。Olson タイムゾーンを使用する必要があるため、NodaTime を使用しています。

NodaTime の ZoneLocalMappingResolver を使用して無効な (スキップされた) DateTime を UTC に変換すると、ギャップの後に間隔の開始を返すように CustomResolver を構成したため、入力の分と秒の部分が変換されません。NodaTime にはTimeZoneInfo.IsInvalidTimeに相当するものがないようです。

NodaTime を使用して、スキップされた日時値を UTC に変換し、以下の Utils クラスの GetUtc() メソッドの結果と一致させるにはどうすればよいですか? (Utils.GetUtc メソッドは NodaTime ではなく System.TimeZoneInfo を使用します)

これはテストケースです:

[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 が失敗しました。予想:<2013-03-10 07:15:45.000000>。実際:<2013-03-10 07:00:00.000000>。

これが 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();
    }
}
4

1 に答える 1

7

NodaTime には、TimeZoneInfo.IsInvalidTime に相当するものがないようです。

その 1 つの質問をするだけでなく、次の質問をしなければならないのではなく、 を使用しますDateTimeZone.MapLocal。これにより、ローカルから UTC へのマッピングについて知ることができるすべての情報が得られます。それが明確か、あいまいか、または無効かどうかです。

ResolveLocalまたは、 but を独自のカスタム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ます - 私は Noda Time で可能な限り行います。

于 2013-03-05T04:30:58.130 に答える