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
Однако меня все еще интересует причина этого ограничения. Возможно, у кого-то есть лучший подход.
Ответы
Ответ 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)
Не спрашивайте меня о причинах этого ограничения - он есть только в исходном коде.