为什么DateTime.Parse无法解析UTC日期

102

为什么它无法解析这个:

DateTime.Parse("Tue, 1 Jan 2008 00:00:00 UTC")

3
在JavaScript中,当您使用new Date().toUTCString()时,IE9仍会错误地添加"UTC"。 - Chris S
对于任何在2009年之后查看此帖子的人,解决此问题的答案是https://momentjs.com/。 - Liam
10个回答

113

它无法解析该字符串,因为"UTC"不是一个有效的时区标识符。

UTC时间是通过在时间字符串末尾添加'Z'来表示的,因此您的解析代码应如下所示:

DateTime.Parse("Tue, 1 Jan 2008 00:00:00Z");
ISO 8601维基百科文章中获得以下信息:
如果时间是UTC时间,直接在时间后面添加一个'Z',不用加空格。 'Z'是零UTC偏移量的区域指示符。因此,“09:30 UTC”表示为“09:30Z”或“0930Z”。 “14:45:15 UTC”将变成“14:45:15Z”或“144515Z”。
UTC时间也称为'Zulu'时间,因为'Zulu'是北约音标字母'Z'的代号。

1
我的示例中的日期字符串来自Internet Explorer。 - David
@Dave:当你说它来自IE时,是指你从网页中提取它吗?你可能需要编写自己的替代DateTime解析器,以提取UTC并将其替换为Z。 - Simon P Stevens
在针对FF进行测试后,我忘记了在我向服务器发送POST请求时调用了toUTCString()方法。FF发送GMT而IE发送UTC。所以这次我不能责怪IE! - David
121
正如其他答案所指出的,带有Z标识符的UTC日期字符串确实可以被成功“解析”,但它也会被转换为本地时间,即返回一个KindLocal且已调整时间戳的DateTime。若要始终获取UTC DateTime,可以使用以下其中之一的方法: DateTime.Parse("2008-01-01 00:00:00Z", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal),就像@crokusek建议的那样,或者
DateTimeOffset.Parse("2008-01-01 00:00:00Z").UtcDateTime,就像 @ruffin 建议的那样。
- Sergii Volchkov
在类似于 http-equiv="last-modified" 的元标记中,格式是 2013-10-23@17:23:00 UTC,这是可能的吗? - PreguntonCojoneroCabrón
8
奇怪的是,DateTime.Parse("Tue, 1 Jan 2008 00:00:00Z").Kind 返回的是 Local,而不是 Utc - marsze

111

假设你使用格式为“o”的datetime,因此你有“2016-07-24T18:47:36Z”,处理这个时间的方法非常简单。

调用DateTime.Parse("2016-07-24T18:47:36Z").ToUniversalTime()

当你调用DateTime.Parse("2016-07-24T18:47:36Z")时,会得到一个设置为本地时区的DateTime。所以它将其转换为本地时间。

ToUniversalTime()会将其更改为UTC DateTime并将其转换回UTC时间。


假设您将Zulu时间发送到Web API,但在显示时却看到本地时间。我想有什么东西刚刚被点击了,谢谢。 - Andrew
或者不进行.ToUniversalTime()转换: DateTime.Parse("2016-07-24T18:47:36Z", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind); - Marcel Wolterbeek

54

只需使用那个:

var myDateUtc = DateTime.SpecifyKind(DateTime.Parse("Tue, 1 Jan 2008 00:00:00"), DateTimeKind.Utc);

if (myDateUtc.Kind == DateTimeKind.Utc)
{
     Console.WriteLine("Yes. I am UTC!");
}

您可以使用在线C#编译器测试此代码:

http://rextester.com/

希望对您有所帮助。


2
我其实有点惊讶,居然没有一种方法可以使用 DateTime.Parse("Some:Time:here", "Utc") - tisaconundrum
这正是我所需要的。我们将UTC DateTime字符串存储到数据库中,没有任何时区定义,当我们在应用程序中再次读取它们时,我们需要将其解析为UTC。 - DVS

24

或者在调用中使用AdjustToUniversal DateTimeStyle。

DateTime.ParseExact(String, String[], IFormatProvider, DateTimeStyles)

7
这个代码是有效的。 我使用了这个代码,并从UTC字符串中得到了正确的UTC日期时间:DateTime.TryParseExact("2012-01-30T00:28:00Z", "yyyy-MM-ddTHH:mm:ssZ", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out timeValue)); - Roboblob
29
这个也可行,无需格式,无需Z:DateTime.Parse("8/3/2013 1:02:41 AM", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); (将其翻译成中文) - crokusek
我正在处理一个类似于Roboblob的UTC时间字符串,并使用类似于Roboblob指定的代码来维护该值的UTC性:DateTime.ParseExact(utcTimeStringINeededToParse, "yyyy-MM-ddTHH:mm:ss.0000000Z", System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.AdjustToUniversal)。 - StackOverflowUser
这个解决方案对我有效,而其他的解决方案则没有用。请注意,还有 DateTimeStyles.AssumeUniversal ,但这不是你想要的,像第一个评论中建议的那样使用 AdjustToUniversal! - Carlos Sanchez

19

您需要指定格式:

