所以我终于能够理解这一点并在此处分享相同的信息,如果它对其他人也有帮助:
第一部分是将C#DateTime对象转换为字符串。 有许多格式说明符可以做到这一点,但对于我们来说,“r”和“o”格式说明符与DateTimeStyles.RoundtripKind
。 您可以在此处查看所有日期时间格式说明符。 看看使用这些格式说明符在代码中进行转换时会发生什么:
//r corresponds to RFC 1123 format (GMT date time format) var gmtDateTimeString = DateTime.Now.ToString("r"); //gives Fri, 23 Sep 2016 15:39:21 GMT //o corresponds to ISO 8601 (Local date time format) var localDateTimeString = DateTime.Now.ToString("o"); //gives 2016-09-23T15:39:21.8899216+05:30
您可以清楚地看到输出的字符串日期时间内嵌有信息,这表明:
现在是第二部分。 如果我要将这些日期时间字符串gmtDateTimeString
和localDateTimeString
回日期时间对象,那么我们需要解析它们。 因此,在传递给DateTime.Parse
API的DateTimeStyles.RoundtripKind
枚举值的帮助下,您实际上表示时区信息已经在字符串中烘焙,并且API使用该信息适当地解析日期时间。
通常,当日期时间数据以XML格式通过线路传输时,我使用的是ISO 8601格式,我在post中看到了这个格式,我在post中提到了这个问题。 因此,在解析从XML文档获取的这样的日期时间字符串时,使用DateTimeStyles.RoundtripKind
根据字符串中存在的时区信息获取正确的日期时间值是合适的。
我很难理解其他答案,所以我决定自己做一些研究。 幸运的是,.NET库的源代码可以在线获得。
DateTimeStyles.RoundTripKind
在源代码中有注释 :
// Attempt to preserve whether the input is unspecified, local or UTC
它或多或少与DateTimeStyles.RoundTripKind
上的MSDN文档一样模糊:
使用“o”或“r”标准格式说明符将DateTime对象转换为字符串时,将保留日期的DateTimeKind字段,然后将该字符串转换回DateTime对象。
通过浏览Reference Source网站,可以看出DateTimeStyles.RoundTripKind
的使用非常少。 基本上,如果设置了标志,那么它可以将DateTime
的类型修改为DateTimeKind.Utc
。 所以这是设置此标志的效果:有时解析的DateTime
值的Kind
属性设置为Utc
。
究竟何时发生这种情况由内部标志ParseFlags.TimeZoneUtc
。 确定何时设置此标志会更复杂,但据我所知,如果使用Z
或GMT
指定时区,解析器将设置此标志。 在源代码中有关于此的评论 :
// NOTENOTE : for now, we only support "GMT" and "Z" (for Zulu time).
我的结论是,如果使用o
或r
格式化时间戳,并且在解析时间戳时使用DateTime
, Utc
如果字符串中的时区是UTC时区,则将得到的DateTime
值的Kind
设置为Utc
。
但是,当没有设置标志时会发生什么? 确定这一点的最佳方法是对两个格式说明符进行一些实际测试。
往返(“O”,“o”)格式说明符
使用o
格式说明符时,时间戳的时区将为Z
表示UTC或+/-
与UTC的偏移量(例如, 2017-02-26T22:55:15.4923368+01:00
)。 这是一个表,显示从往返时间戳解析的DateTime
值的Kind
属性的值:
时区| RoundTripKind | 类 --------- + --------------- + ------ “Z”| 未指定| 本地 “Z”| 指定| 世界标准时间 不是“Z”| 未指定| 本地 不是“Z”| 指定| 本地
如果要以往返格式解析时间戳并且您希望时间戳的时区为UTC,则应指定DateTimeStyles.RoundTripKind
以确保解析的DateTime
值具有类型Utc
。
RFC1123(“R”,“r”)格式说明符
使用r
格式说明符时,时间戳将始终包含GMT
(即使原始DateTime
类型不是Utc
),因此r
格式的表不需要Timezone
列。 但是,我发现在解析RFC1123时间戳时, DateTime.Parse
和DateTime.ParseExact
行为有所不同:
方法| RoundTripKind | 类 ----------- + --------------- + ------------ 解析| 未指定| 本地 解析| 指定| 世界标准时间 ParseExact | 未指定| 不明 ParseExact | 指定| 不明
使用Parse
方法时,RFC1123格式的时间戳与往返格式的UTC时间戳相同。 但是,由于某种原因, ParseExact
方法忽略DateTimeStyles.RoundTripKind
标志。 解析往返格式化时间戳时不是这种情况。
如果要解析RFC1123格式的时间戳,则应使用Parse
方法并指定DateTimeStyles.RoundTripKind
或者如果您更喜欢ParseExact
方法,则必须将已解析时间戳的类型修改为Utc
。 您可以使用DateTime.SpecifyKind
方法创建新的时间戳。
结论
解析往返和RFC1123时间戳时,请指定DateTimeStyles.RoundTripKind
以确保解析的DateTime
值的Kind
属性为Utc
。
如果往返时间戳具有非零偏移,那么您必须将时间戳解析为DateTimeOffset
值以保留偏移量( Local
不会告诉您偏移量是什么 – 只是它可能与0不同)。
不要使用DateTime.ParseExact
来解析RFC1123时间戳(或者在解析时间戳后将类型更改为Utc
)。