为何不同.NET版本执行DateTime.Now.ToString("hh tt", de文化)返回结果不同?
不同.NET运行时下DateTime格式化的区域文化输出差异问题
我对此十分困惑。我此前在相关回答中了解到应当依赖框架内置的本地化能力,因此下述观测结果让我十分意外:
当我运行如下代码时:
using System; using System.Globalization; public class Program { public static void Main() { Console.WriteLine(DateTime.Now.ToString("hh tt", new CultureInfo("de"))); } }
我得到的结果随运行的框架/编译器不同存在明显差异,示例基于UTC时间10:00(即上午10点)测试:
- 在try-dotnet上运行,得到输出:
10 vorm. - 在dotnetfiddle上选择.NET 5作为编译器运行,输出相同:
10 vorm.
更有意思的情况:
- 在dotnetfiddle上选择.NET 4.7.2作为编译器运行,得到输出:
10 - 在dotnetfiddle上选择Roslyn作为编译器运行,得到输出:
10
更奇怪的是,我在本地用控制台应用扩展了测试代码,如下:
Console.WriteLine(DateTime.Now.ToString("hh tt", new CultureInfo("de"))); Console.WriteLine(DateTime.Now.ToString("hh tt", new CultureInfo("de-DE"))); Console.WriteLine(DateTime.Now.ToString("hh tt", new CultureInfo("fr")));
以下是各环境的测试结果,额外加入try-dotnet结果作为对比:
| 区域文化 | .NET 5 | .NET Core 3.1 | .NET Core 2.0 | .NET Framework 4.8 | Mono | try-dotnet |
|---|---|---|---|---|---|---|
| "de" | 10 AM | 10 | 10 | 10 | 10 vorm. | 10 vorm. |
| "de-DE" | 10 | 10 | 10 | 10 | 10 vorm. | 10 vorm. |
| "fr" | 10 AM | 10 | 10 | 10 | 10 AM | 10 AM |
其中Mono列的结果是用Mono JIT compiler version 6.4.0编译文件后通过mono运行得到的,不使用Mono直接运行exe得到的结果与.NET Framework 4.8列一致。
请问该差异的来源是什么?是编译器还是框架的Bug?是功能损坏还是刻意设计的行为?
解答
这个差异和编译器没有任何关系,本质是不同.NET运行时依赖的区域文化数据源不同导致的,属于不同版本分支的设计差异,不是功能损坏。
- .NET Framework 4.x、.NET Core 2.x/3.x版本默认直接读取Windows系统自带的区域文化数据,而德语(de、de-DE)的Windows原生区域规则中早已取消了12小时制对应的上午/下午标识,所以
tt格式符对应的输出为空,你得到的10的结果是符合该数据源规则的。 - Mono运行时独立维护了一套区域文化数据库,它的德语规则里保留了
vorm./nachm.作为上午/下午的对应标识,法语规则也保留了AM/PM标识,所以输出和.NET Framework 有明显区别。 - .NET 5+版本开始默认跨平台使用ICU(Unicode国际组件)的区域文化数据,不同环境下的ICU版本、配置规则会直接影响输出结果:你本地的.NET 5环境用的是默认ICU规则,德语的12小时制标识被对齐为通用的AM/PM,而try-dotnet、dotnetfiddle的运行环境可能配置了兼容旧版规则的文化数据,所以输出
10 vorm.。如果在Windows上的.NET 5+环境开启了使用Windows原生区域数据的开关,输出又会和.NET Framework 4.x一致,tt部分为空。
你可以直接输出对应区域文化的AMDesignator属性验证这个结论:比如执行Console.WriteLine(new CultureInfo("de").DateTimeFormat.AMDesignator),不同环境下的输出就是你用tt格式符得到的内容,该值完全由运行时加载的文化数据库决定,和编译环节无关。
内容的提问来源于stack exchange,提问作者badasta
相关产品推荐
相关产品推荐

