Работа с календарями

Несмотря на то что значение даты и времени обозначает момент во времени, его строковое представление зависит от конкретного языка и региональных параметров. На него влияют как принятые стандарты обозначения даты и времени, так и используемый календарь. В этом разделе рассматривается поддержка календарей в .NET и рассматривается использование классов календарей при работе со значениями даты.

Календари в .NET

Все календари в .NET являются производными от System.Globalization.Calendar класса, который предоставляет базовую реализацию календаря. Одним из классов, наследующих от класса Calendar, является класс EastAsianLunisolarCalendar — базовый класс для всех лунно-солнечных календарей. .NET включает следующие реализации календаря:

  • ChineseLunisolarCalendar. Представляет китайский лунно-солнечный календарь.

  • GregorianCalendar. Представляет григорианский календарь. Этот календарь, в свою очередь, разделяется на подтипы (такие как арабский и ближневосточный французский), определяемые перечислением System.Globalization.GregorianCalendarTypes. Свойство GregorianCalendar.CalendarType определяет подтип григорианского календаря.

  • HebrewCalendar. Представляет еврейский календарь.

  • HijriCalendar. Представляет календарь Хиджра.

  • JapaneseCalendar. Представляет японский календарь.

  • JapaneseLunisolarCalendar. Представляет японский лунно-солнечный календарь.

  • JulianCalendarПредставляет юлианский календарь.

  • KoreanCalendar. Представляет корейский календарь.

  • KoreanLunisolarCalendar. Представляет корейский лунно-солнечный календарь.

  • PersianCalendar. Представляет персидский календарь.

  • TaiwanCalendar. Представляет тайваньский календарь.

  • TaiwanLunisolarCalendar. Представляет тайваньский лунно-солнечный календарь.

  • ThaiBuddhistCalendar. Представляет тай-буддистский календарь.

  • UmAlQuraCalendar. Представляет саудовский календарь.

Календарь можно использовать одним из двух способов:

  • Как календарь, используемый для конкретных языка и региональных параметров. Каждый объект CultureInfo имеет текущий календарь, то есть календарь, который объект использует в настоящий момент. Строковое представление значений даты и времени автоматически отражает используемые язык и региональные параметры и текущий календарь. Обычно текущий календарь является календарем по умолчанию для языка и региональных параметров. CultureInfo Объекты также имеют необязательные календари, которые включают дополнительные календари, которые могут использовать язык и региональные параметры.

  • Как самостоятельный календарь, не зависящий от конкретных языка и региональных параметров. В таком случае методы Calendar используются для выражения дат и значений календаря.

