优雅的方式将纪元时间戳转换为东部时间并使用Nodatime进行反向

时间:2014-11-01 18:40:03

标签: c# datetime timezone portable-class-library nodatime

我正在研究writing a managed wrapper around Massachusetts Bay Transportation Authority (MBTA) Realtime API。它们有一个API,它返回服务器时间,即unix时间戳(epoch)。我正在实施它的库是PCL Profile 78,这意味着我对BCL TimeZone的支持有限,所以我使用了Nodatime

我正在尝试将从服务器返回的时间转换为东部时间,America/New_York作为DateTime对象并反向转换。 My current code is very dirty

public static class TimeUtils
{
    static readonly DateTimeZone mbtaTimeZone = DateTimeZoneProviders.Tzdb["America/New_York"];
    static readonly DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

    public static DateTime GetMbtaDateTime (long unixTimestamp)
    {
        var mbtaEpochTime = epoch.AddSeconds (unixTimestamp);
        var instant = Instant.FromUtc (mbtaEpochTime.Year, mbtaEpochTime.Month,
            mbtaEpochTime.Day, mbtaEpochTime.Hour, mbtaEpochTime.Minute, mbtaEpochTime.Second);
        var nodaTime = instant.InZone (mbtaTimeZone);
        return nodaTime.ToDateTimeUnspecified ();
    }

    public static long MbtaDateTimeToUnixTimestamp (DateTime time)
    {
        TimeSpan secondsSinceEpochMbtaTz = time - epoch;
        var instant = Instant.FromUtc (time.Year, time.Month, time.Day, time.Hour, time.Minute, time.Second);
        var mbtaTzSpan = mbtaTimeZone.GetUtcOffset (instant).ToTimeSpan ();
        var epochDiff = secondsSinceEpochMbtaTz - mbtaTzSpan;
        return (long)epochDiff.TotalSeconds;
    }
}

还有另一种方法可以简单地写这个。我希望Nodatime应该支持将纪元时间转换为America/New_York DateTime和America/New_York DateTime到纪元时间。我的方法MbtaDateTimeToUnixTimestamp是一个残酷的黑客

1 个答案:

答案 0 :(得分:7)

首先,正如评论中所提到的,最好在整个代码中使用Noda Time类型 - 当真正必须时,只使用DateTime。这应该会导致整个代码更加清晰。

将Unix时间戳转换为Instant非常简单:

Instant instant = Instant.FromSecondsSinceUnixEpoch(seconds);

然后,您可以根据当前代码转换为ZonedDateTime ...如果您确实需要使用ToDateTimeUnspecified,则可以使用DateTime

反过来说,你当前的代码看起来很糟糕 - 你假设DateTime是一个有效的UTC值。这与你以后使用时区不符。我怀疑你想将输入转换为LocalDateTime,然后应用时区。例如:

public static long MbtaDateTimeToUnixTimestamp(DateTime time)
{
    var local = LocalDateTime.FromDateTime(time);
    var zoned = local.InZoneStrictly(mbtaTimeZone);
    var instant = zoned.ToInstant();
    return instant.Ticks / NodaConstants.TicksPerSecond;
}

请注意InZoneStrictly来电。如果你传入一个不存在的本地时间一个存在两次的情况,这将引发异常 - 在两种情况下都是由于DST转换。这可能是您想要的 - 您真的需要考虑在这些情况下您想要发生什么,或者尽量避免它们可行。有关详细信息和选项,请参阅time zones section of the documentation