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

How to compare timestamps between different time zones?

如果您必须处理来自不同时区的 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 是它们在 [=27= 中的表示].因此,从全球的角度来看,我们预计

t1p > t0e

还有

t1p.Ticks > t0e.Ticks

测试用例指定我们要确保名为 OrIfLater 的扩展方法始终 returns 较年轻的时间戳(时间轴中更靠右的那个).

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;
        }
    }
    [..]
}

在处理时区和数据比较时,您确实应该使用 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,您将看到以下详细信息:

The number of ticks in the DateTimeOffset object's clock time. The Ticks property is not affected by the value of the Offset property.

所以要检查这是我输出 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);,则效果很好。