65

我想解析一个表示 UTC 格式的 DateTime 的字符串。

我的字符串表示包括 Zulu 时间规范,该规范应指示该字符串表示 UTC 时间。

var myDate = DateTime.Parse("2012-09-30T23:00:00.0000000Z");    

从上面我希望 myDate.Kind 是 DateTimeKind.Utc,而不是 DatetimeKind.Local。

我在做什么错以及如何解析代表 UTC 时间的字符串?

非常感谢!

4

5 回答 5

90

我会亲自使用我的Noda Time项目。(诚​​然,我作为作者有偏见,但它会更干净......)但如果你不能这样做......

使用DateTime.ParseExact指定您期望的确切格式,并在解析代码中包含DateTimeStyles.AssumeUniversal和:DateTimeStyles.AdjustToUniversal

using System;
using System.Globalization;

class Test
{
    static void Main()        
    {
        var date = DateTime.ParseExact("2012-09-30T23:00:00.0000000Z",
                                       "yyyy-MM-dd'T'HH:mm:ss.fffffff'Z'",
                                       CultureInfo.InvariantCulture,
                                       DateTimeStyles.AssumeUniversal |
                                       DateTimeStyles.AdjustToUniversal);
        Console.WriteLine(date);
        Console.WriteLine(date.Kind);
    }
}

(为什么它会在默认情况下调整到本地,AdjustToUniversal这超出了我的范围,但没关系......)

编辑:只是为了扩展我对 mattytommo 建议的反对意见,我的目的是证明它会丢失信息。到目前为止,我失败了——但以一种非常奇特的方式。看看这个 - 在欧洲/伦敦时区运行,时钟回到 2012 年 10 月 28 日,当地时间凌晨 2 点(UTC 凌晨 1 点):

DateTime local1 = DateTime.Parse("2012-10-28T00:30:00.0000000Z");
DateTime local2 = DateTime.Parse("2012-10-28T01:30:00.0000000Z");
Console.WriteLine(local1 == local2); // True

DateTime utc1 = TimeZoneInfo.ConvertTimeToUtc(local1);
DateTime utc2 = TimeZoneInfo.ConvertTimeToUtc(local2);
Console.WriteLine(utc1 == utc2); // False. Hmm.

看起来有一个“有或没有 DST”标志存储在某个地方,但如果我能弄清楚在哪里,我会被吹走的。TimeZoneInfo.ConvertTimeToUtc状态文档

如果dateTime对应的时间不明确,则此方法假定它是源时区的标准时间。

转换时似乎不是这种情况local2......

编辑:好的,它变得更加陌生 - 这取决于您使用的框架版本。考虑这个程序:

using System;
using System.Globalization;

class Test
{
    static void Main()        
    {
        DateTime local1 = DateTime.Parse("2012-10-28T00:30:00.0000000Z");
        DateTime local2 = DateTime.Parse("2012-10-28T01:30:00.0000000Z");

        DateTime utc1 = TimeZoneInfo.ConvertTimeToUtc(local1);
        DateTime utc2 = TimeZoneInfo.ConvertTimeToUtc(local2);
        Console.WriteLine(utc1);
        Console.WriteLine(utc2);

        DateTime utc3 = local1.ToUniversalTime();
        DateTime utc4 = local2.ToUniversalTime();
        Console.WriteLine(utc3);
        Console.WriteLine(utc4);
    }
}

所以这需要两个不同的UTC 值,用 解析它们DateTime.Parse,然后以两种不同的方式将它们转换回 UTC。

.NET 3.5 下的结果:

28/10/2012 01:30:00 // Look - we've lost information
28/10/2012 01:30:00
28/10/2012 00:30:00 // But ToUniversalTime() seems okay...
28/10/2012 01:30:00

.NET 4.5 beta 下的结果:

28/10/2012 00:30:00 // It's okay!
28/10/2012 01:30:00
28/10/2012 00:30:00
28/10/2012 01:30:00
于 2012-04-05T13:05:50.940 回答
28

像往常一样,乔恩的回答非常全面。也就是说,还没有人提到DateTimeStyles.RoundtripKind。如果要将 DateTime 转换为字符串并转换回相同的 DateTime(包括保留DateTime.Kind设置),请使用该DateTimeStyles.RoundtripKind标志。

正如 Jon 所说,正确的做法是在将 DateTime 对象转换为字符串时使用“O”格式化程序。这保留了精度和时区信息。同样,正如乔恩所说,DateTime.ParseExact在转换回来时使用。但是如果你使用 DateTimeStyles.RoundtripKind,你总能取回你输入的内容:

var now = DateTime.UtcNow;
var strNow = now.ToString("O");
var newNow = DateTime.ParseExact(strNow, "O", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind);

在上面的代码中,newNow与 是完全相同的时间now,包括它是 UTC 的事实。DateTime.Now如果运行除了替换之外的相同代码DateTime.UtcNow,您将获得nowback as的精确副本newNow,但这次是本地时间。

出于我的目的,这是正确的,因为我想确保传入和转换的任何内容都转换回完全相同的内容。

于 2012-08-21T22:49:36.477 回答
5

使用TimeZoneInfo以下内容使用该类:

var myDate = TimeZoneInfo.ConvertTimeToUtc(DateTime.Parse("2012-09-30T23:00:00.0000000Z"));
于 2012-04-05T13:02:54.070 回答
3

您可以对解析器方法使用以下格式:yyyy-MM-ddTHH:mm:ss.ffffffK

这将在最后正确处理时区信息(从 .NET 2.0 开始)。

回复:ISO 8601

于 2013-03-22T18:05:05.380 回答
3

之前遇到过类似的问题,几个小时后(并拉扯头发)最终使用DateTime.SpecifyKind

DateTime.SpecifyKind(inputDate, DateTimeKind.Utc);

我相信有人也在上面的评论中避开了这一点。

于 2015-07-15T20:50:13.423 回答