使用 InvariantCulture(2 位数年份)将“12/25/35”解析为日期

Jep*_*sen 4 .net windows datetime locale regional-settings

当我使用InvariantCulture来解析字符串时,同时使用:

var christ1 = DateTime.Parse("12/25/35", CultureInfo.InvariantCulture);
Run Code Online (Sandbox Code Playgroud)

和:

var christ2 = DateTime.ParseExact("12/25/35", "MM/dd/yy", CultureInfo.InvariantCulture);
Run Code Online (Sandbox Code Playgroud)

结果取决于应用程序在哪台计算机上运行。在一台机器上,我得到相当于:

20 年12 月 2535

而在另一台机器上,我得到了一个世纪前的日期,即:

19 年12 月 2535

到底是怎么回事?

Jep*_*sen 6

在这种情况下,所谓的不变文化并不是那么一成不变。这取决于您的操作系统版本和当前用户在操作系统中的区域设置。

在 .NET 术语中,感兴趣的属性TwoDigitYearMax在日历上,因此检查CultureInfo.InvariantCulture.DateTimeFormat.Calendar.TwoDigitYearMax(new GregorianCalendar()).TwoDigitYearMax. 覆盖的文档GregorianCalendar.TwoDigitYearMax

在用户未更改区域设置的 Windows 10 版本 1809(“2018 年 10 月更新”、“Redstone 5”)机器上(更多内容见下文),属性TwoDigitYearMax2029,因此您将获得19 年12 月 2535 .

但是在 Windows 10 版本 1903(“2019 年 5 月更新”、“19H1”)上,相同的属性返回2049,因此您的结果是2019 年12 月 2535。

但是,该值不仅取决于 Windows 版本。用户在区域设置中输入的内容也很重要。所以这并不是真正的“不变”。

您可以通过以下方式在控制面板中更改截止年份:

  1. 单击开始,单击左侧的“齿轮”图标(设置),然后单击时间和语言
  2. 点击区域左侧。
  3. 相关设置下,点击其他日期、时间和区域设置
  4. (您在控制面板中。)单击区域
  5. (在新的区域窗口中。)单击按钮附加设置...靠近页面底部,然后在新窗口中单击日期选项卡。
  6. 日历组中,调整当输入两位数的年份时,将其解释为介于 之间的年份

在较旧的 Windows 版本中,它可能是(粘贴自microsoft.com):

  1. 单击“开始”,指向“设置”,然后单击“控制面板”
  2. 双击区域设置图标。
  3. 单击日期选项卡。
  4. 在“输入两位数年份时,解释年份之间”框中,键入所需的截止年份,然后单击“确定”

或者您可以直接使用 Windows 注册表,转到 key HKEY_CURRENT_USER\Control Panel\International\Calendars\TwoDigitYearMax,对于该键中的每个相关“值”,保持名称组件不变并将值的数据组件编辑为(通常的十进制字符串表示)您想要的截止年份. 如果用户从未从控制面板更改此设置,则最里面的两个键 ( Calendars\TwoDigitYearMax) 尚不存在。有关与注册表混合的常见警告适用。

一旦您在控制面板中更改了年份,在您进行这些更改后启动的每个 .NET 进程都会在TwoDigitYearMax通过InvarantCulture!

因此,如果您希望跨操作系统版本和用户首选项保持一致的行为,我认为您不能使用InvariantCulture. 相反,这样的事情可以工作:

var tmp = (CultureInfo)(CultureInfo.InvariantCulture.Clone());
tmp.DateTimeFormat.Calendar.TwoDigitYearMax = 2039; // any cutoff you need
 // incorrect: tmp.Calendar.TwoDigitYearMax = 2039
var newInvariantCulture = CultureInfo.ReadOnly(tmp);
Run Code Online (Sandbox Code Playgroud)

然后newInvariantCulture在解析带有 2 位数年份的日期时使用该实例(您可以将它放在某个static readonly字段中)。

警告:每个都CultureInfo x带有两个不同的Calendars,它们可能具有不同的 2 位数年份最大值,即:

x.DateTimeFormat.Calendar.TwoDigitYearMax
  !=
x.Calendar.TwoDigitYearMax
Run Code Online (Sandbox Code Playgroud)