Ian*_*oyd 22 .net localization internationalization datetime-format pseudolocalization
我的电脑配置的文化不是en-US
.
使用本机Win32 GetDateFormat
函数时,我得到了正确格式化的日期:
这是对的; 以及Windows呈现它的方式:
任务栏
区域和语言设置
Windows资源管理器
外表
当我尝试使用我当前的语言环境将日期转换为.NET中的字符串时,例如:
DateTime.Now.ToString();
DateTime.Now.ToString(CultureInfo.CurrentCulture);
Run Code Online (Sandbox Code Playgroud)
我得到一个不正确的日期:
在使用有缺陷的.NET代码的Windows中,.NET中的这个错误很明显:
Windows事件查看器:
任务计划程序:
SQL Server Management Studio:
我如何使.NET没有错误?
如何使用当前文化(正确)将日期和时间转换为字符串?
注:允许用户设置自己的Windows任何区域的喜好,他们想要的.就像现在一样,我的程序将无法正确处理 有效设置.告诉用户,"不要这样做"是非常卑鄙的.
类似的例子来自Delphi,它假设日期分隔符永远不能超过一个字符.当Windows配置了使用多个字符作为日期分隔符的区域设置时,例如:
- sk-SK(斯洛伐克 - 斯洛伐克):
.
日期应格式化为:
Run Code Online (Sandbox Code Playgroud)22. 11. 2011
代码库无法接受长于一个字符的日期分隔符,并回退到:
Run Code Online (Sandbox Code Playgroud)22/11/2011
在过去,有些人可能会建议你不要打扰这种边缘情况.这些建议对我没有任何影响.
我会避免与想要通过更改标题来改变我的问题含义的人进行一场惹人喜爱的比赛.但问题不仅限于伪语言环境,专门用于查找应用程序中的错误.
以下是来自世界各地的日期格式的唯一列表:
特别感兴趣的是最后一个不使用格里高历的例子:
ar-SA
:29/12/32 02:03:07مdv-MV
Divehi (马尔代夫):29/12/32 14:03:07prf-AF / ps-AF
:29/12/32 2:03:07غ.و虽然这些是你永远不必担心的边缘情况.
更新14 // 12 // 2011:
该错误的另一个证明是Datetime.Parse
无法解析DateTime.ToString
:
String s = DateTime.Today.ToString("d"); //returns "14////12////2011"
DateTime d = DateTime.Parse(s); //expects "dd//MM//yyyy"
Run Code Online (Sandbox Code Playgroud)
在.Parse
抛出异常.
更新02 // 8,2012 09 :: 56'12:
除了不正确之外,任何使用日期分隔符都会被删除.来自MSDN:
LOCALE_SDATE
Windows Vista及更高版本:不推荐使用此常量.请
LOCALE_SSHORTDATE
改用.自定义区域设置可能没有单个统一的分隔符.例如,诸如"12/31,2006"的格式是有效的.LOCALE_STIME
Windows Vista及更高版本:不推荐使用此常量.请
LOCALE_STIMEFORMAT
改用.自定义区域设置可能没有单个统一的分隔符.例如,诸如"03:56'23"的格式是有效的.
这个特定的错误是由于某些特殊字符的转换,这些字符在模式中没有被转义ShortDatePattern
.
ShortDatePattern = "d//MM//yyyy";
Run Code Online (Sandbox Code Playgroud)
/
在一个模式中意味着"插入日期分隔符",但是当字符串从系统复制到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));
Run Code Online (Sandbox Code Playgroud)
这是所有三种常见案例的完整代码示例
/// <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);
}
Run Code Online (Sandbox Code Playgroud)
/// <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);
}
Run Code Online (Sandbox Code Playgroud)
/// <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);
}
Run Code Online (Sandbox Code Playgroud)