DateTime.TryParseExact 不按预期工作

12

能否有人解释一下下面的代码段为什么返回true?

根据“d”自定义格式说明符的文档,“一个单独的数字日期不带前导零进行格式化”。那么,当我给TryParseExact一个带有前导零的单个数字日期时,为什么它不会失败呢?

DateTime x;
return DateTime.TryParseExact
(
    "01/01/2001",
    @"d\/MM\/yyyy",
    null,
    System.Globalization.DateTimeStyles.None,
    out x
);

更新

我觉得可能我最开始的表述不太清晰。实际上,我的问题是:为什么TryParseExact接受一些不完全匹配的值? 根据我看到的所有文档,“d”匹配“01”和“1”就像“MM”匹配“03”以及“March”一样都是错误的。 这里的问题不是这些值是等价的,而是它们与格式不匹配。

相关文档片段如下:

  • 来自TryParseExact字符串表示的格式必须完全匹配指定的格式。

  • 来自“d”说明符单个数字的日期会被格式化为没有前导零。

对我来说,似乎非常明显,“01”有一个前导0,因此与“d”不完全匹配。


可能造成危害的任何情况? - Petr Abdulin
按照您的逻辑,反向操作也应该失败:DateTime.TryParseExact("1/01/2001", @"dd\/MM\/yyyy", null, System.Globalization.DateTimeStyles.None, out x); - BFree
@veredesmarald:我刚在我的电脑上测试了一下(VS2010 - .Net 4),它运行得非常好... - BFree
@BFree: 我也在运行VS2010/.Net4。DateTime.TryParseExact("1/01/2001", @"dd\/MM\/yyyy", null, System.Globalization.DateTimeStyles.None, out x);返回falseDateTime.ParseExact("1/01/2001", @"dd\/MM\/yyyy", null, System.Globalization.DateTimeStyles.None);抛出FormatException... - verdesmarald
2
导致问题的情况:https://dev59.com/llrUa4cB1Zd3GeqPmruL - Petr Abdulin
显示剩余2条评论
5个回答

8

以下是来自.NET 4的DateTimeParse.ParseByFormat()源代码:

case 'd':
    // Day & Day of week 
    tokenLen = format.GetRepeatCount();
    if (tokenLen <= 2) { 
        // "d" & "dd" 

        if (!ParseDigits(ref str, tokenLen, out tempDay)) { 
            if (!parseInfo.fCustomNumberParser ||
                !parseInfo.parseNumberDelegate(ref str, tokenLen, out tempDay)) {

                result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
                return (false); 
            }
        } 
        if (!CheckNewValue(ref result.Day, tempDay, ch, ref result)) { 
            return (false);
        }
    }
    else
    {...}

解析器将 "d" 和 "dd" 归为一类。

2
如果你想知道的话,.NET代码(也就是微软公开的那部分)可以在http://referencesource.microsoft.com/netframework.aspx免费获取。 - seairth

3

看起来这种行为是设计上的,我认为它这样做是为了与其他字符串格式选项保持一致。

接下来看一个例子:

//Convert DateTime to string
string dateFormat = "d/MM/yyyy";
string date1 = new DateTime(2008, 10, 5).ToString(dateFormat);
string date2 = new DateTime(2008, 10, 12).ToString(dateFormat);

//Convert back to DateTime
DateTime x1, x2;
DateTime.TryParseExact(date1, dateFormat, null, System.Globalization.DateTimeStyles.None, out x1);
DateTime.TryParseExact(date2, dateFormat, null, System.Globalization.DateTimeStyles.None, out x2);

Console.WriteLine(x1);
Console.WriteLine(x2);

在第一部分中,ToString() 输出了一个两位数的日期,因为只写出一个数字的日期并没有太多意义(它会选择1还是2呢?)。因此,当转换为字符串时,"d" 表示一位或两位数字的日期,那么当转换回DateTime时也必须按照同样的方式进行。如果不这样做,在我的示例中,TryParseExact 中的转换回DateTime 将失败,这肯定不是期望的行为。
我认为,如果您确实需要完全匹配 d/MM/yyyy 格式,您可以使用正则表达式验证字符串,然后通过 ParseTryParseTryParseExact(取决于您的正则表达式的好坏,因为它必须处理闰年、30/31 天等)来传递它。

指定符“d”并不意味着“输出单个数字”,而是“输出没有前导零的日期”。显然,大于9的天数始终将以两位数字打印。步骤一中获得的两个字符串完全符合格式,因此应该可以成功解析。 - verdesmarald
@veredesmarald 对的,但如果TryParseExact按照你在问题中提到的期望方式工作,那么我的对date2的TryParseExact将会失败。这会让人非常困惑。如果ToString有一种方式运作,那么Parse也需要以同样的方式运作。 - rsbarro
不会的。我建议你去阅读一下 'd' 的规范。'12' 没有前导零,所以匹配得很好。 - verdesmarald
抱歉,我有点误解了问题。我以为你是想验证只有一个数字的日期字符串,没注意到你主要关心的是那个前导零。嗯,你可能有点头绪。我得再想一下这个问题... - rsbarro
我认为我的原始帖子可能没有明确说明什么是意外的,因此我已经相应地添加了一些更多的细节。 - verdesmarald

0

TryParseExact在这种情况下只是试图变得灵活。但是,当您使用格式说明符将日期转换为字符串时,“d”与“dd”应该按照广告中的方式工作。


方法名称中带有“Exact”的方法试图变得“灵活”,这种情况非常令人不安... - verdesmarald
2
与它们只有Parse和TryParse的数字数据类型不同,日期是一种奇怪的类型,因为它可能具有各种不同的格式。Parse试图猜测它,但ParseExact存在,这样您就可以说01/02/2001是mm/dd/yyyy而不是dd/mm/yyyy。它所允许的这种灵活性不会对任何情况造成伤害,因此我认为这不是一个错误,完全是故意的,你不觉得吗? - Bala R
既然行为与方法的文档不匹配(http://msdn.microsoft.com/en-us/library/system.datetime.tryparseexact.aspx),即[...]必须完全匹配指定格式,我认为这在技术上是一个错误(也许是文档中的错误!)。我认为,正确表示01/02/2001的工具是使用带有适当区域设置的常规Parse方法。 - verdesmarald

0

我认为它不会失败,因为TryParseExact足够聪明,知道'01' == '1'。


1
除了它没有。如果我想接受01,我会使用“dd”而不是“d”。 - verdesmarald
是的,我同意 - 方法名称中有“Exact”一词,因此它应该遵守您的格式。但我认为它只是试图更友好(或者可能是一个错误)。 - Josh M.

0

因为单个的 'd' 意味着你的 DateTime 值将会被转换成尽可能短的值,即如果没有必要就不带前导零。我想当你从字符串转换为 DateTime 时,它不应该失败,因为 TryParseExact 的格式字符串的主要目的是帮助转换为 DateTime,它像一个提示一样,而不是用于验证字符串格式。

如果你仍然需要强制的字符串格式验证,你可以使用 RegEx


1
这不是提示,而是旨在验证格式。TryParseExact的文档明确指出:“字符串表示的格式必须完全匹配指定的格式。” - verdesmarald
1
@veredesmarald:哦,我明白了。那么你刚刚回答了自己的问题。你可以向微软报告TryParseExact的错误,但我怀疑他们会修复它,因为他们有更多令人惊叹的工作要做 :) - Dmitrii Lobanov
他们不愿意“修复”这个方法的真正原因是因为这会破坏很多东西。相反,他们需要修复“d”的文档,以表明在用于解析格式字符串时其行为是不同的。 - binki

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