Windows 10 breaks .NET date parsing in certain locales
If youâ€™re running an app in Windows 10 or Windows Server 2016 under .NET 4.0 or newer, you will see date parsing errors under seven locales. This blog post will explain the issue, the fix (itâ€™s coming!) and the workarounds.
First off, I would like to thank the .NET Framework team at Microsoft for fixing this speedily. Jay Schmelzer and his people also helped by giving me all the details and proofreading this blog post. That said, my recommendations and any possible mistakes are still my own, not theirs. :-)
Edit 2015-10-09: This has been fixed now. The updates are as follows:
- KB3093266 – Windows 10
- KB3088956 – Windows Server 2012 R2 and Windows 8.1
- KB3088955 – Windows Server 2012 and Windows 8
- KB3088957 – Windows 7 SP1, Windows Server 2008 SP2, Windows Server 2008 R2 SP1, and Windows Vista SP2
Windows 10 changes the date and time formatting settings for some cultures. Of particular concern are seven cultures for three different regions:
- Norwegian BokmĂĄl (â€śNorwayâ€ť and â€śSvalbard and Jan Mayenâ€ť variants)
- Serbian (variants â€śCyrillic, Kosovoâ€ť, â€śLatin, Montenegroâ€ť, â€śLatin, Serbiaâ€ť and â€śLatin, Kosovoâ€ť).
For these seven cultures, Windows 10 changes the date and time separators to be the same. For example, in Finnish, the standard date format used to be 26.8.2015 21:08, while it is now 26.8.2015 21.08 â€“ note the subtle change in the time separator.
Note that any change in separators can cause your code to break â€“ for example, if you have written a parser that imports time-of-day values by relying on certain culture settings, you may find that your parsing logic no longer works. But the fact that the time separator changed to be the same as the date separator creates an issue far more insidious and severe.
In all currently released versions of .NET, the DateTime.Parse method has a shortcoming: It always fails to parse a date or a date+time combination in a culture where the date/time separators are the same character. This bug, together with Windows 10â€™s culture changes, breaks the previously hard rule of DateTime.Parse always being able to parse the cultureâ€™s default DateTime representation. Now,
DateTime.Parse(DateTime.Now.ToString()) no longer works under the described conditions. Neither does
DateTime.Parse(DateTime.Now.ToShortDateString()), which is somewhat counterintuitive since the changed time separator isnâ€™t even involved, but true nonetheless â€“ the parser thinks itâ€™s parsing a time instead of a date.
If you own the callsite to DateTime.Parse, you can use ParseExact (see an example below in the workarounds section) and avoid the issue. In fact, using ParseExact is a best practice anyway, if you know the format youâ€™re parsing from. But the real is problem is that almost all of .NET Framework relies on the assumption that default datetime representations survive format/parse roundtrips â€“ and thus, many parts of .NET donâ€™t use ParseExact.
The most problematic failures will occur in databinding and modelbinding code. You will find that WPF, WinForms and Modern (UWP) Apps will fail when binding dates to controls. ASP.NET MVC will fail on modelbinding from form input. It will simply be impossible to enter a valid date. Depending on your scenario, your application may throw FormatExceptions, your DateTime fields will always show errors and refuse to accept input, or your modelbound ASP.NET input will be empty.
The fix (and what about previous .NET or Windows versions?)
Microsoft has fixed this issue in a forthcoming update to .NET 4.6. This patch is planned to ship in Windows 10â€™s September update. DateTime.Parse will be improved to deal with a scenario where the two separators are the same. The separators wonâ€™t change back, and the new separators may still break your code in other scenarios â€“ but thatâ€™s not a Windows or .NET Framework bug.
How about previous versions of Windows? Since it was Windows 10 that introduced the new separators, you wonâ€™t hit this on older versions of Windows. Technically you could – if your system was running a custom locale with date/time separators set to the same character, but in practice custom locales are rare, and such custom locales probably nonexistent. At the moment, there are no plans to patch this separately for older Windows versions.
How about previous versions of .NET Framework? First off, you will not see this if youâ€™re running an app on a version older than .NET 4.0. That is because .NET 4.0 is the first version of the Framework to use the OS culture data â€“ versions before that carried their own regional settings, and they donâ€™t contain cultures where date and time separator is the same character.
If you can wait until the update comes out, you donâ€™t need to read the rest of this blog post. If you canâ€™t, Iâ€™ll give you some ideas on what to do.
As I already stated, if you have an explicit DateTime.Parse call, replace it with ParseExact. That is fairly straightforward to do if you can special case the mentioned 7 cultures. For example, for a Finnish date-only field, you can do
DateTime.Parse(stringToParse, "d.M.yyyy", CultureInfo.GetCultureInfo("fi-FI")).
For all the other scenarios, you need to apply the previous code fixâ€¦ The trick is knowing where to do it.
- For Windows Forms data binding, you need to set the Binding.Parse event to a logic that special cases the said cultures. For a big app, the problem is getting that set up for all the relevant bindings in your application. You might want to explore reflection or similar strategies for doing programmatic injection of custom parsers for all DateTime fields. If you already have custom parsers in place, good luck marrying these two together.
- For WPF data binding, you might want to embed this into an IValueConverter (see tutorial). Again, finding all the necessary injection spots may be a headache.
- For ASP.NET MVC, you need to write a custom ModelBinder. The nice thing here is that you are able to apply this binding easily to all DateTime/Nullable<DateTime> objects by just adding your binder to the ModelBinders collection in your application startup. However, it is important to remember that the default behavior for ASP.NET MVC is to use InvariantCulture for parsing HTTP GET request parameters but to use the CurrentCulture for HTTP POST parameters (more here).
- If you are at the deployment end of the stick (i.e. trying to get those .NET apps to run on computers you administer), you can turn the custom locale approach to your benefit and create a locale that has separate date & time separators. You need to use Locale Builder for that â€“ and yes, it works on Windows 10 even though the blog post only talks about 8.1. Of course, this doesnâ€™t help you as a developer if you cannot enforce the custom locale on your customers.
If you come across other scenarios or have solutions to share, add a comment. Please let me know if you have other feedback / questions / clarifications as well. Thanks!