Обратите внимание, что шесть классов календаря (ChineseLunisolarCalendar, JapaneseLunisolarCalendar, JulianCalendar, KoreanLunisolarCalendar, 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 {0} culture:", ci.Name);

      // Get the culture's default calendar.
      Calendar defaultCalendar = ci.Calendar;
      Console.Write("   Default Calendar: {0}", GetCalendarName(defaultCalendar));

      if (defaultCalendar is GregorianCalendar)
         Console.WriteLine(" ({0})",
                           ((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 и отображается с использованием текущего языка и региональных параметров (в данном случае "Английский (США)") и текущего календаря для языка и региональных параметров (в данном случае григорианского календаря). Затем текущий язык и региональные параметры изменяются на "Арабский (Саудовская Аравия)" и дата отображается с использованием календаря по умолчанию — саудовского календаря. Затем вызывается метод CalendarExists, чтобы определить, поддерживается ли календарь Хиджра языком и региональными параметрами "Арабский (Саудовская Аравия)". Поскольку этот календарь поддерживается, текущий календарь изменяется на календарь Хиджра и дата отображается снова. Обратите внимание, что в обоих случаях дата отображается с использованием текущего календаря текущего языка и региональных параметров.

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: {0}",
                        CultureInfo.CurrentCulture.Name);
      Console.WriteLine("Current Calendar: {0}",
                        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 года. Очевидно, что это значение интерпретируется как дата григорианского календаря, поскольку именно эти значения возвращаются методом DateTime.ToString(String, IFormatProvider) при использовании стандартов инвариантного языка и региональных параметров. Строковое представление даты форматируется по стандартам текущего языка и региональных параметров и выглядит как 14/08/32, указывая соответствующую дату по саудовскому календарю. Затем члены DateTime и Calendar используются для возврата дня, месяца и года значения DateTime. В любом случае, значения, возвращаемые членами DateTime, отражают значения по григорианскому календарю, а значения, возвращаемые членами UmAlQuraCalendar отражают значения по саудовскому календарю.

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: {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();
   }

   private static void DisplayCurrentInfo()
   {
      Console.WriteLine("Current Culture: {0}",
                        CultureInfo.CurrentCulture.Name);
      Console.WriteLine("Current Calendar: {0}",
                        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: {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"));
   }
}
// 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.GetMonth и Calendar.GetDayOfMonth преобразуют значения года, месяца и дня в значения, соответствующие определенному календарю.

Предупреждение

Поскольку некоторые календари не являются дополнительными календарями ни для какого языка и региональных параметров, указание дат по этим календарям всегда требует вызова методов календаря. Это верно для всех календарей, унаследованных от классов EastAsianLunisolarCalendar, JulianCalendar и PersianCalendar.

В следующем примере используется объект JulianCalendar для создания даты 9 января 1905 года по юлианскому календарю. При отображении этой даты по календарю по умолчанию (григорианскому), она представляется как 22 января 1905 года. Вызов отдельных методов JulianCalendar позволяет отобразить дату по юлианскому календарю.

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 Для класса эта дата — 1 января 0001 Г. Большинство других календарей в .NET поддерживают более позднюю дату. При попытке работы с датой и временем, меньшими самой ранней поддерживаемой календарем даты, возникнет исключение ArgumentOutOfRangeException.

Однако существует одно важное исключение из этого правила. По умолчанию (неинициализированное) значение объекта DateTime и объекта DateTimeOffset равно значению GregorianCalendar.MinSupportedDateTime. Если вы пытаетесь отформатировать эту дату в календаре, который не поддерживает 1 января 0001 г. C.E. и не предоставляет описатель формата, метод форматирования использует описатель формата "s" (шаблон даты и времени сортировки) вместо описателя формата "G" (общий шаблон даты и времени). В результате операция форматирования не вызовет исключения ArgumentOutOfRangeException. Вместо этого будет возвращена неподдерживаемая дата. Это продемонстрировано в следующем примере, отображающем значение DateTime.MinValue во время установки текущих языка и региональных параметров в японские (Япония) с японским календарем и в арабские (Египет) с календарем Um Al Qura. Также текущие язык и региональные параметры устанавливаются в английские (США) и вызывается метод 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 {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.
      CultureInfo arEG = 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"));
   }

   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 поддерживают несколько эр.

Внимание

Эра Рейва, новая эра в JapaneseCalendar и JapaneseLunisolarCalendar, начинается 1 мая 2019 года. Это изменение затрагивает все приложения, использующие эти календари. Дополнительные сведения см. в этих статьях:

Эра в большинстве календарей обозначает чрезвычайно длительный период времени. Например, в григорианском календаре, текущая эра охватывает более двух тысячелетий. JapaneseCalendar Для двух JapaneseLunisolarCalendarкалендарей, поддерживающих несколько эпох, это не так. Эра соответствует периоду правления императора. Поддержка нескольких эпох, особенно когда верхний предел текущей эпохи неизвестен, представляет особые проблемы.

Эры и имена эпох

В .NET целые числа, представляющие эры, поддерживаемые определенной реализацией календаря, хранятся в обратном порядке в массиве Calendar.Eras . Текущая эра (которая является эрой с последним диапазоном времени) находится в нулевом индексе, а для Calendar классов, поддерживающих несколько эпох, каждый последовательный индекс отражает предыдущую эпоху. Статическое свойство Calendar.CurrentEra определяет индекс текущей эры в массиве Calendar.Eras; это константа, значение которой всегда ноль. Отдельные классы Calendar также включают статические поля, возвращающие значение текущей эры. Они перечислены в следующей таблице.

Класс календаря Поле текущей эры
ChineseLunisolarCalendar ChineseEra
GregorianCalendar ADEra
HebrewCalendar HebrewEra
HijriCalendar HijriEra
JapaneseLunisolarCalendar JapaneseEra
JulianCalendar JulianEra
KoreanCalendar KoreanEra
KoreanLunisolarCalendar GregorianEra
PersianCalendar PersianEra
ThaiBuddhistCalendar ThaiBuddhistEra
UmAlQuraCalendar UmAlQuraEra

Имя, соответствующее номеру конкретной эры, которое можно получить, передав номер эры методу DateTimeFormatInfo.GetEraName или DateTimeFormatInfo.GetAbbreviatedEraName. В следующем примере эти методы вызываются для получения информации о поддержке эр в классе GregorianCalendar. В нем отображается дата григорианского календаря, соответствующая 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. Обычно если эра не указана, методы даты и времени и календаря предполагают, что значения относятся к текущей эре. Это верно для методов и DateTimeOffset конструкторов, которые включают параметры типаCalendar, а также методы JapaneseCalendar.ToDateTime и японскогоLunisolarCalendar.ToDateTime.DateTime В следующем примере создается экземпляр даты, представляющей 1 января второго года неопределенной эпохи. Если выполнить пример, когда эра Reiwa является текущей, дата интерпретируется как второй год эры Рейва. Эра, 令和, предшествует году в строке, возвращаемой DateTime.ToString(String, IFormatProvider) методом, и соответствует 1 января 2020 года в григорианском календаре. (Эра Reiwa начинается в 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

Однако если эра изменяется, намерение этого кода становится неоднозначным. Является ли дата, предназначенная для представления второго года текущей эпохи, или должна ли она представлять второй год эпохи Heisei? Существует два способа избежать этой неоднозначности:

  • Создайте экземпляр значения даты и времени с помощью класса по умолчанию 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
    
    
    
  • Вызовите метод даты и времени, который явно указывает эру. Включены следующие методы:

    В следующем примере используется три из этих методов для создания экземпляра даты и времени в эпоху Meiji, которая началась 8 сентября 1868 года и закончилась 29 июля 1912 года.

    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 текущий календарь CultureInfo объекта, представляющий язык и региональные параметры ja-JP. (Этот метод работает JapaneseLunisolarCalendar так же, так как он поддерживает те же эпохи, что JapaneseCalendarи .) В предыдущем примере показан этот подход.

Календари, эры и диапазоны дат: расслабленный диапазон проверка

Очень похоже, что отдельные календари поддерживают диапазоны дат, эры в JapaneseCalendar и JapaneseLunisolarCalendar классах также поддерживают диапазоны. Ранее .NET использовал строгий диапазон эры проверка, чтобы убедиться, что дата, определяемая эпохой, находится в диапазоне этой эпохи. То есть, если дата выходит за пределы диапазона указанной эпохи, метод вызывает ArgumentOutOfRangeExceptionисключение. В настоящее время .NET использует расслабленный диапазон проверка по умолчанию. Обновления для всех версий .NET появились расслабленные диапазоны эры проверка; попытка создать экземпляр даты, относящуюся к эпохе, которая находится за пределами диапазона указанной эры "переполнения" в следующую эпоху, и никаких исключений не возникает.

Следующий пример пытается создать экземпляр даты в 65-м году эпохи Showa, которая началась 25 декабря 1926 года и закончилась 7 января 1989 года. Эта дата соответствует 9 января 1990 года, которая находится за пределами диапазона эпохи Showa в .JapaneseCalendar Как показано в выходных данных примера, дата, отображаемая примером, — 9 января 1990 года, во втором году эры Heisei.

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 или более поздней версии: Задайте следующий параметр AppContext в файле app.config:

    <?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
    Value true

Если включена строгая проверка диапазона, предыдущий пример создает исключение 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, объекта, представляющего язык и региональные параметры японского CultureInfo языка (Япония).

Для всех календарей описатель пользовательского формата "g" добавляет эру в результирующую строку. В следующем примере используется строка пользовательского формата "мм-дд-гггг g", где в дату включается эра, при том что текущим является григорианский календарь.

   DateTime dat = new DateTime(2012, 5, 1);
   Console.WriteLine("{0:MM-dd-yyyy g}", dat);
// 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 первый год эры Heisei можно охарактеризовать как Heisei Gannen. .NET принимает это соглашение в операциях форматирования дат и времени, отформатированных со следующими стандартными или настраиваемыми строками формата даты и времени, когда они используются с CultureInfo объектом, представляющим язык и региональные параметры японско-японского языка (ja-JP) с JapaneseCalendar классом:

Например, в следующем примере отображается дата в первом году эры Heisei в .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 или более поздней версии: Задайте следующий параметр AppContext в файле app.config:

    <?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
    Value true

При отключенной поддержке 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 или более поздней версии: Задайте следующий параметр AppContext в файле app.config:

    <?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
    Value true

См. также