模棱两可的 DateTimeOffset 示例

Posted

技术标签:

【中文标题】模棱两可的 DateTimeOffset 示例【英文标题】:Example of ambiguous DateTimeOffset 【发布时间】:2016-04-18 13:50:35 【问题描述】:

我们在数据库/模型中有 DateTimeOffsets。为了在 Web 中显示这些值,我们将 DateTimeOffsets 转换为当前用户的时区。

根据 MSDN,DateTimeOffset 在特定的 TimeZone 中可能是不明确的:

TimeZoneInfo.IsAmbiguousTime Method (DateTimeOffset)

这对我来说根本没有意义。谁能给我一个模棱两可的示例 DateTimeOffset? 我们在时区“西欧标准时间”。

【问题讨论】:

注意,DateTimeOffset 是时区 awareness。它只有日期、时间和 Utc 偏移值。 【参考方案1】:

文档中所说的内容是否不清楚?

通常,当时钟设置为从夏令时返回标准时间时,会产生不明确的时间

即如果您在凌晨 2 点离开 DST 并将时钟重置为凌晨 1 点,那么如果有人开始谈论凌晨 1.30,您不知道那是从现在开始的 30 分钟还是过去 30 分钟发生的事情。

有一组值(通常为一小时长)映射到 UTC 时间中的两组不同时刻。

【讨论】:

【参考方案2】:

示例是(去年 10 月的星期日 2:00-3:00

DateTimeOffset example = new DateTimeOffset(2015, 10, 25, 2, 30, 0, 
  new TimeSpan(0, 2, 0, 0));

TimeZoneInfo tst = TimeZoneInfo.FindSystemTimeZoneById("W. Europe Standard Time");

if (tst.IsAmbiguousTime(example))
  Console.Write("Ambiguous time");

不明确时间相反的是无效时间(去年三月的星期日2:00-3:00):

TimeZoneInfo tst = TimeZoneInfo.FindSystemTimeZoneById("W. Europe Standard Time");

if (tst.IsInvalidTime(new DateTime(2016, 03, 27, 2, 30, 0)))
  Console.Write("Invalid time");

【讨论】:

【参考方案3】:

我认为混淆来自这里定义“模棱两可”的方式。

需要明确的是,DateTimeOffset 本身永远不会模棱两可。它总是代表绝对瞬时时间中的特定时刻。给定日期、时间和偏移量,我可以告诉你当地的挂钟时间和精确的 UTC 时间(通过应用偏移量)。

但是,值的挂起时间部分在特定时区内可能不明确。也就是说,日期和时间只有您忽略偏移量时。这就是TimeZoneInfo.IsAmbiguousTime 告诉你的。那如果不是偏移量,这个值就会是模棱两可的。墙上时间可能是那个时区的人可能会感到困惑的时间。

考虑这个方法有两种重载,一种采用DateTime,另一种采用DateTimeOffset

.KindDateTimeKind.Unspecified 时,DateTime 非常有意义。

DateTime dt = new DateTime(2016, 10, 30, 2, 0, 0);
TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("W. Europe Standard Time");
bool ambiguous = tz.IsAmbiguousTime(dt);  // true

对于其他类型来说意义不大,因为它首先会转换到给定的时区 - 但它仍然会做同样的事情:

DateTime dt = new DateTime(2016, 10, 30, 1, 0, 0, DateTimeKind.Utc);
TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("W. Europe Standard Time");
bool ambiguous = tz.IsAmbiguousTime(dt);  // true

DateTimeOffset 重载本质上与前面的示例执行相同的操作。无论偏移量是多少,它都会应用于日期和时间,然后仅在生成的日期和时间上检查歧义 - 就像在第一个示例中一样。

DateTimeOffset dto = new DateTimeOffset(2016, 10, 30, 2, 0, 0, TimeSpan.FromHours(1));
TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("W. Europe Standard Time");
bool ambiguous = tz.IsAmbiguousTime(dto);  // true

即使偏移量对该时区没有意义,它仍然会在比较之前应用。

DateTimeOffset dto = new DateTimeOffset(2016, 10, 29, 19, 0, 0, TimeSpan.FromHours(-5));
TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("W. Europe Standard Time");
bool ambiguous = tz.IsAmbiguousTime(dto);  // true

归结为重载的实现,本质上是:

// Make sure the dto is adjusted to the tz.  This could be a no-op if it already is.
DateTimeOffset adjusted = TimeZoneInfo.ConvertTime(dto, tz);

// Then just get the wall time, stripping away the offset.
// The resulting datetime has unspecified kind.
DateTime dt = adjusted.DateTime;

// Finally, call the datetime version of the function
bool ambiguous = tz.IsAmbiguousTime(dt);

你可以看到这个in the .net reference source here。他们将其压缩为两行,并在不适用 DST 时使用快捷方式作为开头以获得更好的性能,但这就是它的作用。

【讨论】:

很好的答案!您对墙上时间部分的解释和对时区值的无知有很大帮助!我不知道框架只使用 DateTimeOffset 的 DateTime 部分。 这只是关于 this 功能。还有很多其他地方确实考虑了偏移量。

以上是关于模棱两可的 DateTimeOffset 示例的主要内容,如果未能解决你的问题,请参考以下文章

模棱两可的函数参数

DateTimeOffset vs DateTime

选择 SQL Server DatetimeOffset 作为 .Net DateTimeOffset.Ticks

Haskell - 模棱两可的类型变量

“DateTime”和“DateTimeOffset”之间的区别[重复]

如何将 C# datetimeOffset 转换为指定格式