C# 日期时间和偏移精细度

C# 日期时间和偏移精细度,c#,datetime,C#,Datetime,我正在研究DateTime的“机制”以及BCL中的相关类。 我对以下奇怪的行为感兴趣。考虑下面的代码片段: var dt1 = new DateTime(2014, 10, 24, 15, 30, 00, DateTimeKind.Unspecified); DateTimeOffset dto2 = new DateTimeOffset(dt1, TimeSpan.FromHours(6)); string input = dto2.ToString("o"); DateTime dt6 =

我正在研究
DateTime
的“机制”以及
BCL
中的相关类。 我对以下奇怪的行为感兴趣。考虑下面的代码片段:

var dt1 = new DateTime(2014, 10, 24, 15, 30, 00, DateTimeKind.Unspecified);
DateTimeOffset dto2 = new DateTimeOffset(dt1, TimeSpan.FromHours(6));
string input = dto2.ToString("o");
DateTime dt6 = DateTime.Parse(input, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind);
Console.WriteLine("DT6:" + dt6.ToString("o"));
我知道我应该将字符串解析为
DateTimeOffset
,但我只是想知道为什么库允许我在这里做这些事情,最有趣的是为什么输出会是:
“DT6:2014-10-24T13:30:00.00000000+04:00”
在解析过程中,它将偏移量设置为本地机器时区中给定时间段的实际偏移量,并根据初始偏移量和结果偏移量之间的增量调整小时数。 无论我作为
datetimestyle
选项提供什么,它都会这样做。结果值的类型设置为
Local
,也不管我是否提供
往返
值。 所以问题是为什么默认情况下它将种类设置为
Local
,第二个问题是
DateTime
在哪里存储偏移量,如果它存储偏移量,为什么我们需要
DateTimeOffset
类?

有几件事:

  • DateTime
    DateTimeOffset
    都是结构,而不是类。是的

  • 您的问题归结为理解
    DateTime.Parse
    方法的行为,而不是
    DateTime
    结构。我建议用那种方法读书。这里有很多很好的信息来描述你所看到的行为的微妙之处

  • 文档对退货类型(我的重点)有这样的描述:

    通常,Parse方法返回一个DateTime对象,其Kind属性为
    DateTimeKind.Unspecified
    。但是,解析方法也可以执行时区转换,并根据
    s
    样式
    参数的值,以不同方式设置种类属性的值:

    您正在
    s
    参数中传递一个值
    “2014-10-24T15:30:00.0000000+06:00”
    ,该值包含一个时区偏移量,因此您会受到图表中第一行的影响,结果会转换为您机器的本地时区,不管是什么

  • DateTimeStyles.RoundTripKind
    标志在这里不会有任何影响。只有当您的输入以
    Z
    结束而不是偏移量结束时,您才会看到效果,如上面图表中的第三行。在这种情况下,
    RoundTripKind
    标志确保
    DateTimeKind.Utc
    。没有它,您仍然可以获得
    DateTimeKind.Local

  • 不存储偏移量。
    DateTime
    中存储的唯一内容是将
    Ticks
    Kind
    属性组合成一个值

    有趣的是,请注意,由于此字段的两位是为种类保留的,因此有四种可能的状态,即使只有三个
    DateTimeKind
    值公开。事实上,还有一种隐藏的第四种,在和中的“DateTime的深层黑暗秘密”中描述。(基本上,它是
    DateTimeKind.Local
    的变体)

  • >P>如果您想要一个更好的日期/时间API,请考虑使用.< /P>
DateTime只能存储UTC或本地时间。因此,如果字符串指定了时区偏移量,那么它的Parse()方法必须做一些合理的事情。您看到了一个合理的选择,您没有指定DateTimeStyles.AdjustToUniversal。如果需要知道偏移量,那么当然必须使用DateTimeOffset.Parse()。“至少有一个你需要它的原因。”汉帕桑我很惊讶,DateTime实际上可以存储一个偏移量。它是作为时间跨度存储在里面的,你不知道吗?那么一个有趣的问题(虽然我觉得这是一个修辞性的问题)是为什么DateTimeOffset在DateTime自身试图处理偏移量的情况下被引入?它不存储偏移量。它使用2位来跟踪DateTimeKind。Parse()方法足够聪明,可以处理字符串中的偏移量,这与DateTime可以存储的内容无关。Matt,非常感谢。马特,我看了你的Pluralsight课程,非常有趣。有可能我会在莫斯科的下一次会议上进行一次演讲,希望我能在.NET中讨论日期和时间。这就是为什么我要收集这个话题上最有趣的东西。关于这个话题。如果DateTime只包含上面提到的两部分数据,那么当我从您的本地时区以“o”格式调用ToString时,偏移量来自何处。在Windows中为您的计算机全局设置的一个。哦,所以它每次都只是询问TimeZoneInfo,似乎。顺便说一句,我只是脱口而出“类”,当然,我知道这些类型是结构化的)。本质上,它使用
TimeZoneInfo.Local.GetUtcOffset
(或一个快捷等效的IIRC)。是的,我也总是混淆结构/类。有时候我只是说“对象”,这样我就不用想太多了。尤其是在谈论其他不一定有课程的语言时