Your situation looks like another widest form issue.
From DateTime.TryParseExact
method
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".
Because of this, parsing strings without any date or time separator might be a problem sometimes.
For example let's talk about "d"
custom format specifier. For formatting part, it formats your day part with a single digit without a leading zero. But for parsing, it can parse both 4
and 04
as well. It is the same as "M"
custom format specifier. I don't say you should use d
specifier for 04
. You can, but you shouldn't. You should always use the best formats that fits your string exactly.
Here is my opinion about what is going on here;
Because of the widest form rule, since your string doesn't have any date separator, you format should expect the widest forms of d
and M
which they are dd
and MM
. But I think these specifiers expect with a leading zero values (eg: 06
and 04
) when they used for single digits because what actually they are for. I couldn't find any evidence to support my theory but I am still investigating it.
I have a solution if your strings have always Mdyyyy
format. Maybe it is not the best solution but I think it is useful if your strings have a constant format;
public static DateTime? ParseDate_Mdyyyy(string date)
{
if (date == null)
return null;
if (date.Length < 6)
return null;
if (date.Length == 6)
date = date.Insert(0, "0").Insert(2, "0");
DateTime dt;
if (DateTime.TryParseExact(date, "MMddyyyy",
CultureInfo.InvariantCulture,
DateTimeStyles.None, out dt))
return dt;
return null;
}
Now you can use this method as;
string s = "642014";
DateTime? date = ParseDate_Mdyyyy(s);
Console.WriteLine(date.Value.ToString("ddMMyyyy")); // 04062014
I connected to .NET Framework Team about this problem here their response;
Hi Soner,
The parsing code is not really looking for any separator. Here is what
is going on:
For the case of using “MMddyyyy”
, the parsing starts in the method
DoStrictParse
which will call ParseByFormat
. This method will get the
first part of the format which is “MM”
and then calls ParseDigits
to
get the equivalent digits from the string we parse “642014”
which will
give “64”
. Note that till that time there is no validation happen if
the number is out of range for the month in the selected calendar
(which is Gregorian in our case here). The parsing code will repeat
the same process for “dd”
and will get equivalent part “20”
and then
will repeat it for “yyyy”
but this fail because it expect 4
digits and
we had only two (“14”
).
For the case of using “Mdyyyy”
, it fails because almost same reason
when parsing the part “M”
we know that the month can be 2
digits so
we’ll map it “64”
and will do the same with “d”
which map it to “20”
and then year will fail. I believe this is the reason the
documentation saying always use the widest form.
The recommendation here is either to use the 2
digit forms in the
string like "06042014"
and the parsing should succeed with “MMddyyyy”
and “Mdyyyy”
too. other option is to insert separator "6/4/2014"
or
"06/04/2014"
and parse as "M/d/yyyy"
Thanks, Tarek
Special thanks to; Tarek Mahmoud Sayed, Wes Haggard and Richard Lander.