.NET Date to string在Vista Pseudo-cultures中提供无效字符串

时间:2022-09-01 15:38:37

My computer is configured with a culture that is not en-US.

我的计算机配置了非en-US的文化。

When using the native Win32 GetDateFormat function, i get correctly formatted dates:

使用本机Win32 GetDateFormat函数时,我得到正确格式化的日期:

  • 22//11//2011 4::42::53 P̰̃M]
  • 22 // 11 // 2011 4 :: 42 ::53P̰M]

This is correct; and is also how Windows renders it:

这是对的;以及Windows呈现它的方式:

  • the taskbar

    任务栏

    .NET Date to string在Vista Pseudo-cultures中提供无效字符串

  • Region and Language settings

    区域和语言设置

    .NET Date to string在Vista Pseudo-cultures中提供无效字符串

  • Windows Explorer

    Windows资源管理器

    .NET Date to string在Vista Pseudo-cultures中提供无效字符串

  • Outlook

    外表

    .NET Date to string在Vista Pseudo-cultures中提供无效字符串

When i try to convert a date to a string in .NET using my current locale, e.g.:

当我尝试使用我当前的语言环境将日期转换为.NET中的字符串时,例如:

DateTime.Now.ToString();
DateTime.Now.ToString(CultureInfo.CurrentCulture);

i get an incorrect date:

我的日期不正确:

  • 22////11////2011 4::::42::::53 P̰̃M]
  • 22 //// 11 //// 2011 4 :::: 42 :::: 53P̰M]

This bug in .NET is evident anyplace in Windows that uses the buggy .NET code:

在使用有缺陷的.NET代码的Windows中,.NET中的这个错误很明显:

  • Windows Event Viewer:

    Windows事件查看器:

    .NET Date to string在Vista Pseudo-cultures中提供无效字符串

  • Task Scheduler:

    任务计划程序:

    .NET Date to string在Vista Pseudo-cultures中提供无效字符串

  • SQL Server Management Studio:

    SQL Server Management Studio:

    .NET Date to string在Vista Pseudo-cultures中提供无效字符串

How do i make .NET not buggy?

我如何使.NET没有错误?

How do i convert dates and times to strings using the current culture (correctly)?

如何使用当前文化(正确)将日期和时间转换为字符串?

Note: The user is allowed to set their Windows to any locale preferences they want. As it is now, my program will not handle valid settings properly. Telling the user, "Don't do that" is pretty mean-spirited.

注意:允许用户将Windows设置为他们想要的任何区域设置首选项。就像现在一样,我的程序将无法正确处理有效设置。告诉用户,“不要那样做”是非常卑鄙的。

A similar example comes from Delphi, which assumes that a date separator can never be more than one character. When Windows is configured with a locale that uses multiple characters for the date separator, e.g.:

类似的例子来自Delphi,它假设日期分隔符永远不能超过一个字符。当Windows配置了使用多个字符作为日期分隔符的区域设置时,例如:

  • sk-SK (Slovak - Slovakia) : .
  • sk-SK(斯洛伐克 - 斯洛伐克):.

where dates should be formatted as:

日期应格式化为:

22. 11. 2011

the code library fails to accept a date separator longer than one character, and falls back to:

代码库无法接受长于一个字符的日期分隔符,并回退到:

22/11/2011

In the past some might suggest that you not to bother with such edge cases. Such suggestions carry no weight with me.

在过去,有些人可能会建议你不要打扰这种边缘情况。这些建议对我没有任何影响。

i'll avoid getting into a pissing match with someone who wants to alter the meaning of my question by changing the title. But the question is not limited to pseudo-locales, specifically designed to find bugs in applications.

我会避免与想要通过更改标题来改变我的问题含义的人进行一场捣蛋比赛。但问题不仅限于伪语言环境,专门用于查找应用程序中的错误。

Bonus Chatter

Here's a unique list of date formats from around the world:

以下是来自世界各地的日期格式的唯一列表:

  • 11.11.25
  • 25年11月11日
  • 11.25.2011
  • 2011年11月25日
  • 11/25/2011
  • 2011/11/25
  • 2011.11.25
  • 2011.11.25
  • 2011.11.25.
  • 2011.11.25。
  • 2011/11/25
  • 2011/11/25
  • 2011-11-25
  • 2011-11-25
      1. 2011
      2. 2011
    1. 2011
  • 2011
  • 25.11.11
  • 11年11月25日
  • 25.11.2011
  • 25.11.2011
  • 25.11.2011 г.
  • 25.11.2011г。
  • 25.11.2011.
  • 25.11.2011。
  • 25//11//2011
  • 25 // 11 // 2011
  • 25/11 2011
  • 2011年11月25日
  • 25/11/2011
  • 25/11/2011
  • 25/11/2554
  • 25/11/2554
  • 25-11-11
  • 25-11-11
  • 25-11-2011
  • 25-11-2011
  • 29/12/32
  • 29/12/32

