C# DateTime 不包含 TimeZone,但它以某种方式知道它以哪种方式被初始化。怎么可能?

Posted

技术标签:

【中文标题】C# DateTime 不包含 TimeZone,但它以某种方式知道它以哪种方式被初始化。怎么可能?【英文标题】:C# DateTime does not contain TimeZone yet it somehows know in which way it has been initialized. How it is possible? 【发布时间】:2022-01-19 15:40:19 【问题描述】:

我正在阅读 https://***.com/a/6519330/1375882 并且我了解 DateTime 不包含时区。实际上,DateTime 没有 TimeZone 成员字段。然而有ToLocalTime()ToUniversalTime() 成员。例如。我正在 +2 时区计算机中测试我的代码,它给出了跟踪(例如,现在它在 +2 时区是16.12.2021 16:17:23)并且代码给出:

DateTime tmpNow = DateTime.UtcNow;

//16.12.2021 14:17:23 - this if fine, DateTime is UTC
tmp = tmpNow.ToString();

//16.12.2021 16:17:23
tmp = tmpNow.ToLocalTime().ToString(); 

//16.12.2021 14:17:23 - this is fine, DateTime is UTC
tmp = tmpNow.ToUniversalTime().ToString(); 

DateTime tmpCustom = new DateTime(2021, 12, 16, 16, 9, 10);

//16.12.2021 16:09:10 - this is strange, I assume that DateTime(...) is aware that it is
// executed in +2 and that 16:09:10 is in +2 and therefore ToString should be 14:09:10
tmp = tmpCustom.ToString(); 

 //16.12.2021 18:09:10 - this is strange, see previous
tmp = tmpCustom.ToLocalTime().ToString();

//16.12.2021 14:09:10 - complete loss of understanding. If initialization has been to 14:09:10 UTC 
// as I expected, then ToLocalTime.ToString had to bee 16:09:10, but ToLocalTime()... was 18:09:10
// hence ToUniversalTime() definitely had to be 2 hours before - 16:09:10.
tmp = tmpCustom.ToUniversalTime().ToString();

所以,这真的很奇怪 - DateTime 以某种方式感知它是使用 UTC 初始化还是使用简单的日期时间数字组合进行初始化。如何解释 DateTime 代码行为以及这种对隐藏时区的依赖?

【问题讨论】:

这是因为 tmpCustom 是“未指定”。如果您在未指定的情况下调用 ToLocal,C# 假定开发人员最了解您并且时间实际上是通用的,因此它会继续进行,就好像它是“UTC 到本地”并添加 2 小时。同样,如果它是未指定的并且您调用 ToUniversal,它会假设您知道它是本地的,并需要 2 小时才能使其成为通用。您调用的方法是指定源类型和目标;如果未指定,您可以想象这些方法称为“IsActuallyLocalMakeItToUniversal”和“IsActuallyUniversalMakeItToLocal” 【参考方案1】:

所有这些都在文档中,您只需要知道在哪里查找即可。这里相关的是the Kind property。它是一个枚举:Local、Utc 或 Unspecified。

当为本地时,它采用当前文化的时区。见docs for the constructor you use:

Kind 属性被初始化为 DateTimeKind.Unspecified。

然后前往ToUniversalTime()

ToUniversalTime 方法返回的值由当前 DateTime 对象的 Kind 属性决定

[...]

未指定:假定当前的 DateTime 对象是本地时间,并且按照 Kind 是 Local 的方式执行转换。

因此,如果您将代码更改为 new DateTime(2021, 12, 16, 16, 9, 10, DateTimeKind.Local),则之后的方法将按预期工作 - 除了您声称您期望“14:09”在那里的评论之外。那不会发生。

【讨论】:

我猜你的意思是说如果 OP 会使用 DateTimeKind.Utc 它会按预期工作。由于明确声明 DateTimeKind.Local 不应该有所作为,因为它将被隐含地未指定,它再次被视为本地,所以没有区别。 @Rand 实际上 Local 和 Utc 都很好。未指定不是。如果您使用 Local 类型在 DateTime 上调用 ToLocalTime,您将获得相同的日期和时间。与 Utc 类型的 ToUniversal 相同。 抱歉,但我不明白为什么 .ToString() 应该返回 14:09:10,如果明确声明了 DateTimeKind.Local。 (在我的机器和 dotnetfiddle 上进行了测试,我得到了16:09:10,正如我所料) @Rand 对于任何种类的类型,new DateTime(2021, 12, 16, 16, 09, 10, DateTimeKind.*).ToString() 不可能返回他们用 cmets 表示的 14:09 时间。 ToString() 会打印出 DateTime 的组成部分,Hour 是 16。

以上是关于C# DateTime 不包含 TimeZone,但它以某种方式知道它以哪种方式被初始化。怎么可能?的主要内容,如果未能解决你的问题,请参考以下文章

django 1.4 timezone.now() 与 datetime.datetime.now()

`datetime.now(pytz timezone)` 啥时候失败?

日期时间、熊猫和时区问题:AttributeError:“datetime.timezone”对象没有属性“_utcoffset”

DateTime 对象上的不同 timezone_types

[转] datetime.nowdatetime.utcnow以及Django中的timezone.now之间的区别

如何在c#中获取用户本地机器时区