DateTime date = DateTime.ParseExact(
    "Tue, 1 Jan 2008 00:00:00 UTC", 
    "ddd, d MMM yyyy HH:mm:ss UTC", 
    CultureInfo.InvariantCulture);

2
不错的建议,但是如果提供的日期字符串末尾没有包含UTC,这个方法就会失败。比如说,如果你传递了一个日期字符串末尾是+01的情况,就会导致一个FormatException异常。这取决于他想要做什么吧。 - Simon P Stevens

11

为了正确解析问题中给定的字符串而不改变它,请使用以下内容:

using System.Globalization;

string dateString = "Tue, 1 Jan 2008 00:00:00 UTC";
DateTime parsedDate = DateTime.ParseExact(dateString, "ddd, d MMM yyyy hh:mm:ss UTC", CultureInfo.CurrentCulture, DateTimeStyles.AssumeUniversal);

此实现使用字符串来指定正在解析的日期字符串的确切格式。DateTimeStyles参数用于指定给定字符串是协调世界时字符串。


2
根据文档,“返回的DateTime值的Kind属性为DateTimeKind.Local。”ParseExact正确解析为UTC,但在返回之前将其转换为本地时间。如果要使返回值作为UTC不变返回,请使用DateTimeStyles.RoundtripKind - Daniel Schilling

7

这不是有效的格式,但是"Tue, 1 Jan 2008 00:00:00 GMT"是有效的。

文档描述如下:

一个包含时区信息并符合ISO 8601标准的字符串。例如,以下两个字符串中的第一个指定协调世界时(UTC);第二个指定比UTC早七小时的时区时间:

2008-11-01T19:35:00.0000000Z

一个包含GMT标识符并符合RFC 1123时间格式的字符串。例如:

Sat, 01 Nov 2008 19:35:00 GMT

一个包含日期和时间以及时区偏移信息的字符串。例如:

03/01/2009 05:42:00 -5:00


在类似于这种格式的日期字符串中,将“UTC”替换为“GMT”非常有效,感谢您的提示。 - James Wilkins
3
请注意,这将返回一个 KindLocalDateTime 对象。看起来解决方法是使用 DateTimeOffset.Parse (然后使用 x.UtcDateTime) 来确保在解析过程中不会偏离 UTC 时间。 - ruffin

2
我已经编写了一个实用方法,结合了这里展示的所有技巧以及一些其他技巧:
    static private readonly string[] MostCommonDateStringFormatsFromWeb = {
        "yyyy'-'MM'-'dd'T'hh:mm:ssZ",  //     momentjs aka universal sortable with 'T'     2008-04-10T06:30:00Z          this is default format employed by moment().utc().format()
        "yyyy'-'MM'-'dd'T'hh:mm:ss.fffZ", //  syncfusion                                   2008-04-10T06:30:00.000Z      retarded string format for dates that syncfusion libs churn out when invoked by ejgrid for odata filtering and so on
        "O", //                               iso8601                                      2008-04-10T06:30:00.0000000
        "s", //                               sortable                                     2008-04-10T06:30:00
        "u"  //                               universal sortable                           2008-04-10 06:30:00Z
    };

    static public bool TryParseWebDateStringExactToUTC(
        out DateTime date,
        string input,
        string[] formats = null,
        DateTimeStyles? styles = null,
        IFormatProvider formatProvider = null
    )
    {
        formats = formats ?? MostCommonDateStringFormatsFromWeb;
        return TryParseDateStringExactToUTC(out date, input, formats, styles, formatProvider);
    }

    static public bool TryParseDateStringExactToUTC(
        out DateTime date,
        string input,
        string[] formats = null,
        DateTimeStyles? styles = null,
        IFormatProvider formatProvider = null
    )
    {
        styles = styles ?? DateTimeStyles.AllowWhiteSpaces | DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal; //0 utc
        formatProvider = formatProvider ?? CultureInfo.InvariantCulture;

        var verdict = DateTime.TryParseExact(input, result: out date, style: styles.Value, formats: formats, provider: formatProvider);
        if (verdict && date.Kind == DateTimeKind.Local) //1
        {
            date = date.ToUniversalTime();
        }

        return verdict;

        //0 employing adjusttouniversal is vital in order for the resulting date to be in utc when the 'Z' flag is employed at the end of the input string
        //  like for instance in   2008-04-10T06:30.000Z
        //1 local should never happen with the default settings but it can happen when settings get overriden   we want to forcibly return utc though
    }

请注意使用'-'和'T'(单引号括起来)。这是最佳实践,因为区域设置会干扰字符的解释,例如'-'被解释为'/'或'.'或任何您的区域设置表示日期组件分隔符的字符。我还包括了第二个实用程序方法,展示了如何解析从Web客户端提供给REST API后端的大多数常见日期字符串格式。享受吧。


1

只需要用“GMT”替换“UTC”——简单易行,不会破坏正确格式的日期:

DateTime.Parse("Tue, 1 Jan 2008 00:00:00 UTC".Replace("UTC", "GMT"))

-6
不确定为什么,但您可以将 DateTime.ToUniversalTime 包装在 try/catch 中,在更多的代码中实现相同的结果。
祝好运。

ToUniversalTime 永远不会抛出异常。 - porges

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接