如何判断给定的字符串是否为有效的UTC DateTime格式?

4
我需要允许接受UTC日期时间:http://www.w3.org/TR/NOTE-datetime
例如:
 - Year:
         YYYY (eg 1997)    Year and month:
         YYYY-MM (eg 1997-07)    Complete date:
         YYYY-MM-DD (eg 1997-07-16)    Complete date plus hours and minutes:
         YYYY-MM-DDThh:mmTZD (eg 1997-07-16T19:20+01:00)    Complete date plus hours, minutes and seconds:
         YYYY-MM-DDThh:mm:ssTZD (eg 1997-07-16T19:20:30+01:00)    Complete date plus hours, minutes, seconds and a decimal fraction of
   a second
         YYYY-MM-DDThh:mm:ss.sTZD (eg 1997-07-16T19:20:30.45+01:00) where:

        YYYY = four-digit year
        MM   = two-digit month (01=January, etc.)
        DD   = two-digit day of month (01 through 31)
        hh   = two digits of hour (00 through 23) (am/pm NOT allowed)
        mm   = two digits of minute (00 through 59)
        ss   = two digits of second (00 through 59)
        s    = one or more digits representing a decimal fraction of a second
        TZD  = time zone designator (Z or +hh:mm or -hh:mm)

如何将字符串转换为特定格式的DateTime?
我编写了以下代码:
 private const string Format = "YYYY-MM-DDThh:mm:ssZ";

        public DateTime? Parse(string modifiedSince)
        {
            // how to know whether it's a valid DateTime in UTC format?
        }

但它总是返回null,这意味着它无法解析为DateTime。

它应该成功验证以下UTC值,但它没有:

2013-05-10
2013-05-10T05:04:10
2013-05-10T05:04:10.40
013-05-10T05:04:10.4Z

(通常情况下Z是可选的)

如何使用DateTime.Parse或.ParseExact使其成功返回上述格式的日期?

所有其他日期格式应该失败,例如20130510。

我可以编写一个正则表达式来代替吗?


一个单独的TryParseExact无法验证您列出的所有值。关键在于exact。输入的格式必须完全匹配指定的格式(包括秒的小数部分的精度)。 - yoozer8
请使用格式“yyyy-MM-dd”;YYYY和DD无效。请在此处查看自定义格式字符串:http://msdn.microsoft.com/en-us/library/8kb3ddd4.aspx。 - arunlalam
格式不应该是yyyy-MM-ddThh:mm:ssZ吗? - vc 74
是的,你说得对 @vc 74。 - The Light
4个回答

10
您缺少了一些重要的信息。UTC不是一个格式 UTC指的是“协调世界时”(是的,缩写故意打乱了顺序)。这是本初子午线上的一个固定时钟,实际上相当于GMT。它不会因为“夏令时”或“夏季时间”而改变,并且在谈论其他时区的偏移量时,我们将其置于零点。
您描述的格式称为ISO8601。从技术上讲,ISO8601标准定义了几种格式,但最常用的一种也在RFC3339中定义,并且仅涵盖您列出的最后两种格式。换句话说,ISO8601允许以不同的精度表示多个时间,但RFC3339要求值高达
现在当涉及到.Net时,您还需要了解另外一些重要的概念。具体来说,您需要知道DateTime.KindDateTimeOffset
当您有一个包含Z+00:00结尾的字符串值时,您知道它代表UTC时间,并且可以存储在具有DateTime.Kind == DateTimeKind.UtcDateTime中。
如果您有其他偏移量,例如+01:00-04:00,那么您应该将其存储在DateTimeOffset对象中。如果您尝试将其存储在DateTime中,则必须选择是忽略偏移量还是应用并将时间存储为UTC。
如果没有结尾的任何内容,那么您就存在歧义。您可以将其存储在Datetime.Kind == DateTimeKind.UnspecifiedDateTime中,但必须非常小心地处理它。它没有关于该值来源的时区或偏移量的任何信息-因此,任何数学运算可能是不正确的。例如,您应该永远不要通过减去具有未指定(或本地)类别的两个DateTime值来获得持续时间。有关详细信息,请参见此博客文章

