Ninja Nichols

The discipline of programming

Why do my custom datetime strings STILL depend on client locale?

C#’s built in culture support is great. Just call ToString() on any DateTime object and it will be converted into a localized string with the appropriate formatting for the client’s locale. Take a look:

Locale Culture DateTime.ToString()
English en 12/15/2014 10:48:51 PM
Arabic ar 23/02/36 10:48:51 م
Chinese zh 2014/12/15 22:48:51
Danish da 15-12-2014 22:48:51
German de 15.12.2014 22:48:51
Hungarian hu 2014.12.15. 22:48:51

Not only is the order different, but we’ve got slashes, dashes and periods. Fortunately it’s all taken care of for us! But while that’s great for the front-end, having the back-end pass around these localized date strings is just asking for trouble.

Of course, the Right Way to format DateTime strings for back-end use is with the Round-trip format specifier (“O”), which creates an unambiguous, Culture-invariant, timezone aware and ISO 8601-complaint string representation (which looks something like 2014-12-15T22:48:51.0000000-05:00).

But alas, not every application was written to emit/accept ISO 8601-formatted datetime strings. And so, dear reader, I now present one of the many pitfalls to doing custom format strings correctly. Here is the gist of some code I encountered recently:

WebService.ExternalCall(someData, timestamp.ToString("MM/dd/yyyy hh:mm:ss"));

Not too bad really, there might be some issues if the app happens to run in a different timezone than the web service, but at least that custom format string will make sure those timestamps are always in the same format. Except it doesn’t. Soon we start finding entries like this 12-15-2014 22:48:51. Dashes?!! WTF, we clearly specified *slashes*!

Turns out the “/” character in a DateTime format string is actually just a placeholder for the localized date separator. It will be replaced by whatever the client set it to. Keep in mind that users are not locked into the regional defaults and are free to customize their own date/time settings.

Windows 7 Region and Language Settings
Don’t assume that your English-speaking users won’t go and change the time format to something unexpected.

To make the “/” to always be a slash character, we have to either escape it or explicitly override the client’s culture (the same goes for the time separator, “:”, although none of the default Cultures/Locales use anything other than “:”):

timestamp.ToString("MM'/'dd'/'yyyy hh':'mm':'ss");

-or-

timestamp.ToString("MM/dd/yyyy hh:mm:ss", DateTimeFormatInfo.InvariantInfo);

Seriously though, just use “O” if at all possible. It’s quick, easy and solves this problem and many others.

timestamp.ToString("O");