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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With