by Michael S. Kaplan, published on 2010/09/14 07:01 -04:00, original URI: http://blogs.msdn.com/b/michkap/archive/2010/09/14/10061557.aspx
The title almost sounds like a riddle.
Though not quite.
Read it a couple of times, it will make sense....
As often happens, it started with a question:
...an interesting bug.
According to http://msdn.microsoft.com/en-us/library/k494fzbf.aspxThe value of the current DateTime object is formatted using the general date and time format specifier ('G')..
But we have a report where calling DateTime.MinValue.ToString(“G”) is throwing an exception. It’s happening when the UmAlQuraCalendar is the calendar.
You can repro this in PowerShell by calling [DateTime]::MinValue.ToString("G", [System.Globalization.CultureInfo]::CreateSpecificCulture("ar”)).
Something I’m confused by is that an exception isn’t thrown when “G” isn’t specified. [DateTime]::MinValue.ToString([System.Globalization.CultureInfo]::CreateSpecificCulture("ar"))
Why the difference in behavior?
Okay, let's look at a simple repro of this.
Simple repro:
public static void Main() {
Console.WriteLine(DateTime.MinValue.ToString());
Console.WriteLine(DateTime.MinValue.ToString(CultureInfo.CreateSpecificCulture("ar")));
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("ar");
Console.WriteLine(DateTime.MinValue.ToString());
Console.WriteLine(DateTime.MinValue.ToString("G"));
Console.WriteLine(DateTime.MinValue.ToString("G", CultureInfo.CreateSpecificCulture("ar")));
}
Either of those last two lines in red will throw:
Unhandled Exception: System.ArgumentOutOfRangeException: Specified time is not supported in this calendar. It should be between 04/30/1900 00:00:00 (Gregorian date) and 05/13/2029 23:59:59 (Gregorian date), inclusive.
Parameter name: time
at System.Globalization.UmAlQuraCalendar.CheckTicksRange(Int64 ticks)
at System.Globalization.UmAlQuraCalendar.GetDatePart(DateTime time, Int32 part)
at System.DateTimeFormat.FormatCustomized(DateTime dateTime, String format, DateTimeFormatInfo dtfi, TimeSpan offset)
at System.DateTimeFormat.Format(DateTime dateTime, String format, DateTimeFormatInfo dtfi, TimeSpan offset)
at System.DateTime.ToString(String format)
at Test.Main()
Now this question is specifically made interesting by the doc topic quoted, which mentions:
The value of the current DateTime object is formatted using the general date and time format specifier ('G').
This method uses formatting information derived from the current culture. In particular, it combines the custom format strings returned by the ShortDatePattern and LongTimePattern properties of the DateTimeFormatInfo object returned by the Thread.CurrentThread.CurrentCulture.DateTimeFormat property. For more information, see CultureInfo.CurrentCulture. Other overloads of the ToString method enable you to specify the culture whose formatting to use and to define the output pattern of the DateTime value.
Perhaps if I gave the output of that code above an additional clue would come out of it all, interspersed with the code that caused it:
Console.WriteLine(DateTime.MinValue.ToString());
1/1/0001 12:00:00 AM
Console.WriteLine(DateTime.MinValue.ToString(CultureInfo.CreateSpecificCulture("ar")));
0001-01-01T00:00:00
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("ar");
Console.WriteLine(DateTime.MinValue.ToString());
0001-01-01T00:00:00
Console.WriteLine(DateTime.MinValue.ToString("G"));
{{EXCEPTION}}
Console.WriteLine(DateTime.MinValue.ToString("G", CultureInfo.CreateSpecificCulture("ar")));
{{EXCEPTION}}
Okay, so the doc claims related to the current culture are very misleading here, as are the claims that the "G" format is used by default (since it does "G"-like things but does not throw in a case where an explicit "G" does.
In the first three, it has a clear idea of what the MinValue is, and even though it is using the specified culture's (and current culture's when no culture is specified) format, it is not using the culture in question to do the actual formatting.
Let's just call it a bug. :-)
Now this is the kind of bug that starts as an oversight.
But once it is shipped, fixing it would basically break a person's code, any way you look at it.
To bwe honest I'd rather they fixed it anyway, but I can understand why they would not want to do that in this case given the nature of what kind of code might get broken.
This leads to one last question for some:
Why not fix the documentation?
It would be interesting to see proposals to doing the fix. I mean how do you document a bug like this, exactly?
I mean how, in an easy succinct way, do you document that the default way to do something in the simplest overload doesn't behave correctly?
Personally, I find the whole DateTime.MinValue concept to be kind of lame anyway, since it never makes sense as a value, even ignoring the Out of [implied] range issues.
It is the Theory vs. practice in software development issues, which point out that even though DateTime.MinValue doe not throw in the Gregorian calendar, that date is meaningless in that calendar. Supporting DateTime.MinValue for that calendar is incorrect too!
This whole section of .Net is slightly flawed conceptually, though really its only flaw was caused by the fact that it tried to formalize something that was going on in Windows. Perhaps, given that model decision, the choice of throwing on any of these dates in any calendar is the real flaw.
I mean, is it more impressive to throw when a standard (the Saudi calndar) sets a limit than when one is out of the bounds of a calendar's known range?
Somehow I doubt this one will ever be seriously taken up. Though it makes me miss Kim and Katy to think about it....
Alex Cohn on 14 Sep 2010 1:59 PM:
I understand the date range is limited for many calendars, and Arabic too. But I thought it should go back 1400 years or so. What is special about 04/30/1900?
Michael S. Kaplan on 14 Sep 2010 4:56 PM:
The Saudi standard that the UmAlQuraCalendar class follows has that as a start date; dates before that are undefined.
Doug Ewell on 15 Sep 2010 8:32 AM:
> But once it is shipped, fixing it would basically break a person's code, any way you look at it.
Developers rely on this exception being thrown?
Michael S. Kaplan on 15 Sep 2010 8:35 AM:
Actually, I was thinking of the other direction -- the throw is arguably the "right" behavior, so you would be adding a throw....
referenced by