【问题标题】:DateTime.ParseExact with 7 digits / one or two digit monthDateTime.ParseExact 7 位数/一位或两位数月份
【发布时间】:2014-02-20 08:55:42
【问题描述】:

直到现在我还以为我会理解DateTime.ParseExact 的工作原理,但这令人困惑。为什么下面一行返回false

DateTime.TryParseExact("2013122", "yyyyMdd", CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out lastUpdate)

月份也可以有两位数。在我看来,它应该能够理解这意味着 2013 年 1 月 22 日。为什么我走错了路?我错过了什么还是有简单的解决方法?


同时我正在使用这个不是很优雅但有效的解决方法:

public static DateTime? ParseDate_yyyyMdd(String date)
{
    if (date == null)
        return null;
    date = date.Trim();
    if (date.Length < 7)
        return null;
    if (date.Length == 7)
        date = date.Insert(4, "0");
    DateTime dt;
    if (DateTime.TryParseExact(date, "yyyyMMdd", CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out dt))
        return dt;
    return null;
}

给出我想要的结果:

DateTime? date = ParseDate_yyyyMdd("2013122");
Console.Write(date.ToString()); // 01/22/2013

但是,我仍然对这种限制的原因感兴趣。也许有人也有更好的方法。

【问题讨论】:

  • 我认为答案是正确的,但这对我来说仍然很奇怪。我不明白 widest 规则。你是说嘿,尝试将我的前 4 个字符解析为一年,1 个字符之后解析为一个月,2 个字符之后解析为一天。但是方法会忽略它。
  • .. 它说:hey, your string doesn't have date separator, and that's why I ignore your format because it doesn't have widest form of your specifiers.
  • 如果您改用 ""yyyyMd",它会解析,但解释错误,lastUpdate=2013/Dec/2。
  • 您的解决方法可能不那么难看:if (date.Length == 7) { date = date.Insert(4, "0"); }
  • 我就这个问题联系了 .NET Framework 团队,here their response

标签: c# .net parsing datetime


【解决方案1】:

我确实在源代码中找到了它。这证实了活动挂图和 Mark Sturgill 的答案。

在某处调用内部 ParseByFormat 计数(在您的情况下)'M':

    // System.DateTimeParse
    private static bool ParseByFormat(ref __DTString str, ref __DTString format, ref ParsingInfo parseInfo, DateTimeFormatInfo dtfi, ref DateTimeResult result)
    {   
        ...
        case 'M':
            num = format.GetRepeatCount();
            if (num <= 2)
            {
                if (!DateTimeParse.ParseDigits(ref str, num, out newValue2) && (!parseInfo.fCustomNumberParser || !parseInfo.parseNumberDelegate(ref str, num, out newValue2)))
                {
                    result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
                    return false;
                }
            }

接下来的调用不是很有趣,除了 ParseDigits 调用中的 2 个小数字:

    // System.DateTimeParse
    internal static bool ParseDigits(ref __DTString str, int digitLen, out int result)
    {
        if (digitLen == 1)
        {
            return DateTimeParse.ParseDigits(ref str, 1, 2, out result);
        }
        return DateTimeParse.ParseDigits(ref str, digitLen, digitLen, out result);
    }

但现在我们进入了有趣的部分:

    // System.DateTimeParse
    internal static bool ParseDigits(ref __DTString str, int minDigitLen, int maxDigitLen, out int result)
    {
        result = 0;
        int index = str.Index;
        int i;
        for (i = 0; i < maxDigitLen; i++)
        {
            if (!str.GetNextDigit())
            {
                str.Index--;
                break;
            }
            result = result * 10 + str.GetDigit();
        }
        if (i < minDigitLen)
        {
            str.Index = index;
            return false;
        }
        return true;
    }

这意味着(正如已经回答的那样):

如果您不使用最大位数并且下一个字符也是数字,则格式无效。这就是以下返回 true 的原因:

DateTime.TryParseExact("20131-22", "yyyyM-dd", System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out lastUpdate)

不要问我这个限制的原因 - 它就在源代码中。

【讨论】:

  • 打败我。我只是在查看源代码:D
  • +1 感谢您的分析。但它也应该在文档中更明确地提到。我希望这会在某个时候得到解决。
【解决方案2】:

来自MSDN documentation

如果您不在自定义格式模式中使用日期或时间分隔符,请为提供程序参数使用不变的区域性以及每个自定义格式说明符的最宽形式。例如,如果您想在模式中指定小时,请指定较宽的形式“HH”,而不是较窄的形式“H”。

我认为原因是它试图从左到右解析(没有回溯)。因为没有分隔符,所以无法确定日期部分的边界。

【讨论】:

  • 你试过这样转换吗?对于yyyyMMdd,它仍然返回 false
  • @OndrejJanacek 它返回trueyyyyMMd。您的格式需要 8 个字符,其中字符串是 7 个字符。
  • @Soner Gönül 但它没有按预期解析
  • @Plue 实际上是。使用yyyyMMd 格式,您将获得02.12.2013(2013 年 12 月 2 日)。 Ondrej 的格式不会解析它们中的任何一个,因为至少与 "2013122" 的长度不同
  • @Soner Gönül 那么如何从2013122获得22.01.2013
【解决方案3】:

http://msdn.microsoft.com/en-us/library/ms131044(v=vs.110).aspx

如果您不在自定义格式模式中使用日期或时间分隔符,请为提供程序参数使用不变的区域性和每个自定义格式说明符的最宽形式。例如,如果您想在模式中指定小时,请指定较宽的形式“HH”,而不是较窄的形式“H”。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-25
    • 1970-01-01
    • 2015-02-18
    • 2011-01-02
    相关资源
    最近更新 更多