如何比较不同时区之间的时间戳?
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);
,则效果很好。
如果您必须处理来自不同时区的 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);
,则效果很好。