My computer is configured with a culture that is not en-US.
When using the native Win32 GetDateFormat function, i get correctly formatted dates:
This specific bug is due to the transformation of some special characters that aren't escaped in the patterns like ShortDatePattern
.
ShortDatePattern = "d//MM//yyyy";
/
in a pattern means "insert the date separator" but here the expansion is already done (at least on my system) when the string is copied from the system to the DateTimeFormat
structure. Sadly it is missing an escaping (Obviously not visible on any language not using a special character as a separator and not visible in english as it is replaced with itself)
The only solution seem to be to escape the separators in all the patterns of the DateTimeFormat
instance :
var c = new System.Globalization.CultureInfo("qps-ploc", true);
c.DateTimeFormat.ShortDatePattern =
c.DateTimeFormat.ShortDatePattern.Replace("/", "'/'");
c.DateTimeFormat.LongTimePattern =
c.DateTimeFormat.LongTimePattern.Replace(":", "':'");
Console.WriteLine(DateTime.Now.ToString(c));
Here's full code samples for all three common cases
/// <summary>Convert a date to the short date string in the current locale (e.g. 30//11//2011)</summary>
/// <param name="value">A DateTime to be converted to a short date string</param>
/// <returns>A string containing the localized version of the date</returns>
public static String DateToStr(DateTime value)
{
String format = CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern;
//The bug in .NET is that it assumes "/" in a date pattern means "the date separator"
//What .NET doesn't realize is that the locale strings returned by Windows are the Windows format strings.
//The bug is exposed in locale's that use two slashes as for their date separator:
// dd//MM//yyyy
// Which .NET misinterprets to give:
// 30////11////2011
// when really it should be taken literally to be:
// dd'//'MM'//'yyyy
//which is what this fix does
format = format.Replace("/", "'/'");
return value.ToString(format);
}
/// <summary>
/// Convert a time to string using the short time format in the current locale(e.g. 7::21 AM)
/// </summary>
/// <param name="value">A DateTime who's time portion will be converted to a localized string</param>
/// <returns>A string containing the localized version of the time</returns>
public static String TimeToStr(DateTime value)
{
String format = CultureInfo.CurrentCulture.DateTimeFormat.ShortTimePattern;
//The bug in .NET is that it assumes ":" in a time pattern means "the time separator"
//What .NET doesn't realize is that the locale strings returned by Windows are the Windows format strings.
//The bug is exposed in locale's that use two colons as their time separator:
// h::mm::ss tt
// Which .NET misinterprets to give:
// 11::::39::::17 AM
// when really it should be taken literally to be:
// h'::'mm'::'ss tt
//which is what this fix does
format = format.Replace(":", "':'");
return value.ToString(format);
}
/// <summary>
/// Convert a datetime to a string in the current locale (e.g. 30//11//2001 7::21 AM)
/// </summary>
/// <param name="datetime">A DateTime to be converted to a general string in the current locale</param>
/// <returns>A string containing the localized version of the datetime</returns>
public static String DateTimeToStr(DateTime datetime)
{
return DateToStr(datetime)+" "+TimeToStr(datetime);
}
Your best bet is to log the bug with MS and then create an extension method that detects these edge cases and handles them.
Something like this (off the top of my head):
public static class DateTimeFix
{
public static string FixedToString(this DateTime value)
{
if (IsEdgeCase())
return FixEdgeCase(value);
else
return value.ToString();
}
// Edge case logic below
}
Then you use:
DateTime.Now.FixedToString()
in your code.