虽然日期和时间值表示某个时刻,但其字符串表示形式受文化影响,并且取决于用于显示日期和时间值的特定文化约定以及该文化使用的历法。 本主题探讨对 .NET 中日历的支持,并讨论处理日期值时日历类的使用。
.NET 中的日历
.NET 中的所有日历都派生自 System.Globalization.Calendar 提供基本日历实现的类。 从 Calendar 类继承的类之一是 EastAsianLunisolarCalendar 类,该类是所有 lunisolar 日历的基类。 .NET 包含以下日历实现:
ChineseLunisolarCalendar,表示中国农历。
GregorianCalendar,表示公历。 此日历通过System.Globalization.GregorianCalendarTypes 枚举进一步划分为各种子类型(例如阿拉伯语和中东法语)。 该 GregorianCalendar.CalendarType 属性指定公历的子类型。
HebrewCalendar,表示希伯来历。
HijriCalendar,表示 Hijri 日历。
JapaneseCalendar,表示日本日历。
JapaneseLunisolarCalendar,代表日本的阴阳历。
JulianCalendar,表示朱利安日历。
KoreanCalendar,表示朝鲜日历。
KoreanLunisolarCalendar,表示朝鲜阴阳历。
PersianCalendar,表示波斯历。
TaiwanCalendar,表示台湾日历。
TaiwanLunisolarCalendar,表示台湾阴阳历。
ThaiBuddhistCalendar,表示泰国佛教日历。
UmAlQuraCalendar,表示 Um Al Qura 日历。
可以通过以下两种方式之一使用日历:
作为特定文化使用的日历。 每个 CultureInfo 对象都有一个当前日历,该日历是对象当前使用的日历。 所有日期和时间值的字符串表示形式会自动反映当前文化及其当前日历。 通常,当前日历是文化的默认日历。 CultureInfo 对象还具有选择性日历,其中包括文化可以使用的其他日历。
作为与特定区域性无关的独立日历。 在这种情况下, Calendar 方法用于将日期表示为反映日历的值。
请注意,六个日历类 (ChineseLunisolarCalendar、、JapaneseLunisolarCalendar、JulianCalendarKoreanLunisolarCalendar、 PersianCalendar和TaiwanLunisolarCalendar) 只能用作独立日历。 任何文化不将其用作默认日历或可选日历。
日历和文化
每种文化都有一个默认日历,该日历由 CultureInfo.Calendar 属性定义。 该 CultureInfo.OptionalCalendars 属性返回一组 Calendar 对象,该数组指定特定区域性支持的所有日历,包括该区域性的默认日历。
下面的示例说明了 CultureInfo.Calendar 和 CultureInfo.OptionalCalendars 属性。 它为泰国(泰国)文化和日本(日本)文化创建CultureInfo
对象,并显示它们的默认日历和可选日历。 请注意,在这两种情况下,区域性的默认日历也包括在 CultureInfo.OptionalCalendars 集合中。
using System;
using System.Globalization;
public class Example
{
public static void Main()
{
// Create a CultureInfo for Thai in Thailand.
CultureInfo th = CultureInfo.CreateSpecificCulture("th-TH");
DisplayCalendars(th);
// Create a CultureInfo for Japanese in Japan.
CultureInfo ja = CultureInfo.CreateSpecificCulture("ja-JP");
DisplayCalendars(ja);
}
static void DisplayCalendars(CultureInfo ci)
{
Console.WriteLine($"Calendars for the {ci.Name} culture:");
// Get the culture's default calendar.
Calendar defaultCalendar = ci.Calendar;
Console.Write(" Default Calendar: {0}", GetCalendarName(defaultCalendar));
if (defaultCalendar is GregorianCalendar)
Console.WriteLine($" ({((GregorianCalendar) defaultCalendar).CalendarType})");
else
Console.WriteLine();
// Get the culture's optional calendars.
Console.WriteLine(" Optional Calendars:");
foreach (var optionalCalendar in ci.OptionalCalendars) {
Console.Write("{0,6}{1}", "", GetCalendarName(optionalCalendar));
if (optionalCalendar is GregorianCalendar)
Console.Write(" ({0})",
((GregorianCalendar) optionalCalendar).CalendarType);
Console.WriteLine();
}
Console.WriteLine();
}
static string GetCalendarName(Calendar cal)
{
return cal.ToString().Replace("System.Globalization.", "");
}
}
// The example displays the following output:
// Calendars for the th-TH culture:
// Default Calendar: ThaiBuddhistCalendar
// Optional Calendars:
// ThaiBuddhistCalendar
// GregorianCalendar (Localized)
//
// Calendars for the ja-JP culture:
// Default Calendar: GregorianCalendar (Localized)
// Optional Calendars:
// GregorianCalendar (Localized)
// JapaneseCalendar
// GregorianCalendar (USEnglish)
Imports System.Globalization
Public Module Example
Public Sub Main()
' Create a CultureInfo for Thai in Thailand.
Dim th As CultureInfo = CultureInfo.CreateSpecificCulture("th-TH")
DisplayCalendars(th)
' Create a CultureInfo for Japanese in Japan.
Dim ja As CultureInfo = CultureInfo.CreateSpecificCulture("ja-JP")
DisplayCalendars(ja)
End Sub
Sub DisplayCalendars(ci As CultureInfo)
Console.WriteLine("Calendars for the {0} culture:", ci.Name)
' Get the culture's default calendar.
Dim defaultCalendar As Calendar = ci.Calendar
Console.Write(" Default Calendar: {0}", GetCalendarName(defaultCalendar))
If TypeOf defaultCalendar Is GregorianCalendar Then
Console.WriteLine(" ({0})",
CType(defaultCalendar, GregorianCalendar).CalendarType)
Else
Console.WriteLine()
End If
' Get the culture's optional calendars.
Console.WriteLine(" Optional Calendars:")
For Each optionalCalendar In ci.OptionalCalendars
Console.Write("{0,6}{1}", "", GetCalendarName(optionalCalendar))
If TypeOf optionalCalendar Is GregorianCalendar Then
Console.Write(" ({0})",
CType(optionalCalendar, GregorianCalendar).CalendarType)
End If
Console.WriteLine()
Next
Console.WriteLine()
End Sub
Function GetCalendarName(cal As Calendar) As String
Return cal.ToString().Replace("System.Globalization.", "")
End Function
End Module
' The example displays the following output:
' Calendars for the th-TH culture:
' Default Calendar: ThaiBuddhistCalendar
' Optional Calendars:
' ThaiBuddhistCalendar
' GregorianCalendar (Localized)
'
' Calendars for the ja-JP culture:
' Default Calendar: GregorianCalendar (Localized)
' Optional Calendars:
' GregorianCalendar (Localized)
' JapaneseCalendar
' GregorianCalendar (USEnglish)
当前特定 CultureInfo 对象使用的日历是由文化的 DateTimeFormatInfo.Calendar 属性定义的。 区域性的 DateTimeFormatInfo 对象由 CultureInfo.DateTimeFormat 属性返回。 创建文化时,其默认值与属性 CultureInfo.Calendar 的值相同。 但是,您可以将该文化的当前日历更改为由属性 CultureInfo.OptionalCalendars 返回的数组中包含的任何日历。 如果尝试将当前日历设置为属性值中不包含的 CultureInfo.OptionalCalendars 日历,则会抛出一个 ArgumentException。
以下示例更改阿拉伯文(沙特阿拉伯)文化使用的日历。 它首先实例化一个 DateTime 值,并使用当前区域性来展示该值——在本例中,当前区域性是英语(美国),当前区域性的日历是公历。 接下来,它将当前文化设置更改为阿拉伯语(沙特阿拉伯),并使用其默认的 Um Al-Qura 日历格式显示日期。 然后,它调用 CalendarExists
该方法来确定 Hijri 日历是否受阿拉伯语(沙特阿拉伯)文化的支持。 因为该日历受支持,所以该示例将当前日历更改为回历并再次显示日期。 请注意,在每个情况下,日期都使用当前文化的日历显示。
using System;
using System.Globalization;
using System.Threading;
public class Example
{
public static void Main()
{
DateTime date1 = new DateTime(2011, 6, 20);
DisplayCurrentInfo();
// Display the date using the current culture and calendar.
Console.WriteLine(date1.ToString("d"));
Console.WriteLine();
CultureInfo arSA = CultureInfo.CreateSpecificCulture("ar-SA");
// Change the current culture to Arabic (Saudi Arabia).
Thread.CurrentThread.CurrentCulture = arSA;
// Display date and information about the current culture.
DisplayCurrentInfo();
Console.WriteLine(date1.ToString("d"));
Console.WriteLine();
// Change the calendar to Hijri.
Calendar hijri = new HijriCalendar();
if (CalendarExists(arSA, hijri)) {
arSA.DateTimeFormat.Calendar = hijri;
// Display date and information about the current culture.
DisplayCurrentInfo();
Console.WriteLine(date1.ToString("d"));
}
}
private static void DisplayCurrentInfo()
{
Console.WriteLine($"Current Culture: {CultureInfo.CurrentCulture.Name}");
Console.WriteLine($"Current Calendar: {DateTimeFormatInfo.CurrentInfo.Calendar}");
}
private static bool CalendarExists(CultureInfo culture, Calendar cal)
{
foreach (Calendar optionalCalendar in culture.OptionalCalendars)
if (cal.ToString().Equals(optionalCalendar.ToString()))
return true;
return false;
}
}
// The example displays the following output:
// Current Culture: en-US
// Current Calendar: System.Globalization.GregorianCalendar
// 6/20/2011
//
// Current Culture: ar-SA
// Current Calendar: System.Globalization.UmAlQuraCalendar
// 18/07/32
//
// Current Culture: ar-SA
// Current Calendar: System.Globalization.HijriCalendar
// 19/07/32
Imports System.Globalization
Imports System.Threading
Module Example
Public Sub Main()
Dim date1 As Date = #6/20/2011#
DisplayCurrentInfo()
' Display the date using the current culture and calendar.
Console.WriteLine(date1.ToString("d"))
Console.WriteLine()
Dim arSA As CultureInfo = CultureInfo.CreateSpecificCulture("ar-SA")
' Change the current culture to Arabic (Saudi Arabia).
Thread.CurrentThread.CurrentCulture = arSA
' Display date and information about the current culture.
DisplayCurrentInfo()
Console.WriteLine(date1.ToString("d"))
Console.WriteLine()
' Change the calendar to Hijri.
Dim hijri As Calendar = New HijriCalendar()
If CalendarExists(arSA, hijri) Then
arSA.DateTimeFormat.Calendar = hijri
' Display date and information about the current culture.
DisplayCurrentInfo()
Console.WriteLine(date1.ToString("d"))
End If
End Sub
Private Sub DisplayCurrentInfo()
Console.WriteLine("Current Culture: {0}",
CultureInfo.CurrentCulture.Name)
Console.WriteLine("Current Calendar: {0}",
DateTimeFormatInfo.CurrentInfo.Calendar)
End Sub
Private Function CalendarExists(ByVal culture As CultureInfo,
cal As Calendar) As Boolean
For Each optionalCalendar As Calendar In culture.OptionalCalendars
If cal.ToString().Equals(optionalCalendar.ToString()) Then Return True
Next
Return False
End Function
End Module
' The example displays the following output:
' Current Culture: en-US
' Current Calendar: System.Globalization.GregorianCalendar
' 6/20/2011
'
' Current Culture: ar-SA
' Current Calendar: System.Globalization.UmAlQuraCalendar
' 18/07/32
'
' Current Culture: ar-SA
' Current Calendar: System.Globalization.HijriCalendar
' 19/07/32
日期和日历
除包括类型Calendar参数且允许日期元素(即月、日和年)反映指定日历中的值的构造函数外,DateTime和DateTimeOffset的值始终基于公历。 例如,这意味着该 DateTime.Year 属性返回公历中的年份,而 DateTime.Day 该属性返回公历中的月份日期。
重要
请务必记住,日期值与其字符串表示形式之间存在差异。 前者基于公历:后者基于特定区域性的当前日历。
以下示例说明了DateTime属性与其相应的Calendar方法之间的这种差异。 在此示例中,当前文化为阿拉伯语(埃及),当前日历为乌姆·库拉历。 值 DateTime 设置为 2011 年第七个月的第 15 天。 很显然,该值被解释为公历日期,因为这些相同的值是由 DateTime.ToString(String, IFormatProvider) 方法在使用不变区域性的约定时返回的。 使用当前区域性的约定设置格式后,该日期的字符串表示形式为 14/08/32,与古兰经历中的日期等效。 接下来,成员DateTime
和Calendar
将用于返回DateTime值的日期、月份和年份。 在每个情况下,成员返回 DateTime 的值反映公历中的值,而成员返回 UmAlQuraCalendar 的值反映 Uum al-Qura 日历中的值。
using System;
using System.Globalization;
using System.Threading;
public class Example
{
public static void Main()
{
// Make Arabic (Egypt) the current culture
// and Umm al-Qura calendar the current calendar.
CultureInfo arEG = CultureInfo.CreateSpecificCulture("ar-EG");
Calendar cal = new UmAlQuraCalendar();
arEG.DateTimeFormat.Calendar = cal;
Thread.CurrentThread.CurrentCulture = arEG;
// Display information on current culture and calendar.
DisplayCurrentInfo();
// Instantiate a date object.
DateTime date1 = new DateTime(2011, 7, 15);
// Display the string representation of the date.
Console.WriteLine($"Date: {date1:d}");
Console.WriteLine($"Date in the Invariant Culture: {date1.ToString("d", CultureInfo.InvariantCulture)}");
Console.WriteLine();
// Compare DateTime properties and Calendar methods.
Console.WriteLine($"DateTime.Month property: {date1.Month}");
Console.WriteLine($"UmAlQura.GetMonth: {cal.GetMonth(date1)}");
Console.WriteLine();
Console.WriteLine($"DateTime.Day property: {date1.Day}");
Console.WriteLine($"UmAlQura.GetDayOfMonth: {cal.GetDayOfMonth(date1)}");
Console.WriteLine();
Console.WriteLine($"DateTime.Year property: {date1.Year:D4}");
Console.WriteLine($"UmAlQura.GetYear: {cal.GetYear(date1)}");
Console.WriteLine();
}
private static void DisplayCurrentInfo()
{
Console.WriteLine($"Current Culture: {CultureInfo.CurrentCulture.Name}");
Console.WriteLine($"Current Calendar: {DateTimeFormatInfo.CurrentInfo.Calendar}");
}
}
// The example displays the following output:
// Current Culture: ar-EG
// Current Calendar: System.Globalization.UmAlQuraCalendar
// Date: 14/08/32
// Date in the Invariant Culture: 07/15/2011
//
// DateTime.Month property: 7
// UmAlQura.GetMonth: 8
//
// DateTime.Day property: 15
// UmAlQura.GetDayOfMonth: 14
//
// DateTime.Year property: 2011
// UmAlQura.GetYear: 1432
Imports System.Globalization
Imports System.Threading
Module Example
Public Sub Main()
' Make Arabic (Egypt) the current culture
' and Umm al-Qura calendar the current calendar.
Dim arEG As CultureInfo = CultureInfo.CreateSpecificCulture("ar-EG")
Dim cal As Calendar = New UmAlQuraCalendar()
arEG.DateTimeFormat.Calendar = cal
Thread.CurrentThread.CurrentCulture = arEG
' Display information on current culture and calendar.
DisplayCurrentInfo()
' Instantiate a date object.
Dim date1 As Date = #07/15/2011#
' Display the string representation of the date.
Console.WriteLine("Date: {0:d}", date1)
Console.WriteLine("Date in the Invariant Culture: {0}",
date1.ToString("d", CultureInfo.InvariantCulture))
Console.WriteLine()
' Compare DateTime properties and Calendar methods.
Console.WriteLine("DateTime.Month property: {0}", date1.Month)
Console.WriteLine("UmAlQura.GetMonth: {0}",
cal.GetMonth(date1))
Console.WriteLine()
Console.WriteLine("DateTime.Day property: {0}", date1.Day)
Console.WriteLine("UmAlQura.GetDayOfMonth: {0}",
cal.GetDayOfMonth(date1))
Console.WriteLine()
Console.WriteLine("DateTime.Year property: {0:D4}", date1.Year)
Console.WriteLine("UmAlQura.GetYear: {0}",
cal.GetYear(date1))
Console.WriteLine()
End Sub
Private Sub DisplayCurrentInfo()
Console.WriteLine("Current Culture: {0}",
CultureInfo.CurrentCulture.Name)
Console.WriteLine("Current Calendar: {0}",
DateTimeFormatInfo.CurrentInfo.Calendar)
End Sub
End Module
' The example displays the following output:
' Current Culture: ar-EG
' Current Calendar: System.Globalization.UmAlQuraCalendar
' Date: 14/08/32
' Date in the Invariant Culture: 07/15/2011
'
' DateTime.Month property: 7
' UmAlQura.GetMonth: 8
'
' DateTime.Day property: 15
' UmAlQura.GetDayOfMonth: 14
'
' DateTime.Year property: 2011
' UmAlQura.GetYear: 1432
根据日历实例化日期
由于 DateTime 值 DateTimeOffset 基于公历,因此必须调用一个重载构造函数,该构造函数包含一个类型 Calendar 参数,以实例化日期值(如果要使用不同日历中的日、月或年值)。 还可以调用特定日历 Calendar.ToDateTime 方法的重载之一,以根据特定日历的值实例化 DateTime 对象。
以下示例通过将DateTime对象传递给HebrewCalendar构造函数来实例化DateTime值,并通过调用DateTime方法实例化第二个HebrewCalendar.ToDateTime(Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32)值。 由于这两个值是使用希伯来语日历中的相同值创建的,因此 DateTime.Equals 该方法的调用显示两 DateTime 个值相等。
using System;
using System.Globalization;
public class Example
{
public static void Main()
{
HebrewCalendar hc = new HebrewCalendar();
DateTime date1 = new DateTime(5771, 6, 1, hc);
DateTime date2 = hc.ToDateTime(5771, 6, 1, 0, 0, 0, 0);
Console.WriteLine("{0:d} (Gregorian) = {1:d2}/{2:d2}/{3:d4} ({4}): {5}",
date1,
hc.GetMonth(date2),
hc.GetDayOfMonth(date2),
hc.GetYear(date2),
GetCalendarName(hc),
date1.Equals(date2));
}
private static string GetCalendarName(Calendar cal)
{
return cal.ToString().Replace("System.Globalization.", "").
Replace("Calendar", "");
}
}
// The example displays the following output:
// 2/5/2011 (Gregorian) = 06/01/5771 (Hebrew): True
Imports System.Globalization
Module Example
Public Sub Main()
Dim hc As New HebrewCalendar()
Dim date1 As New Date(5771, 6, 1, hc)
Dim date2 As Date = hc.ToDateTime(5771, 6, 1, 0, 0, 0, 0)
Console.WriteLine("{0:d} (Gregorian) = {1:d2}/{2:d2}/{3:d4} ({4}): {5}",
date1,
hc.GetMonth(date2),
hc.GetDayOfMonth(date2),
hc.GetYear(date2),
GetCalendarName(hc),
date1.Equals(date2))
End Sub
Private Function GetCalendarName(cal As Calendar) As String
Return cal.ToString().Replace("System.Globalization.", "").
Replace("Calendar", "")
End Function
End Module
' The example displays the following output:
' 2/5/2011 (Gregorian) = 06/01/5771 (Hebrew): True
表示当前日历中的日期
日期和时间格式设置方法在将日期转换为字符串时始终使用当前日历。 这意味着年、月和月的字符串表示形式反映当前日历,不一定反映公历。
以下示例演示当前日历如何影响日期的字符串表示形式。 它将当前文化更改为中文(传统、台湾),并实例化日期值。 然后,它显示当前日历和日期,将当前日历更改为 TaiwanCalendar,并再次显示当前日历和日期。 第一次显示日期时,它表示为公历中的日期。 第二次显示时,它表示为台湾日历中的日期。
using System;
using System.Globalization;
using System.Threading;
public class Example
{
public static void Main()
{
// Change the current culture to zh-TW.
CultureInfo zhTW = CultureInfo.CreateSpecificCulture("zh-TW");
Thread.CurrentThread.CurrentCulture = zhTW;
// Define a date.
DateTime date1 = new DateTime(2011, 1, 16);
// Display the date using the default (Gregorian) calendar.
Console.WriteLine($"Current calendar: {zhTW.DateTimeFormat.Calendar}");
Console.WriteLine(date1.ToString("d"));
// Change the current calendar and display the date.
zhTW.DateTimeFormat.Calendar = new TaiwanCalendar();
Console.WriteLine($"Current calendar: {zhTW.DateTimeFormat.Calendar}");
Console.WriteLine(date1.ToString("d"));
}
}
// The example displays the following output:
// Current calendar: System.Globalization.GregorianCalendar
// 2011/1/16
// Current calendar: System.Globalization.TaiwanCalendar
// 100/1/16
Imports System.Globalization
Imports System.Threading
Module Example
Public Sub Main()
' Change the current culture to zh-TW.
Dim zhTW As CultureInfo = CultureInfo.CreateSpecificCulture("zh-TW")
Thread.CurrentThread.CurrentCulture = zhTW
' Define a date.
Dim date1 As Date = #1/16/2011#
' Display the date using the default (Gregorian) calendar.
Console.WriteLine("Current calendar: {0}",
zhTW.DateTimeFormat.Calendar)
Console.WriteLine(date1.ToString("d"))
' Change the current calendar and display the date.
zhTW.DateTimeFormat.Calendar = New TaiwanCalendar()
Console.WriteLine("Current calendar: {0}",
zhTW.DateTimeFormat.Calendar)
Console.WriteLine(date1.ToString("d"))
End Sub
End Module
' The example displays the following output:
' Current calendar: System.Globalization.GregorianCalendar
' 2011/1/16
' Current calendar: System.Globalization.TaiwanCalendar
' 100/1/16
在非当前使用的日历中表示日期
如果要使用一种不是特定文化当前使用的日历来表示日期,必须调用该Calendar对象的方法。 例如,Calendar.GetYear和Calendar.GetMonthCalendar.GetDayOfMonth方法将年份、月和日转换为反映特定日历的值。
警告
由于有些日历不属于任何文化的可选日历,要表示这些日历中的日期,总是需要调用日历方法。 所有派生自EastAsianLunisolarCalendar、JulianCalendar和PersianCalendar类的日历都是如此。
以下示例使用对象 JulianCalendar 在 Julian 日历中实例化日期 1905 年 1 月 9 日。 使用此默认日历(公历)显示此日期时,它表示为 1905 年 1 月 22 日。 对各个 JulianCalendar 方法的调用使日期能够在 Julian 日历中表示。
using System;
using System.Globalization;
public class Example
{
public static void Main()
{
JulianCalendar julian = new JulianCalendar();
DateTime date1 = new DateTime(1905, 1, 9, julian);
Console.WriteLine("Date ({0}): {1:d}",
CultureInfo.CurrentCulture.Calendar,
date1);
Console.WriteLine("Date in Julian calendar: {0:d2}/{1:d2}/{2:d4}",
julian.GetMonth(date1),
julian.GetDayOfMonth(date1),
julian.GetYear(date1));
}
}
// The example displays the following output:
// Date (System.Globalization.GregorianCalendar): 1/22/1905
// Date in Julian calendar: 01/09/1905
Imports System.Globalization
Module Example
Public Sub Main()
Dim julian As New JulianCalendar()
Dim date1 As New Date(1905, 1, 9, julian)
Console.WriteLine("Date ({0}): {1:d}",
CultureInfo.CurrentCulture.Calendar,
date1)
Console.WriteLine("Date in Julian calendar: {0:d2}/{1:d2}/{2:d4}",
julian.GetMonth(date1),
julian.GetDayOfMonth(date1),
julian.GetYear(date1))
End Sub
End Module
' The example displays the following output:
' Date (System.Globalization.GregorianCalendar): 1/22/1905
' Date in Julian calendar: 01/09/1905
日历和日期范围
日历支持的最早日期由该日历 Calendar.MinSupportedDateTime 的属性指示。 对于该 GregorianCalendar 类,该日期为 0001 年 1 月 1 日 C.E。.NET 中的大多数其他日历都支持较晚的日期。 尝试处理早于日历最早支持日期的日期和时间值时,会引发ArgumentOutOfRangeException异常。
但是,存在一个重要的异常。 DateTime 对象和 DateTimeOffset 对象的默认(未初始化)值等于 GregorianCalendar.MinSupportedDateTime 值。 如果您尝试在不支持 0001 年 1 月 1 日 C.E. 的日历中设置此日期的格式,并且未提供格式说明符,则格式设置方法使用“s”(可排序的日期/时间模式)格式说明符,而不是“G”(常规日期/时间模式)格式说明符。 因此,格式设置操作不会引发 ArgumentOutOfRangeException 异常。 相反,它会返回不受支持的日期。 下面的示例阐释了这一点,在使用日语日历将当前区域性设置为日语(日本)以及使用古兰经历将当前区域性设置为阿拉伯语(埃及)时,它将显示 DateTime.MinValue 的值。 它还会将当前区域性设置为英语(美国),并调用 DateTime.ToString(IFormatProvider) 方法以及所有这些 CultureInfo 对象。 在每个情况下,使用可排序的日期/时间模式显示日期。
using System;
using System.Globalization;
using System.Threading;
public class Example
{
public static void Main()
{
DateTime dat = DateTime.MinValue;
// Change the current culture to ja-JP with the Japanese Calendar.
CultureInfo jaJP = CultureInfo.CreateSpecificCulture("ja-JP");
jaJP.DateTimeFormat.Calendar = new JapaneseCalendar();
Thread.CurrentThread.CurrentCulture = jaJP;
Console.WriteLine($"Earliest supported date by {GetCalendarName(jaJP)} calendar: {jaJP.DateTimeFormat.Calendar.MinSupportedDateTime:d}");
// Attempt to display the date.
Console.WriteLine(dat.ToString());
Console.WriteLine();
// Change the current culture to ar-EG with the Um Al Qura calendar.
CultureInfo arEG = CultureInfo.CreateSpecificCulture("ar-EG");
arEG.DateTimeFormat.Calendar = new UmAlQuraCalendar();
Thread.CurrentThread.CurrentCulture = arEG;
Console.WriteLine($"Earliest supported date by {GetCalendarName(arEG)} calendar: {arEG.DateTimeFormat.Calendar.MinSupportedDateTime:d}");
// Attempt to display the date.
Console.WriteLine(dat.ToString());
Console.WriteLine();
// Change the current culture to en-US.
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US");
Console.WriteLine(dat.ToString(jaJP));
Console.WriteLine(dat.ToString(arEG));
Console.WriteLine(dat.ToString("d"));
}
private static string GetCalendarName(CultureInfo culture)
{
Calendar cal = culture.DateTimeFormat.Calendar;
return cal.GetType().Name.Replace("System.Globalization.", "").Replace("Calendar", "");
}
}
// The example displays the following output:
// Earliest supported date by Japanese calendar: 明治 1/9/8
// 0001-01-01T00:00:00
//
// Earliest supported date by UmAlQura calendar: 01/01/18
// 0001-01-01T00:00:00
//
// 0001-01-01T00:00:00
// 0001-01-01T00:00:00
// 1/1/0001
Imports System.Globalization
Imports System.Threading
Module Example
Public Sub Main()
Dim dat As Date = DateTime.MinValue
' Change the current culture to ja-JP with the Japanese Calendar.
Dim jaJP As CultureInfo = CultureInfo.CreateSpecificCulture("ja-JP")
jaJP.DateTimeFormat.Calendar = New JapaneseCalendar()
Thread.CurrentThread.CurrentCulture = jaJP
Console.WriteLine("Earliest supported date by {1} calendar: {0:d}",
jaJP.DateTimeFormat.Calendar.MinSupportedDateTime,
GetCalendarName(jaJP))
' Attempt to display the date.
Console.WriteLine(dat.ToString())
Console.WriteLine()
' Change the current culture to ar-EG with the Um Al Qura calendar.
Dim arEG As CultureInfo = CultureInfo.CreateSpecificCulture("ar-EG")
arEG.DateTimeFormat.Calendar = New UmAlQuraCalendar()
Thread.CurrentThread.CurrentCulture = arEG
Console.WriteLine("Earliest supported date by {1} calendar: {0:d}",
arEG.DateTimeFormat.Calendar.MinSupportedDateTime,
GetCalendarName(arEG))
' Attempt to display the date.
Console.WRiteLine(dat.ToString())
Console.WRiteLine()
' Change the current culture to en-US.
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US")
Console.WriteLine(dat.ToString(jaJP))
Console.WriteLine(dat.ToString(arEG))
Console.WriteLine(dat.ToString("d"))
End Sub
Private Function GetCalendarName(culture As CultureInfo) As String
Dim cal As Calendar = culture.DateTimeFormat.Calendar
Return cal.GetType().Name.Replace("System.Globalization.", "").Replace("Calendar", "")
End Function
End Module
' The example displays the following output:
' Earliest supported date by Japanese calendar: 明治 1/9/8
' 0001-01-01T00:00:00
'
' Earliest supported date by UmAlQura calendar: 01/01/18
' 0001-01-01T00:00:00
'
' 0001-01-01T00:00:00
' 0001-01-01T00:00:00
' 1/1/0001
使用纪元
日历通常将日期划分为纪元。 但是, Calendar .NET 中的类不支持日历定义的每个纪元,大多数 Calendar 类仅支持单个纪元。 只有JapaneseCalendar类和JapaneseLunisolarCalendar类支持多个纪元。
重要
从 2019 年 5 月 1 日起,在 JapaneseCalendar 和 JapaneseLunisolarCalendar 中采用一种新纪元,即令和纪元。 此更改会影响使用这些日历的所有应用程序。 有关详细信息,请参阅以下文章:
- 在 .NET 中处理日语日历中的新纪元,其中记录了添加到 .NET 的功能以支持具有多个纪元的日历,并讨论了处理多纪元日历时要使用的最佳做法。
- 准备您的应用程序以应对日本纪元变更,本指南提供如何在 Windows 上测试应用程序的信息,以确保它们对纪元变更做好准备。
- .NET Framework 的新日本纪元更新摘要,其中列出了与新日本日历时代相关的单个 Windows 版本的 .NET Framework 更新,记录了适用于多纪元支持的新 .NET Framework 功能,并包括用于测试应用程序的内容。
大多数日历中的一个纪元表示很长的时间段。 例如,在公历中,当前时代跨越了两千多年。 支持多个纪元的JapaneseCalendar和JapaneseLunisolarCalendar这两个日历,情况并非如此。 一个时代对应于皇帝统治时期。 对多个时代的支持,特别是当当前时代的上限未知时,会带来特殊的挑战。
纪元和纪元名称
在 .NET 中,表示特定日历实现支持的纪元的整数以反向顺序存储在数组中 Calendar.Eras 。 当前时代(即具有最新时间范围的纪元)为索引零,对于 Calendar 支持多个纪元的类,每个连续索引都反映上一个时代。 静态 Calendar.CurrentEra 属性定义数组中 Calendar.Eras 当前纪元的索引;它是一个始终为零的常量。 各个 Calendar 类还包括返回当前纪元值的静态字段。 下表中列出了它们。
通过将纪元号传递给 DateTimeFormatInfo.GetEraName 或 DateTimeFormatInfo.GetAbbreviatedEraName 方法,可以检索与特定纪元号对应的名称。 下面的示例调用这些方法来检索有关 GregorianCalendar 类中的纪元支持信息。 它显示对应于当前纪元第二年 1 月 1 日的公历日期,以及对应于每个受支持日本日历纪元的第二年 1 月 1 日的公历日期。
using System;
using System.Globalization;
public class Example
{
public static void Main()
{
int year = 2;
int month = 1;
int day = 1;
Calendar cal = new JapaneseCalendar();
Console.WriteLine("\nDate instantiated without an era:");
DateTime date1 = new DateTime(year, month, day, 0, 0, 0, 0, cal);
Console.WriteLine("{0}/{1}/{2} in Japanese Calendar -> {3:d} in Gregorian",
cal.GetMonth(date1), cal.GetDayOfMonth(date1),
cal.GetYear(date1), date1);
Console.WriteLine("\nDates instantiated with eras:");
foreach (int era in cal.Eras) {
DateTime date2 = cal.ToDateTime(year, month, day, 0, 0, 0, 0, era);
Console.WriteLine("{0}/{1}/{2} era {3} in Japanese Calendar -> {4:d} in Gregorian",
cal.GetMonth(date2), cal.GetDayOfMonth(date2),
cal.GetYear(date2), cal.GetEra(date2), date2);
}
}
}
Imports System.Globalization
Module Example
Public Sub Main()
Dim year As Integer = 2
Dim month As Integer = 1
Dim day As Integer = 1
Dim cal As New JapaneseCalendar()
Console.WriteLine("Date instantiated without an era:")
Dim date1 As New Date(year, month, day, 0, 0, 0, 0, cal)
Console.WriteLine("{0}/{1}/{2} in Japanese Calendar -> {3:d} in Gregorian",
cal.GetMonth(date1), cal.GetDayOfMonth(date1),
cal.GetYear(date1), date1)
Console.WriteLine()
Console.WriteLine("Dates instantiated with eras:")
For Each era As Integer In cal.Eras
Dim date2 As Date = cal.ToDateTime(year, month, day, 0, 0, 0, 0, era)
Console.WriteLine("{0}/{1}/{2} era {3} in Japanese Calendar -> {4:d} in Gregorian",
cal.GetMonth(date2), cal.GetDayOfMonth(date2),
cal.GetYear(date2), cal.GetEra(date2), date2)
Next
End Sub
End Module
此外,“g”自定义日期和时间格式字符串在日期和时间的字符串表示形式中包含日历的纪元名称。 有关详细信息,请参阅自定义日期和时间格式字符串。
用纪元实例化日期
对于支持多个纪元的两个 Calendar 类,由特定年、月和当月日期值构成的日期可能不明确。 例如,JapaneseCalendar 支持的所有纪元中都包含数字为 1 的年份。 通常,如果未指定纪元,则日期和时间和日历方法都假定值属于当前纪元。 包含 DateTime 类型参数的 DateTimeOffset 和 Calendar 构造函数,以及 JapaneseCalendar.ToDateTime 和 JapaneseLunisolarCalendar.ToDateTime 方法也是如此。 以下示例实例化一个日期,该日期表示某个未指定的纪元的第二年一月一日。 如果在 Reiwa 纪元是当前时代时执行该示例,则日期将解释为 Reiwa 时代的第二年。 该纪元(令和)在方法返回 DateTime.ToString(String, IFormatProvider) 的字符串中的年份之前,对应于公历中的 2020 年 1 月 1 日。 (雷瓦时代始于公历2019年。
using System;
using System.Globalization;
public class Example
{
public static void Main()
{
var japaneseCal = new JapaneseCalendar();
var jaJp = new CultureInfo("ja-JP");
jaJp.DateTimeFormat.Calendar = japaneseCal;
var date = new DateTime(2, 1, 1, japaneseCal);
Console.WriteLine($"Gregorian calendar date: {date:d}");
Console.WriteLine($"Japanese calendar date: {date.ToString("d", jaJp)}");
}
}
Imports System.Globalization
Public Module Example
Public Sub Main()
Dim japaneseCal = New JapaneseCalendar()
Dim jaJp = New CultureInfo("ja-JP")
jaJp.DateTimeFormat.Calendar = japaneseCal
Dim dat = New DateTime(2, 1, 1, japaneseCal)
Console.WriteLine($"Gregorian calendar dat: {dat:d}")
Console.WriteLine($"Japanese calendar dat: {dat.ToString("d", jaJp)}")
End Sub
End Module
但是,如果时代发生更改,则此代码的意图变得不明确。 日期是代表当前时代的第二年,还是打算代表海西时代的第二年? 有两种方法可以避免这种歧义:
使用默认 GregorianCalendar 类实例化日期和时间值。 然后,可以将日语日历或日语 Lunisolar 日历用于日期的字符串表示形式,如以下示例所示。
using System; using System.Globalization; public class Example { public static void Main() { var japaneseCal = new JapaneseCalendar(); var jaJp = new CultureInfo("ja-JP"); jaJp.DateTimeFormat.Calendar = japaneseCal; var date = new DateTime(1905, 2, 12); Console.WriteLine($"Gregorian calendar date: {date:d}"); // Call the ToString(IFormatProvider) method. Console.WriteLine($"Japanese calendar date: {date.ToString("d", jaJp)}"); // Use a FormattableString object. FormattableString fmt = $"{date:d}"; Console.WriteLine($"Japanese calendar date: {fmt.ToString(jaJp)}"); // Use the JapaneseCalendar object. Console.WriteLine($"Japanese calendar date: {jaJp.DateTimeFormat.GetEraName(japaneseCal.GetEra(date))}" + $"{japaneseCal.GetYear(date)}/{japaneseCal.GetMonth(date)}/{japaneseCal.GetDayOfMonth(date)}"); // Use the current culture. CultureInfo.CurrentCulture = jaJp; Console.WriteLine($"Japanese calendar date: {date:d}"); } } // The example displays the following output: // Gregorian calendar date: 2/12/1905 // Japanese calendar date: 明治38/2/12 // Japanese calendar date: 明治38/2/12 // Japanese calendar date: 明治38/2/12 // Japanese calendar date: 明治38/2/12
Imports System.Globalization Public Module Example Public Sub Main() Dim japaneseCal = New JapaneseCalendar() Dim jaJp = New CultureInfo("ja-JP") jaJp.DateTimeFormat.Calendar = japaneseCal Dim dat = New DateTime(1905, 2, 12) Console.WriteLine($"Gregorian calendar date: {dat:d}") ' Call the ToString(IFormatProvider) method. Console.WriteLine($"Japanese calendar date: {dat.ToString("d", jaJp)}") ' Use a FormattableString object. Dim fmt As FormattableString = $"{dat:d}" Console.WriteLine($"Japanese calendar date: {fmt.ToString(jaJp)}") ' Use the JapaneseCalendar object. Console.WriteLine($"Japanese calendar date: {jaJp.DateTimeFormat.GetEraName(japaneseCal.GetEra(dat))}" + $"{japaneseCal.GetYear(dat)}/{japaneseCal.GetMonth(dat)}/{japaneseCal.GetDayOfMonth(dat)}") ' Use the current culture. CultureInfo.CurrentCulture = jaJp Console.WriteLine($"Japanese calendar date: {dat:d}") End Sub End Module ' The example displays the following output: ' Gregorian calendar date: 2/12/1905 ' Japanese calendar date: 明治38/2/12 ' Japanese calendar date: 明治38/2/12 ' Japanese calendar date: 明治38/2/12 ' Japanese calendar date: 明治38/2/12
调用一个明确指定某个纪元的日期和时间方法。 这包括以下方法:
ToDateTime(Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32) 方法属于 JapaneseCalendar 类或 JapaneseLunisolarCalendar 类。
DateTime 或 DateTimeOffset 解析方法,例如 Parse,TryParse,ParseExact 或 TryParseExact,包括要解析的字符串,如果当前文化为 Japanese-Japan(“ja-JP”),并且该文化的日历为 DateTimeStyles,可以选择性地包含 JapaneseCalendar 参数。 要分析的字符串必须包含纪元。
DateTime或DateTimeOffset解析方法,包含
provider
类型的IFormatProvider参数。provider
必须是一个 CultureInfo 对象,该对象代表当前日历为 JapaneseCalendar 的 Japanese-Japan(“ja-JP”)文化,或者是一个 DateTimeFormatInfo 对象,其 Calendar 属性为 JapaneseCalendar。 要分析的字符串必须包含纪元。
下面的示例使用其中三种方法来实例化明治时代的日期和时间,该时代始于1868年9月8日,止于1912年7月29日。
using System; using System.Globalization; public class Example { public static void Main() { var japaneseCal = new JapaneseCalendar(); var jaJp = new CultureInfo("ja-JP"); jaJp.DateTimeFormat.Calendar = japaneseCal; // We can get the era index by calling DateTimeFormatInfo.GetEraName. int eraIndex = 0; for (int ctr = 0; ctr < jaJp.DateTimeFormat.Calendar.Eras.Length; ctr++) if (jaJp.DateTimeFormat.GetEraName(ctr) == "明治") eraIndex = ctr; var date1 = japaneseCal.ToDateTime(23, 9, 8, 0, 0, 0, 0, eraIndex); Console.WriteLine($"{date1.ToString("d", jaJp)} (Gregorian {date1:d})"); try { var date2 = DateTime.Parse("明治23/9/8", jaJp); Console.WriteLine($"{date2.ToString("d", jaJp)} (Gregorian {date2:d})"); } catch (FormatException) { Console.WriteLine("The parsing operation failed."); } try { var date3 = DateTime.ParseExact("明治23/9/8", "gyy/M/d", jaJp); Console.WriteLine($"{date3.ToString("d", jaJp)} (Gregorian {date3:d})"); } catch (FormatException) { Console.WriteLine("The parsing operation failed."); } } } // The example displays the following output: // 明治23/9/8 (Gregorian 9/8/1890) // 明治23/9/8 (Gregorian 9/8/1890) // 明治23/9/8 (Gregorian 9/8/1890)
Imports System.Globalization Public Module Example Public Sub Main() Dim japaneseCal = New JapaneseCalendar() Dim jaJp = New CultureInfo("ja-JP") jaJp.DateTimeFormat.Calendar = japaneseCal ' We can get the era index by calling DateTimeFormatInfo.GetEraName. Dim eraIndex As Integer = 0 For ctr As Integer = 0 To jaJp.DateTimeFormat.Calendar.Eras.Length - 1 If jaJp.DateTimeFormat.GetEraName(ctr) = "明治" Then eraIndex = ctr Next Dim date1 = japaneseCal.ToDateTime(23, 9, 8, 0, 0, 0, 0, eraIndex) Console.WriteLine($"{date1.ToString("d", jaJp)} (Gregorian {date1:d})") Try Dim date2 = DateTime.Parse("明治23/9/8", jaJp) Console.WriteLine($"{date2.ToString("d", jaJp)} (Gregorian {date2:d})") Catch e As FormatException Console.WriteLine("The parsing operation failed.") End Try Try Dim date3 = DateTime.ParseExact("明治23/9/8", "gyy/M/d", jaJp) Console.WriteLine($"{date3.ToString("d", jaJp)} (Gregorian {date3:d})") Catch e As FormatException Console.WriteLine("The parsing operation failed.") End Try End Sub End Module ' The example displays the following output: ' 明治23/9/8 (Gregorian 9/8/1890) ' 明治23/9/8 (Gregorian 9/8/1890) ' 明治23/9/8 (Gregorian 9/8/1890)
小窍门
使用支持多个纪元的日历时, 请始终 使用公历日期实例化日期,或者在基于该日历实例化日期和时间时指定纪元。
在指定 ToDateTime(Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32) 方法的纪元时,可以在日历的属性中提供纪元的 Eras 索引。 但是,对于其纪元可能会更改的日历,这些索引不是常量值;当前时代为索引 0,最旧的纪元为索引 Eras.Length - 1
。 将新纪元添加到日历时,前一个纪元的索引将增加一个。 可以按如下所示提供相应的纪元索引:
对于当前纪元中的日期,请始终使用日历 CurrentEra 的属性。
对于指定纪元中的日期,使用 DateTimeFormatInfo.GetEraName 该方法检索与指定纪元名称对应的索引。 这要求 JapaneseCalendar 是与表示 ja-JP 文化的 CultureInfo 对象相关联的当前日历。 (此技术也适用于 JapaneseLunisolarCalendar,因为它支持与 JapaneseCalendar 相同的时代。)前面的示例演示了此方法。
日历、纪元和日期范围:放宽的范围检查
与单个日历具有支持的日期范围一样,JapaneseCalendar 和 JapaneseLunisolarCalendar 类中的纪元也具有支持的范围。 以前,.NET 使用严格的纪元范围检查来确保特定于纪元的日期在该纪元范围内。 也就是说,如果日期超出指定纪元的范围,该方法将引发一个 ArgumentOutOfRangeException。 目前,默认情况下,.NET 使用宽松的范围检查。 对所有 .NET 版本的更新引入了宽松的纪元范围检查;当尝试实例化一个超出指定纪元范围的日期时,该日期“溢出”到下一个纪元,且不会引发异常。
以下示例尝试实例化 Showa 时代第 65 年的日期,该日期始于 1926 年 12 月 25 日,于 1989 年 1 月 7 日结束。 该日期对应于 1990 年 1 月 9 日,超出了 JapaneseCalendar 中昭和纪元的范围。 如示例所示,示例显示的日期是1990年1月9日,在海西时代的第二年。
using System;
using System.Globalization;
public class Example
{
public static void Main()
{
var jaJp = new CultureInfo("ja-JP");
var cal = new JapaneseCalendar();
jaJp.DateTimeFormat.Calendar = cal;
string showaEra = "昭和";
var dt = cal.ToDateTime(65, 1, 9, 15, 0, 0, 0, GetEraIndex(showaEra));
FormattableString fmt = $"{dt:d}";
Console.WriteLine($"Japanese calendar date: {fmt.ToString(jaJp)}");
Console.WriteLine($"Gregorian calendar date: {fmt}");
int GetEraIndex(string eraName)
{
foreach (var ctr in cal.Eras)
if (jaJp.DateTimeFormat.GetEraName(ctr) == eraName)
return ctr;
return 0;
}
}
}
// The example displays the following output:
// Japanese calendar date: 平成2/1/9
// Gregorian calendar date: 1/9/1990
Imports System.Globalization
Public Module Example
Dim jaJp As CultureInfo
Dim cal As Calendar
Public Sub Main()
jaJp = New CultureInfo("ja-JP")
cal = New JapaneseCalendar()
jaJp.DateTimeFormat.Calendar = cal
Dim showaEra = "昭和"
Dim dt = cal.ToDateTime(65, 1, 9, 15, 0, 0, 0, GetEraIndex(showaEra))
Dim fmt As FormattableString = $"{dt:d}"
Console.WriteLine($"Japanese calendar date: {fmt.ToString(jaJp)}")
Console.WriteLine($"Gregorian calendar date: {fmt}")
End Sub
Private Function GetEraIndex(eraName As String) As Integer
For Each ctr As Integer In cal.Eras
If jaJp.DateTimeFormat.GetEraName(ctr) = eraName Then Return ctr
Next
Return 0
End Function
End Module
' The example displays the following output:
' Japanese calendar date: 平成2/1/9
' Gregorian calendar date: 1/9/1990
如果放宽的范围检查是不可取的,则可以通过多种方式还原严格的范围检查,具体取决于运行应用程序的 .NET 版本:
.NET Core: 将以下内容添加到 .netcore.runtime.json 配置文件:
"runtimeOptions": { "configProperties": { "Switch.System.Globalization.EnforceJapaneseEraYearRanges": true } }
.NET Framework 4.6 或更高版本: 在 app.config 文件中设置以下 AppContext 开关:
<?xml version="1.0" encoding="utf-8"?> <configuration> <runtime> <AppContextSwitchOverrides value="Switch.System.Globalization.EnforceJapaneseEraYearRanges=true" /> </runtime> </configuration>
.NET Framework 4.5.2 或更早版本: 设置以下注册表值:
价值 密钥 HKEY_LOCAL_MACHINE\Software\Microsoft\.NETFramework\AppContext 条目 Switch.System.Globalization.EnforceJapaneseEraYearRanges 类型 REG_SZ 价值 是
启用严格范围检查后,前面的示例将 ArgumentOutOfRangeException 引发并显示以下输出:
Unhandled Exception: System.ArgumentOutOfRangeException: Valid values are between 1 and 64, inclusive.
Parameter name: year
at System.Globalization.GregorianCalendarHelper.GetYearOffset(Int32 year, Int32 era, Boolean throwOnError)
at System.Globalization.GregorianCalendarHelper.ToDateTime(Int32 year, Int32 month, Int32 day, Int32 hour, Int32 minute, Int32 second, Int32 millisecond, Int32 era)
at Example.Main()
使用多纪元日历表示日期
如果对象 Calendar 支持纪元并且是对象的当前日历 CultureInfo ,则纪元包含在完整日期和时间、长日期和短日期模式的日期和时间值的字符串表示形式中。 以下示例展示了当当前文化为日本(日语)且当前日历为日本日历时的日期格式模式。
using System;
using System.Globalization;
using System.IO;
using System.Threading;
public class Example
{
public static void Main()
{
StreamWriter sw = new StreamWriter(@".\eras.txt");
DateTime dt = new DateTime(2012, 5, 1);
CultureInfo culture = CultureInfo.CreateSpecificCulture("ja-JP");
DateTimeFormatInfo dtfi = culture.DateTimeFormat;
dtfi.Calendar = new JapaneseCalendar();
Thread.CurrentThread.CurrentCulture = culture;
sw.WriteLine("\n{0,-43} {1}", "Full Date and Time Pattern:", dtfi.FullDateTimePattern);
sw.WriteLine(dt.ToString("F"));
sw.WriteLine();
sw.WriteLine("\n{0,-43} {1}", "Long Date Pattern:", dtfi.LongDatePattern);
sw.WriteLine(dt.ToString("D"));
sw.WriteLine("\n{0,-43} {1}", "Short Date Pattern:", dtfi.ShortDatePattern);
sw.WriteLine(dt.ToString("d"));
sw.Close();
}
}
// The example writes the following output to a file:
// Full Date and Time Pattern: gg y'年'M'月'd'日' H:mm:ss
// 平成 24年5月1日 0:00:00
//
// Long Date Pattern: gg y'年'M'月'd'日'
// 平成 24年5月1日
//
// Short Date Pattern: gg y/M/d
// 平成 24/5/1
Imports System.Globalization
Imports System.IO
Imports System.Threading
Module Example
Public Sub Main()
Dim sw As New StreamWriter(".\eras.txt")
Dim dt As Date = #05/01/2012#
Dim culture As CultureInfo = CultureInfo.CreateSpecificCulture("ja-JP")
Dim dtfi As DateTimeFormatInfo = culture.DateTimeFormat
dtfi.Calendar = New JapaneseCalendar()
Thread.CurrentThread.CurrentCulture = culture
sw.WriteLine("{0,-43} {1}", "Full Date and Time Pattern:", dtfi.FullDateTimePattern)
sw.WriteLine(dt.ToString("F"))
sw.WriteLine()
sw.WriteLine("{0,-43} {1}", "Long Date Pattern:", dtfi.LongDatePattern)
sw.WriteLine(dt.ToString("D"))
sw.WriteLine()
sw.WriteLine("{0,-43} {1}", "Short Date Pattern:", dtfi.ShortDatePattern)
sw.WriteLine(dt.ToString("d"))
sw.WriteLine()
sw.Close()
End Sub
End Module
' The example writes the following output to a file:
' Full Date and Time Pattern: gg y'年'M'月'd'日' H:mm:ss
' 平成 24年5月1日 0:00:00
'
' Long Date Pattern: gg y'年'M'月'd'日'
' 平成 24年5月1日
'
' Short Date Pattern: gg y/M/d
' 平成 24/5/1
警告
该JapaneseCalendar类是 .NET 中唯一一个既支持多个纪元中的日期,又能作为CultureInfo对象的当前日历的日历类,特别是表示日本(Japan)文化的CultureInfo对象。
对于所有日历,“g”自定义格式说明符包括结果字符串中的纪元。 以下示例使用“MM-dd-yyyy g”自定义格式字符串在当前日历为公历时在结果字符串中包含纪元。
DateTime dat = new DateTime(2012, 5, 1);
Console.WriteLine($"{dat:MM-dd-yyyy g}");
// The example displays the following output:
// 05-01-2012 A.D.
Dim dat As Date = #05/01/2012#
Console.WriteLine("{0:MM-dd-yyyy g}", dat)
' The example displays the following output:
' 05-01-2012 A.D.
在以下情况下,如果日期的字符串表示形式使用的不是当前日历,Calendar类包含一个Calendar.GetEra方法,该方法可以与Calendar.GetYear、Calendar.GetMonth和Calendar.GetDayOfMonth方法一起使用,以明确指示日期及其所对应的纪元。 下面的示例使用 JapaneseLunisolarCalendar 该类来提供插图。 但是,请注意,要在结果字符串中包括纪元的有意义名称或缩写,而不是整数,您需要实例化 DateTimeFormatInfo 对象,并将 JapaneseCalendar 设为其当前日历。 (日历 JapaneseLunisolarCalendar 不能是任何文化的当前日历,但在这种情况下,两个日历共享相同的纪元。)
using System;
using System.Globalization;
public class Example
{
public static void Main()
{
DateTime date1 = new DateTime(2011, 8, 28);
Calendar cal = new JapaneseLunisolarCalendar();
Console.WriteLine("{0} {1:d4}/{2:d2}/{3:d2}",
cal.GetEra(date1),
cal.GetYear(date1),
cal.GetMonth(date1),
cal.GetDayOfMonth(date1));
// Display eras
CultureInfo culture = CultureInfo.CreateSpecificCulture("ja-JP");
DateTimeFormatInfo dtfi = culture.DateTimeFormat;
dtfi.Calendar = new JapaneseCalendar();
Console.WriteLine("{0} {1:d4}/{2:d2}/{3:d2}",
dtfi.GetAbbreviatedEraName(cal.GetEra(date1)),
cal.GetYear(date1),
cal.GetMonth(date1),
cal.GetDayOfMonth(date1));
}
}
// The example displays the following output:
// 4 0023/07/29
// 平 0023/07/29
Imports System.Globalization
Module Example
Public Sub Main()
Dim date1 As Date = #8/28/2011#
Dim cal As New JapaneseLunisolarCalendar()
Console.WriteLine("{0} {1:d4}/{2:d2}/{3:d2}",
cal.GetEra(date1),
cal.GetYear(date1),
cal.GetMonth(date1),
cal.GetDayOfMonth(date1))
' Display eras
Dim culture As CultureInfo = CultureInfo.CreateSpecificCulture("ja-JP")
Dim dtfi As DateTimeFormatInfo = culture.DateTimeFormat
dtfi.Calendar = New JapaneseCalendar()
Console.WriteLine("{0} {1:d4}/{2:d2}/{3:d2}",
dtfi.GetAbbreviatedEraName(cal.GetEra(date1)),
cal.GetYear(date1),
cal.GetMonth(date1),
cal.GetDayOfMonth(date1))
End Sub
End Module
' The example displays the following output:
' 4 0023/07/29
' 平 0023/07/29
在日本日历中,一个时代的第一年被称为甘宁(元年)。 例如,平成时代的第一年可以描述为平成元年,而不是Heisei 1。 在将以下标准或自定义日期和时间格式字符串与表示日语-日本(“ja-JP”)区域性且具有 CultureInfo 类的 JapaneseCalendar 对象结合使用,对日期和时间进行格式化操作时,.NET 会采用此约定:
- 长日期模式,由“D”标准日期和时间格式字符串指示。
- 完整日期长时间模式,由“F”标准的日期和时间格式字符串所表示。
- 完整日期短时间模式,由“f”标准日期和时间格式字符串指示。
- 年/月模式,由“Y”或“y”标准日期和时间格式字符串指示。
- “ggy'年'”或“ggy年”自定义日期和时间格式字符串。
例如,以下示例在JapaneseCalendar中显示平成时代第一年的日期。
using System;
using System.Globalization;
public class Example
{
public static void Main()
{
var enUs = new CultureInfo("en-US");
var japaneseCal = new JapaneseCalendar();
var jaJp = new CultureInfo("ja-JP");
jaJp.DateTimeFormat.Calendar = japaneseCal;
string heiseiEra = "平成";
var date = japaneseCal.ToDateTime(1, 8, 18, 0, 0, 0, 0, GetEraIndex(heiseiEra));
FormattableString fmt = $"{date:D}";
Console.WriteLine($"Japanese calendar date: {fmt.ToString(jaJp)} (Gregorian: {fmt.ToString(enUs)})");
int GetEraIndex(string eraName)
{
foreach (var ctr in japaneseCal.Eras)
if (jaJp.DateTimeFormat.GetEraName(ctr) == eraName)
return ctr;
return 0;
}
}
}
// The example displays the following output:
// Japanese calendar date: 平成元年8月18日 (Gregorian: Friday, August 18, 1989)
Imports System.Globalization
Module Program
Dim jaJp As CultureInfo
Dim japaneseCal As Calendar
Sub Main()
Dim enUs = New CultureInfo("en-US")
japaneseCal = New JapaneseCalendar()
jaJp = New CultureInfo("ja-JP")
jaJp.DateTimeFormat.Calendar = japaneseCal
Dim heiseiEra = "平成"
Dim dat = japaneseCal.ToDateTime(1, 8, 18, 0, 0, 0, 0, GetEraIndex(heiseiEra))
Dim fmt As FormattableString = $"{dat:D}"
Console.WriteLine($"Japanese calendar date: {fmt.ToString(jaJp)} (Gregorian: {fmt.ToString(enUs)})")
End Sub
Private Function GetEraIndex(eraName As String) As Integer
For Each ctr In japaneseCal.Eras
If jaJp.DateTimeFormat.GetEraName(ctr) = eraName Then
Return ctr
End If
Next
Return 0
End Function
End Module
' The example displays the following output:
' Japanese calendar date: 平成元年8月18日 (Gregorian: Friday, August 18, 1989)
如果这种行为在格式化操作中是不可取的,可以通过执行以下步骤来还原以前的行为,该行为始终将纪元的第一年表示为“1”而不是“Gannen”,具体取决于 .NET 的版本:
.NET Core: 将以下内容添加到 .netcore.runtime.json 配置文件:
"runtimeOptions": { "configProperties": { "Switch.System.Globalization.FormatJapaneseFirstYearAsANumber": true } }
.NET Framework 4.6 或更高版本: 在 app.config 文件中设置以下 AppContext 开关:
<?xml version="1.0" encoding="utf-8"?> <configuration> <runtime> <AppContextSwitchOverrides value="Switch.System.Globalization.FormatJapaneseFirstYearAsANumber=true" /> </runtime> </configuration>
.NET Framework 4.5.2 或更早版本: 设置以下注册表值:
价值 密钥 HKEY_LOCAL_MACHINE\Software\Microsoft\.NETFramework\AppContext 条目 Switch.System.Globalization.FormatJapaneseFirstYearAsANumber 类型 REG_SZ 价值 是
由于禁用了 gannen 对格式设置作的支持,前面的示例将显示以下输出:
Japanese calendar date: 平成1年8月18日 (Gregorian: Friday, August 18, 1989)
.NET 也进行了更新,以便日期和时间分析操作支持包含表示为“1”或元年的年份的字符串。 虽然你不应该这样做,但你可以还原以前的行为,以仅识别“1”作为纪元的第一年。 可以按如下所示执行此作,具体取决于 .NET 的版本:
.NET Core: 将以下内容添加到 .netcore.runtime.json 配置文件:
"runtimeOptions": { "configProperties": { "Switch.System.Globalization.EnforceLegacyJapaneseDateParsing": true } }
.NET Framework 4.6 或更高版本: 在 app.config 文件中设置以下 AppContext 开关:
<?xml version="1.0" encoding="utf-8"?> <configuration> <runtime> <AppContextSwitchOverrides value="Switch.System.Globalization.EnforceLegacyJapaneseDateParsing=true" /> </runtime> </configuration>
.NET Framework 4.5.2 或更早版本: 设置以下注册表值:
价值 密钥 HKEY_LOCAL_MACHINE\Software\Microsoft\.NETFramework\AppContext 条目 Switch.System.Globalization.EnforceLegacyJapaneseDateParsing 类型 REG_SZ 价值 是