Enabling Evil - Overriding System.DateTime's default ToString
Had an interesting back and forth with Tom Wayson today. He wanted to be able to "modify the default behavior of DateTime.ToString()."
So, pushing aside issues of localization and the questions of "why would you want to do that?" let's look at the problem.
He has an intranet application and doesn't need to localize it. He wants to 'enforce' a specific date/time format and wants to avoid writing DateTime.Now.ToString("MM/dd/yy HH:mm:ss") everytime. He also doesn't want to explicitly set the system-wide settings in Control Panel | Regional Settings.
He noted that the output of DateTime.Now.ToString on a standard en-us machine in the states gave this output:
8/15/2006 3:27:27 PM
It looks like the output of ToString is the combination of the DateTimeFormatInfo.ShortDatePattern and DateTimeFormatInfo.ShortTimePattern. So, he:
Imports System
Imports System.Globalization
Public Module MyModule
Sub Main()
Dim customCulture As CultureInfo = New CultureInfo("en-US")
customCulture.DateTimeFormat.ShortDatePattern = "MM/dd/yy"
'HH means 24 hour time, while hh means 12 hour time
customCulture.DateTimeFormat.ShortTimePattern = "HH:mm:ss"
System.Threading.Thread.CurrentThread.CurrentCulture = customCulture
System.Threading.Thread.CurrentThread.CurrentUICulture = customCulture
WL(DateTime.Now.ToString())
WL(DateTime.Now.ToShortDateString())
WL(DateTime.Now.ToShortTimeString())
End Sub
Sub WL(ByVal text As Object)
Console.WriteLine(text)
End Sub
End Module
But the output was still 12 hour time:
8/15/06 3:28:34 PM
Ah...a little Reflectoring shows us that the default format string for System.DateTime is "G" as in System.DateTime.ToString("G") where G is one of the presets.
From PowerShell we see:
PS>C:\Documents and Settings\shanselm\Desktop
[DateTime]::Now.ToString("g")
8/15/2006 3:28 PM
PS>C:\Documents and Settings\shanselm\Desktop
[DateTime]::Now.ToString("G")
8/15/2006 3:28:02 PM
PS>C:\Documents and Settings\shanselm\Desktop
[DateTime]::Now.ToString()
8/15/2006 3:28:15 PM
So we add:
Imports System
Imports System.Globalization
Public Module MyModule
Sub Main()
Dim customCulture As CultureInfo = New CultureInfo("en-US")
customCulture.DateTimeFormat.ShortDatePattern = "MM/dd/yy"
'HH means 24 hour time, while hh means 12 hour time
customCulture.DateTimeFormat.ShortTimePattern = "HH:mm:ss"
customCulture.DateTimeFormat.LongTimePattern = "HH:mm:ss"
System.Threading.Thread.CurrentThread.CurrentCulture = customCulture
System.Threading.Thread.CurrentThread.CurrentUICulture = customCulture
WL(DateTime.Now.ToString())
WL(DateTime.Now.ToShortDateString())
WL(DateTime.Now.ToShortTimeString())
End Sub
Sub WL(ByVal text As Object)
Console.WriteLine(text)
End Sub
End Module
And gets the output he expects, indicating that "G" is the combination of a ShortDate and a LongTime.
8/15/2006 15:28:57
About Scott
Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.
About Newsletter
We're now migrating to 2.0, though, and may be able to sneak a proper fix in. So what would be the "right" way to do this? A custom culture? It will need to work with databinding- ie setting the format string to {0:d} or similar...
;)
Scott, I find your comments about changing default behavior ('evil') interesting in light of our email conversation a week or two ago, regarding the hacking of Object.prototype in JavaScript . . . (You know, the one where you asked me "Why isn't it a good thing? It works...").
;)
Comments are closed.
CultureInfo customCulture = new CultureInfo(Thread.CurrentThread.CurrentCulture.Name);
customCulture.DateTimeFormat = DateTimeFormatInfo.InvariantInfo;
Thread.CurrentThread.CurrentCulture = customCulture;
string sNow = DateTime.Now.ToString();
This will output the same string that you have. But honestly, you should pass DateTimeFormatInfo.InvariantInfo to ToString() explicitly, and then you don't have to go about mangling the thread's cultureinfo. While you're at it, this thing is going across the wire? Maybe you ought to use UTC and a format that round trips.
string sNow = DateTime.UtcNow.ToString("u", DateTimeFormatInfo.InvariantInfo);
it comes out the other side...
DateTime dNow = DateTime.Parse(sNow, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.RoundtripKind);
DateTime dLocal = dNow.ToLocalTime();
c