Подтвердить что ты не робот

DateTime.ParseExact с 7 цифрами/один или два знака месяца

До сих пор я думал, что буду понимать, как работает DateTime.ParseExact, но это путает. Почему следующая строка возвращает false?

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

В месяце могут также быть две цифры. На мой взгляд, он должен уметь понимать, что это означает 22 января 2013 года. Почему я ошибаюсь? Я что-то пропустил или есть простой способ обхода?


Между тем я использую это обходное решение, которое не очень элегантно, но работает:

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

Однако меня все еще интересует причина этого ограничения. Возможно, у кого-то есть лучший подход.

4b9b3361

Ответ 1

От Документация MSDN:

Если вы не используете разделители даты или времени в шаблоне пользовательского формата, используйте культуру инварианта для параметра поставщика и самую широкую форму каждого спецификатора специального формата. Например, если вы хотите указать часы в шаблоне, укажите более широкую форму "HH" вместо более узкой формы "H".

Я думаю, что причина в том, что он пытается разобрать слева направо (без возврата). Поскольку нет разделителей, он не может определить границы частей даты.

Ответ 2

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

Если вы не используете разделители даты или времени в шаблоне пользовательского формата, используйте культуру инварианта для параметра поставщика и самую широкую форму каждого специализированного спецификатора формата. Например, если вы хотите указать часы в шаблоне, укажите более широкую форму "HH" вместо более узкой формы "H".

Ответ 3

Я отследил это в исходном коде. Это подтверждает ответы флипчарта и Марка Штургилла.

Где-то вызывается внутренний 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:

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

Итак, это означает (как уже было сказано):

Если вы не используете максимальное количество цифр, а следующий символ также является цифрой, формат недействителен. Именно поэтому верно следующее:

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

Не спрашивайте меня о причинах этого ограничения - он есть только в исходном коде.