Of particular interest is the last example which doesn't use the gregorian calendar:

特别感兴趣的是最后一个不使用格里高历的例子:

  • Arabic (Saudi Arabia) ar-SA: 29/12/32 02:03:07 م
  • 阿拉伯语(沙特阿拉伯)ar-SA:29/12/32 02:03:07
  • Divehi (Maldives) dv-MV: 29/12/32 14:03:07
  • Divehi(马尔代夫)dv-MV:29/12/32 14:03:07
  • Dari/Pashto (Afghanistan) prf-AF / ps-AF: 29/12/32 2:03:07 غ.و
  • 达里/普什图语(阿富汗)prf-AF / ps-AF:29/12/32 2:03:07غ.و

Although those are edge cases that you'd never have to worry about.

虽然这些是你永远不必担心的边缘情况。


Update 14//12//2011:

更新14 // 12 // 2011:

Another demonstration of the bug is that Datetime.Parse cannot parse DateTime.ToString:

另一个bug的演示是Datetime.Parse无法解析DateTime.ToString:

String s = DateTime.Today.ToString("d");   //returns "14////12////2011"
DateTime d = DateTime.Parse(s);            //expects "dd//MM//yyyy"

The .Parse throws an exception.

.Parse会抛出异常。


Update 02//8, 2012 09::56'12:

更新02 // 8,2012 09 :: 56'12:

Any use of a date separator is depricated, in addition to being incorrect. From MSDN:

除了不正确之外,还会对任何日期分隔符的使用进行描述。来自MSDN:

LOCALE_SDATE

Windows Vista and later: This constant is deprecated. Use LOCALE_SSHORTDATE instead. A custom locale might not have a single, uniform separator character. For example, a format such as "12/31, 2006" is valid.

Windows Vista及更高版本:不推荐使用此常量。请改用LOCALE_SSHORTDATE。自定义区域设置可能没有单个统一的分隔符。例如,诸如“12 / 31,2006”的格式是有效的。

LOCALE_STIME

Windows Vista and later: This constant is deprecated. Use LOCALE_STIMEFORMAT instead. A custom locale might not have a single, uniform separator character. For example, a format such as "03:56'23" is valid.

Windows Vista及更高版本:不推荐使用此常量。请改用LOCALE_STIMEFORMAT。自定义区域设置可能没有单个统一的分隔符。例如,诸如“03:56'23”的格式是有效的。

2 个解决方案

#1


7  

This specific bug is due to the transformation of some special characters that aren't escaped in the patterns like ShortDatePattern.

这个特定的错误是由于一些特殊字符的转换,这些特殊字符在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)

/在一个模式中意味着“插入日期分隔符”,但是当字符串从系统复制到DateTimeFormat结构时,此处已完成扩展(至少在我的系统上)。可悲的是,它丢失了一个转义(显然在任何不使用特殊字符作为分隔符的语言中都不可见,并且在英语中不可见,因为它被自身替换)

The only solution seem to be to escape the separators in all the patterns of the DateTimeFormat instance :

唯一的解决方案似乎是在DateTimeFormat实例的所有模式中转义分隔符:

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

这是所有三种常见案例的完整代码示例

Date to string

/// <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);
}

Time to string

/// <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);
}

Datetime to string

/// <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);
}

#2


2  

Your best bet is to log the bug with MS and then create an extension method that detects these edge cases and handles them.

最好的办法是用MS记录错误,然后创建一个检测这些边缘情况并处理它们的扩展方法。

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.

在你的代码中。

#1


7  

This specific bug is due to the transformation of some special characters that aren't escaped in the patterns like ShortDatePattern.

这个特定的错误是由于一些特殊字符的转换,这些特殊字符在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)

/在一个模式中意味着“插入日期分隔符”,但是当字符串从系统复制到DateTimeFormat结构时,此处已完成扩展(至少在我的系统上)。可悲的是,它丢失了一个转义(显然在任何不使用特殊字符作为分隔符的语言中都不可见,并且在英语中不可见,因为它被自身替换)

The only solution seem to be to escape the separators in all the patterns of the DateTimeFormat instance :

唯一的解决方案似乎是在DateTimeFormat实例的所有模式中转义分隔符:

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

这是所有三种常见案例的完整代码示例

Date to string

/// <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);
}

Time to string

/// <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);
}

Datetime to string

/// <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);
}

#2


2  

Your best bet is to log the bug with MS and then create an extension method that detects these edge cases and handles them.

最好的办法是用MS记录错误,然后创建一个检测这些边缘情况并处理它们的扩展方法。

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.

在你的代码中。