如何比较不同时区之间的时间戳?

时间:2016-04-07 00:35:45

标签: c# extension-methods datetime-format

如果您必须处理来自不同时区的DateTime对象 - 请说是因为您的Web应用程序在东海岸的一台服务器上运行,另一台在西海岸运行 - 并且您想确保比较两个时间戳(例如,客户首先点击的时间戳)时,你没有犯错,那么有一个方法可以为你做到这一点。

典型的测试用例可能如下所示:

namespace ImageServerTest
{
    [..]
    [TestMethod]
    public void TestOrIfLater()
    {
        var PST = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time");
        var EST = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
        var t0 = DateTime.UtcNow;
        var t0p = TimeZoneInfo.ConvertTimeFromUtc(t0, PST);
        var t0e = TimeZoneInfo.ConvertTimeFromUtc(t0, EST);
        Thread.Sleep(100);
        var t1 = DateTime.UtcNow;
        var t1p = TimeZoneInfo.ConvertTimeFromUtc(t1, PST);
        var t1e = TimeZoneInfo.ConvertTimeFromUtc(t1, EST);
        Assert.AreEqual(t1, t0.OrIfLater(t1));
        Assert.AreEqual(t1p, t0p.OrIfLater(t1p));
        Assert.AreEqual(t1e, t1e.OrIfLater(t1p));
        Assert.AreEqual(t1e, t0p.OrIfLater(t1e));
        //Assert.IsTrue(t1p > t0e); //fails
        //Assert.IsTrue(t1p.Ticks > t0e.Ticks); //fails
        Assert.AreEqual(t1p, t1p.OrIfLater(t0e));
    }
}

显然,t1小于t0(意味着t1> t0或t1.Ticks> t0.Ticks),t0p / t0e和t1p / t1e是它们在PST / EST中的表示。因此,从全球的角度来看,我们期待

t1p > t0e

以及

t1p.Ticks > t0e.Ticks

测试用例指定我们要确保名为 OrIfLater 的扩展方法始终返回较年轻的时间戳(时间轴右侧的时间戳)。

2 个答案:

答案 0 :(得分:1)

在处理时区和数据比较时,您确实应该使用DateTimeOffset。为此目的,它被添加到BCL。

您现有的代码断言产生以下值:

True
True
True
True
False
False
False

但如果你改为DateTimeOffset,就像这样:

TimeZoneInfo PST = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time");
TimeZoneInfo EST = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
DateTimeOffset t0 = DateTimeOffset.UtcNow;
DateTimeOffset t0p = TimeZoneInfo.ConvertTime(t0, PST);
DateTimeOffset t0e = TimeZoneInfo.ConvertTime(t0, EST);
Thread.Sleep(100);
DateTimeOffset t1 = DateTimeOffset.UtcNow;
DateTimeOffset t1p = TimeZoneInfo.ConvertTime(t1, PST);
DateTimeOffset t1e = TimeZoneInfo.ConvertTime(t1, EST);
Assert.AreEqual(t1, t0.OrIfLater(t1));
Assert.AreEqual(t1p, t0p.OrIfLater(t1p));
Assert.AreEqual(t1e, t1e.OrIfLater(t1p));
Assert.AreEqual(t1e, t0p.OrIfLater(t1e));
Assert.IsTrue(t1p > t0e);
Assert.IsTrue(t1p.Ticks > t0e.Ticks); //still fails
Assert.AreEqual(t1p, t1p.OrIfLater(t0e));

...然后你得到这个结果:

True
True
True
True
True
False
True

当然,您需要将扩展​​方法从使用DateTime更改为使用DateTimeOffset

Ticks属性比较仍有一个失败。但这不是一个错误。它是如何运作的。

如果您查看MSDN Documentation,您会看到以下详细信息:

  

DateTimeOffset对象的时钟时间中的滴答数。   Ticks属性不受Offset属性值的影响。

所以要检查这是我输出new [] { t0, t0p, t0e, t1, t1p, t1e, }.Select(x => x.Ticks)的结果然后我得到了这个:

635955882954740587 
635955630954740587 
635955738954740587 
635955882955751105 
635955630955751105 
635955738955751105 

Ticks确实相对于当地时间而不是基础UTC时间。最终失败的Assert无效。

但是,如果将其更改为Assert.IsTrue(t1p.UtcTicks > t0e.UtcTicks);,则可以正常使用。

答案 1 :(得分:-1)

namespace Stuff
{
    public static class DynamicHelper
    {
        /// <summary>
        /// returns the latest date/time of the two, based on universal time
        /// </summary>
        /// <param name="a">this timestamp</param>
        /// <param name="b">the parameter timestamp to compare with</param>
        /// <example>var latest = yesterday.OrIfLater(today);</example>
        /// <returns>the most recent of the two timestamps</returns>
        public static DateTimeOffset OrIfLater(this DateTimeOffset a, DateTimeOffset b)
        {
            return a.UtcTicks > b.UtcTicks ? a : b;
        }
    }
    [..]
}
相关问题