22

我的计算机配置的文化不是en-US.

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

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

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

  • 任务栏

    在此处输入图像描述

  • 区域和语言设置

    在此处输入图像描述

  • Windows资源管理器

    在此处输入图像描述

  • 外表

    在此处输入图像描述

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

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

我得到一个不正确的日期:

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

.NET 中的这个错误在 Windows 中使用有错误的 .NET 代码的任何地方都很明显:

  • Windows 事件查看器:

    在此处输入图像描述

  • 任务调度器:

    在此处输入图像描述

  • SQL Server 管理工作室:

    在此处输入图像描述

我如何使 .NET 不出错?

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

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

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

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

日期应格式化为:

22. 11. 2011

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

22/11/2011

在过去,有些人可能会建议您不要为这种边缘情况而烦恼。这样的建议对我来说毫无意义。

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

奖金喋喋不休

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

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

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

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

尽管这些是您永远不必担心的极端情况。


2011 年 14 月 12 日更新:

该错误的另一个演示是Datetime.Parse无法解析DateTime.ToString

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

.Parse抛出异常。


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

除了不正确之外,日期分隔符的任何使用都被弃用。来自 MSDN:

LOCALE_SDATE

Windows Vista 及更高版本:不推荐使用此常量。改为使用LOCALE_SSHORTDATE。自定义语言环境可能没有单个统一的分隔符。例如,“2006 年 12 月 31 日”这样的格式是有效的。

LOCALE_STIME

Windows Vista 及更高版本:不推荐使用此常量。改为使用LOCALE_STIMEFORMAT。自定义语言环境可能没有单个统一的分隔符。例如,“03:56'23”这样的格式是有效的。

4

2 回答 2

7

这个特定的错误是由于一些特殊字符的转换,这些特殊字符在ShortDatePattern.

ShortDatePattern = "d//MM//yyyy";

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

唯一的解决方案似乎是在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));

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

日期转字符串

/// <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);
}
于 2011-11-30T13:59:01.813 回答
2

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

像这样的东西(在我的脑海中):

public static class DateTimeFix
{
    public static string FixedToString(this DateTime value)
    {
        if (IsEdgeCase())
            return FixEdgeCase(value);
        else
            return value.ToString();
    }

    // Edge case logic below
}

然后你使用:

DateTime.Now.FixedToString()

在你的代码中。

于 2011-11-30T12:20:43.457 回答