最后一件事,要理解 .Net 没有内置类来表示不带时间的日期。因此,我们通常使用午夜的 DateTime - 但您无法区分这与实际给出午夜时间的值。换句话说,在解析 2013-05-102013-05-10T00:00:00 后,它们是等价的。

以下是一些代码供您参考。

public DateTime? Parse(string s)
{
    // you may want to add a few more formats here
    var formats = new[] { "yyyy-MM-dd",
                          "yyyy-MM-ddThh:mm:ss",
                          "yyyy-MM-ddThh:mm:ssZ" };

    DateTime dt;
    if (DateTime.TryParseExact(s, formats,
                               CultureInfo.InvariantCulture, // ISO is invariant
                               DateTimeStyles.RoundtripKind, // this is important
                               out dt))
        return dt;

    return null;
}

如果您打算使用这种方法,还应该了解DateTimeOffset.TryParseExact


好的回答,谢谢。但是这里的“RoundtripKind”是什么意思呢?另外,InvariantCulture 能确保我们得到 ISO 8601 吗?我很惊讶它似乎并不总是能够做到:https://dev59.com/cmkw5IYBdhLWcg3wn7-O#23801542/ - Max Barraclough

2
TryParseExact,顾名思义,只能成功地解析精确匹配指定格式的字符串。根据文档:
“字符串表示的格式必须与指定格式完全匹配。”
你提供的所有示例都不匹配你指定的格式字符串。如果您希望接受一些格式集 - 例如由UTC标准定义的格式 - 并拒绝所有其他格式,请使用接受可能格式数组的TryParseExact重载。
该重载非常适合具有少量可接受格式的情况,例如问题中提到的UTC标准指定的格式。

如何判断一个日期时间字符串是否为有效的UTC日期时间? - The Light
你可以使用标准中指定的有效格式集合 - 这些已经在你的问题顶部包含了 - 并将这些格式字符串(或它们的.NET等效项,如此处所述)提供给我提到的TryParseExact重载方法的数组参数。 - Dan J
哦,亲爱的,我希望有一种比手动检查那8-9个组合更容易的方法。 - The Light
1
您希望有一种比测试字符串是否为有效格式更简单的方法来测试字符串是否为有效格式吗? - Preston Guillot
据我所知,你问题中展示的可接受格式字符串不幸地没有被内置到.NET框架中。至少只有六个。值得澄清的是:为什么你需要确定一个字符串是否符合有效的UTC时间戳格式? - Dan J

1

实际上,您所需要的只需使用TryParseExact(string input, string[] formats, ....)的另一个“不太出名”的构造函数即可轻松实现。

  • 您可以按以下方式定义可接受格式的数组,然后将其传递给上面的TryParseExact

string[] acceptableFormats =
{
"M/d/yyyy h:mm:ss tt", "M/d/yyyy h:mm tt",
"MM/dd/yyyy hh:mm:ss", "M/d/yyyy h:mm:ss",
"M/d/yyyy hh:mm tt", "M/d/yyyy hh tt",
"M/d/yyyy h:mm", "M/d/yyyy h:mm",
"MM/dd/yyyy hh:mm", "M/dd/yyyy hh:mm"
};

if (DateTime.TryParseExact(dateString, acceptableFormats,  
new CultureInfo("en-US"),   
DateTimeStyles.None,   
out dateValue))  
{  
// Do something useful  
}

这是相关构造函数的MSDN链接

0

试试这个;

bool validDate = false;
DateTime dt = new DateTime();
try
{
    dt = Convert.ToDateTime(modifiedSince);
    validDate = true;
}
catch(FormatException) { string message = "Not a valid date..."; }

if (validDate)
{
    //Do whatever else you need to do with the validated date.
}

无论是否涵盖了所有已知的日期格式尚不清楚,但这是我使用的方法,希望它对您有用。


至于输出格式,那就由你决定了。一旦你获得了有效的 DateTime,你可以将其呈现为字符串,例如 dt.ToString("YYYY-MM-DDT hh:mm:ssZ");虽然我不确定 Z 会怎么样,因为我自己从未尝试过。 - Mike Varosky

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