DateTime.ParseExact with 7 digits / one or two dig

2019-02-12 14:43发布

Until now i thought that i would understand how DateTime.ParseExact works, but this is confusing. Why does following line returns false?

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

The month can also have two digits. In my opinion it should be able to understand that it means 22 January 2013. Why i'm on the wrong track? Did I miss something or is there an easy workaround?


Meanwhile i'm using this workaround which is not very elegant but works:

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;
}

Gives my desired result:

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

However, i'm still interested in the reason for this limitation. Maybe someone also has a better approach.

3条回答
倾城 Initia
2楼-- · 2019-02-12 14:47

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

If you do not use date or time separators in a custom format pattern, use the invariant culture for the provider parameter and the widest form of each custom format specifier. For example, if you want to specify hours in the pattern, specify the wider form, "HH", instead of the narrower form, "H".

查看更多
可以哭但决不认输i
3楼-- · 2019-02-12 14:54

I did track down that in source code. Which confirms the answers of flipchart and Mark Sturgill.

Somewhere an internal ParseByFormat is called which counts (in your case) the '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;
                }
            }

The next call is not very interesting, except for the 2 little numbers in ParseDigits call:

    // 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);
    }

But now we get to the fun part:

    // 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;
    }

So that means (as already answered):

If you do not use the maximum number of digits AND the next character is also a digit, the format is not valid. Which is the reason why the following returns true:

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

Don't ask me about the reasons for that limitation - it is just there in source code.

查看更多
淡お忘
4楼-- · 2019-02-12 14:57

From MSDN documentation:

If you do not use date or time separators in a custom format pattern, use the invariant culture for the provider parameter and the widest form of each custom format specifier. For example, if you want to specify hours in the pattern, specify the wider form, "HH", instead of the narrower form, "H".

I think that the reason is that it tries to parse left to right (without backtracking). Because there are no delimiters, it can't determine the boundaries of the date parts.

查看更多
登录 后发表回答