Преобразование времени из одного часового пояса в другой

Это становится все более важным для любого приложения, которое работает с датами и временем для обработки различий между часовыми поясами. Приложение больше не может предположить, что все время может быть выражено в локальном времени, что является временем, доступным из DateTime структуры. Например, веб-страница, отображающая текущее время в восточной части США, не будет доверять клиенту в восточной Азии. В этой статье объясняется, как преобразовать время из одного часового пояса в другой и преобразовать DateTimeOffset значения с ограниченными возможностями часового пояса.

Преобразование во время в формате UTC

Время в формате UTC — это высокоточный, атомарный стандарт времени. Часовые пояса мира выражаются как положительные или отрицательные смещения из UTC. Таким образом, в формате UTC предоставляется свободное часовое пояс или нейтральное время часового пояса. Использование UTC рекомендуется, когда важно переносимость даты и времени на компьютерах. Дополнительные сведения и другие рекомендации по использованию дат и времени см. в разделе "Рекомендации по написанию кода" с помощью DateTime в платформа .NET Framework. Преобразование отдельных часовых поясов во время в формате UTC упрощает сравнение времен.

Примечание.

Можно также сериализовать структуру DateTimeOffset для представления единой точки во времени однозначно. Поскольку DateTimeOffset объекты хранят значение даты и времени вместе со смещением из UTC, они всегда представляют определенную точку времени в отношении UTC.

Самый простой способ преобразовать время в UTC — вызвать static метод (Sharedв Visual Basic). TimeZoneInfo.ConvertTimeToUtc(DateTime) Точное преобразование, выполняемое методом, зависит от значения dateTime свойства параметра Kind , как показано в следующей таблице:

DateTime.Kind Преобразование
DateTimeKind.Local Преобразует местное время во время в формате UTC.
DateTimeKind.Unspecified Предполагает, что параметр dateTime является местным временем и преобразовывает местное время в UTC.
DateTimeKind.Utc Возвращает неизмененный параметр dateTime.

Следующий код преобразует текущее локальное время в формате UTC и отображает результат в консоль:

DateTime dateNow = DateTime.Now;
Console.WriteLine($"The date and time are {TimeZoneInfo.ConvertTimeToUtc(dateNow)} UTC.");
Dim dateNow As Date = Date.Now
Console.WriteLine("The date and time are {0} UTC.", _
                  TimeZoneInfo.ConvertTimeToUtc(dateNow))

Если значение даты и времени не представляет локальное время или utc, метод, скорее всего, ToUniversalTime вернет ошибочный результат. Однако метод можно использовать TimeZoneInfo.ConvertTimeToUtc для преобразования даты и времени из указанного часового пояса. Дополнительные сведения о получении объекта, представляющего часовой TimeZoneInfo пояс назначения, см. в статье Поиск часовых поясов, определенных в локальной системе. Следующий код использует метод для преобразования восточного стандартного TimeZoneInfo.ConvertTimeToUtc времени в UTC:

DateTime easternTime = new DateTime(2007, 01, 02, 12, 16, 00);
string easternZoneId = "Eastern Standard Time";
try
{
    TimeZoneInfo easternZone = TimeZoneInfo.FindSystemTimeZoneById(easternZoneId);
    Console.WriteLine($"The date and time are {TimeZoneInfo.ConvertTimeToUtc(easternTime, easternZone)} UTC.");
}
catch (TimeZoneNotFoundException)
{
    Console.WriteLine($"Unable to find the {easternZoneId} zone in the registry.");
}
catch (InvalidTimeZoneException)
{
    Console.WriteLine($"Registry data on the {easternZoneId} zone has been corrupted.");
}
Dim easternTime As New Date(2007, 01, 02, 12, 16, 00)
Dim easternZoneId As String = "Eastern Standard Time"
Try
    Dim easternZone As TimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(easternZoneId)
    Console.WriteLine("The date and time are {0} UTC.", _
                      TimeZoneInfo.ConvertTimeToUtc(easternTime, easternZone))
Catch e As TimeZoneNotFoundException
    Console.WriteLine("Unable to find the {0} zone in the registry.", _
                      easternZoneId)
Catch e As InvalidTimeZoneException
    Console.WriteLine("Registry data on the {0} zone has been corrupted.", _
                      easternZoneId)
End Try

Метод TimeZoneInfo.ConvertTimeToUtc создает исключение, ArgumentException если DateTime свойство объекта Kind и часовой пояс не совпадают. Несоответствие возникает, если свойство, но TimeZoneInfo объект не представляет локальный часовой пояс, или если Kind свойство DateTimeKind.Utc равно, но TimeZoneInfo объект не равенTimeZoneInfo.Utc.DateTimeKind.LocalKind

Все эти методы принимают DateTime значения в качестве параметров и возвращают DateTime значение. Для DateTimeOffset значений структура имеет ToUniversalTime метод экземпляра, DateTimeOffset который преобразует дату и время текущего экземпляра в формате UTC. В следующем примере метод вызывается ToUniversalTime для преобразования локального времени и нескольких других раз в формате UTC:

DateTimeOffset localTime, otherTime, universalTime;

// Define local time in local time zone
localTime = new DateTimeOffset(new DateTime(2007, 6, 15, 12, 0, 0));
Console.WriteLine("Local time: {0}", localTime);
Console.WriteLine();

// Convert local time to offset 0 and assign to otherTime
otherTime = localTime.ToOffset(TimeSpan.Zero);
Console.WriteLine("Other time: {0}", otherTime);
Console.WriteLine("{0} = {1}: {2}",
                  localTime, otherTime,
                  localTime.Equals(otherTime));
Console.WriteLine("{0} exactly equals {1}: {2}",
                  localTime, otherTime,
                  localTime.EqualsExact(otherTime));
Console.WriteLine();

// Convert other time to UTC
universalTime = localTime.ToUniversalTime();
Console.WriteLine("Universal time: {0}", universalTime);
Console.WriteLine("{0} = {1}: {2}",
                  otherTime, universalTime,
                  universalTime.Equals(otherTime));
Console.WriteLine("{0} exactly equals {1}: {2}",
                  otherTime, universalTime,
                  universalTime.EqualsExact(otherTime));
Console.WriteLine();
// The example produces the following output to the console:
//    Local time: 6/15/2007 12:00:00 PM -07:00
//
//    Other time: 6/15/2007 7:00:00 PM +00:00
//    6/15/2007 12:00:00 PM -07:00 = 6/15/2007 7:00:00 PM +00:00: True
//    6/15/2007 12:00:00 PM -07:00 exactly equals 6/15/2007 7:00:00 PM +00:00: False
//
//    Universal time: 6/15/2007 7:00:00 PM +00:00
//    6/15/2007 7:00:00 PM +00:00 = 6/15/2007 7:00:00 PM +00:00: True
//    6/15/2007 7:00:00 PM +00:00 exactly equals 6/15/2007 7:00:00 PM +00:00: True
Dim localTime, otherTime, universalTime As DateTimeOffset

' Define local time in local time zone
localTime = New DateTimeOffset(#6/15/2007 12:00:00PM#)
Console.WriteLine("Local time: {0}", localTime)
Console.WriteLine()

' Convert local time to offset 0 and assign to otherTime
otherTime = localTime.ToOffset(TimeSpan.Zero)
Console.WriteLine("Other time: {0}", otherTime)
Console.WriteLine("{0} = {1}: {2}", _
                  localTime, otherTime, _
                  localTime.Equals(otherTime))
Console.WriteLine("{0} exactly equals {1}: {2}", _
                  localTime, otherTime, _
                  localTime.EqualsExact(otherTime))
Console.WriteLine()

' Convert other time to UTC
universalTime = localTime.ToUniversalTime()
Console.WriteLine("Universal time: {0}", universalTime)
Console.WriteLine("{0} = {1}: {2}", _
                  otherTime, universalTime, _
                  universalTime.Equals(otherTime))
Console.WriteLine("{0} exactly equals {1}: {2}", _
                  otherTime, universalTime, _
                  universalTime.EqualsExact(otherTime))
Console.WriteLine()
' The example produces the following output to the console:
'    Local time: 6/15/2007 12:00:00 PM -07:00
'    
'    Other time: 6/15/2007 7:00:00 PM +00:00
'    6/15/2007 12:00:00 PM -07:00 = 6/15/2007 7:00:00 PM +00:00: True
'    6/15/2007 12:00:00 PM -07:00 exactly equals 6/15/2007 7:00:00 PM +00:00: False
'    
'    Universal time: 6/15/2007 7:00:00 PM +00:00
'    6/15/2007 7:00:00 PM +00:00 = 6/15/2007 7:00:00 PM +00:00: True
'    6/15/2007 7:00:00 PM +00:00 exactly equals 6/15/2007 7:00:00 PM +00:00: True   

Преобразование времени в формате UTC в заданный часовой пояс

Чтобы преобразовать время в формате UTC в местное, см. информацию в разделе Преобразование времени в формате UTC в местное время. Чтобы преобразовать UTC в время в любом заданном часовом поясе, вызовите ConvertTimeFromUtc метод. Метод принимает два параметра:

  • Время в формате UTC, которое требуется преобразовать. Это должно быть DateTime значение, для свойства которого Kind задано Unspecified значение или Utc.

  • Часовой пояс, в который требуется преобразовать время в формате UTC.

Следующий код преобразует UTC в центральное стандартное время:

DateTime timeUtc = DateTime.UtcNow;
try
{
    TimeZoneInfo cstZone = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
    DateTime cstTime = TimeZoneInfo.ConvertTimeFromUtc(timeUtc, cstZone);
    Console.WriteLine("The date and time are {0} {1}.",
                      cstTime,
                      cstZone.IsDaylightSavingTime(cstTime) ?
                              cstZone.DaylightName : cstZone.StandardName);
}
catch (TimeZoneNotFoundException)
{
    Console.WriteLine("The registry does not define the Central Standard Time zone.");
}
catch (InvalidTimeZoneException)
{
    Console.WriteLine("Registry data on the Central Standard Time zone has been corrupted.");
}
Dim timeUtc As Date = Date.UtcNow
Try
    Dim cstZone As TimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time")
    Dim cstTime As Date = TimeZoneInfo.ConvertTimeFromUtc(timeUtc, cstZone)
    Console.WriteLine("The date and time are {0} {1}.", _
                      cstTime, _
                      IIf(cstZone.IsDaylightSavingTime(cstTime), _
                          cstZone.DaylightName, cstZone.StandardName))
Catch e As TimeZoneNotFoundException
    Console.WriteLine("The registry does not define the Central Standard Time zone.")
Catch e As InvalidTimeZoneException
    Console.WriteLine("Registry data on the Central Standard Time zone has been corrupted.")
End Try

Преобразование времени в формате UTC в местное время

Чтобы преобразовать UTC в локальное время, вызовите ToLocalTime метод DateTime объекта, время которого требуется преобразовать. Точное поведение метода зависит от значения свойства объекта Kind , как показано в следующей таблице:

DateTime.Kind Преобразование
DateTimeKind.Local DateTime Возвращает значение без изменений.
DateTimeKind.Unspecified Предполагает, что DateTime значение — UTC и преобразует utc в локальное время.
DateTimeKind.Utc Преобразует значение в локальное DateTime время.

Примечание.

Метод TimeZone.ToLocalTime работает идентично методу DateTime.ToLocalTime . Для преобразования требуется один параметр, являющийся значением даты и времени.

Вы также можете преобразовать время в любой назначенный часовой пояс в местное время с помощью static метода (Sharedв Visual Basic). TimeZoneInfo.ConvertTime Этот метод рассматривается в следующем разделе.

Преобразование между любыми двумя часовыми поясами

Можно преобразовать между двумя часовых поясами с помощью одного из следующих двух static методов TimeZoneInfo класса (Sharedв Visual Basic):

  • ConvertTime

    Параметры этого метода — это значение даты и времени для преобразования, объект, TimeZoneInfo представляющий часовой пояс значения даты и времени, а TimeZoneInfo также объект, представляющий часовой пояс для преобразования значения даты и времени.

  • ConvertTimeBySystemTimeZoneId

    Параметры этого метода — это значение даты и времени для преобразования, идентификатор часового пояса даты и времени, а также идентификатор часового пояса для преобразования значения даты и времени в.

Оба метода требуют, чтобы Kind свойство значения даты и времени для преобразования и TimeZoneInfo объекта или часового пояса, представляющего его часовой пояс, соответствовало друг другу. В противном случае возникает исключение ArgumentException. Например, если свойство значения даты и времени имеет значение, исключение возникает, если KindTimeZoneInfo объект, переданный в качестве параметра методу, не равенTimeZoneInfo.Local.DateTimeKind.Local Исключение также возникает, если идентификатор, переданный в качестве параметра методу, не равен TimeZoneInfo.Local.Id.

В следующем примере метод используется ConvertTime для преобразования из Гавайского стандартного времени в местное время:

DateTime hwTime = new DateTime(2007, 02, 01, 08, 00, 00);
try
{
    TimeZoneInfo hwZone = TimeZoneInfo.FindSystemTimeZoneById("Hawaiian Standard Time");
    Console.WriteLine("{0} {1} is {2} local time.",
            hwTime,
            hwZone.IsDaylightSavingTime(hwTime) ? hwZone.DaylightName : hwZone.StandardName,
            TimeZoneInfo.ConvertTime(hwTime, hwZone, TimeZoneInfo.Local));
}
catch (TimeZoneNotFoundException)
{
    Console.WriteLine("The registry does not define the Hawaiian Standard Time zone.");
}
catch (InvalidTimeZoneException)
{
    Console.WriteLine("Registry data on the Hawaiian Standard Time zone has been corrupted.");
}
Dim hwTime As Date = #2/01/2007 8:00:00 AM#
Try
    Dim hwZone As TimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Hawaiian Standard Time")
    Console.WriteLine("{0} {1} is {2} local time.", _
                      hwTime, _
                      IIf(hwZone.IsDaylightSavingTime(hwTime), hwZone.DaylightName, hwZone.StandardName), _
                      TimeZoneInfo.ConvertTime(hwTime, hwZone, TimeZoneInfo.Local))
Catch e As TimeZoneNotFoundException
    Console.WriteLine("The registry does not define the Hawaiian Standard Time zone.")
Catch e As InvalidTimeZoneException
    Console.WriteLine("Registry data on the Hawaiian Standard Time zone has been corrupted.")
End Try

Преобразование значений DateTimeOffset

Значения даты и времени, представленные объектами, не полностью осведомлены о часовом DateTimeOffset поясе, так как объект отсоединяется от часового пояса во время создания экземпляра. Однако во многих случаях приложению просто необходимо преобразовать дату и время на основе двух разных смещения из UTC, а не по времени в определенных часовых поясах. Для выполнения этого преобразования можно вызвать метод текущего экземпляра ToOffset . Один параметр метода — это смещение нового значения даты и времени, возвращаемое методом.

Например, если дата и время запроса пользователя на веб-страницу известна и сериализуется как строка в формате MM/dd/гггг чч:мм:ss zzzz, следующий ReturnTimeOnServer метод преобразует это значение даты и времени в дату и время на веб-сервере:

public DateTimeOffset ReturnTimeOnServer(string clientString)
{
   string format = @"M/d/yyyy H:m:s zzz";
   TimeSpan serverOffset = TimeZoneInfo.Local.GetUtcOffset(DateTimeOffset.Now);

   try
   {
      DateTimeOffset clientTime = DateTimeOffset.ParseExact(clientString, format, CultureInfo.InvariantCulture);
      DateTimeOffset serverTime = clientTime.ToOffset(serverOffset);
      return serverTime;
   }
   catch (FormatException)
   {
      return DateTimeOffset.MinValue;
   }
}
Public Function ReturnTimeOnServer(clientString As String) As DateTimeOffset
    Dim format As String = "M/d/yyyy H:m:s zzz"
    Dim serverOffset As TimeSpan = TimeZoneInfo.Local.GetUtcOffset(DateTimeOffset.Now)

    Try
        Dim clientTime As DateTimeOffset = DateTimeOffset.ParseExact(clientString, format, CultureInfo.InvariantCulture)
        Dim serverTime As DateTimeOffset = clientTime.ToOffset(serverOffset)
        Return serverTime
    Catch e As FormatException
        Return DateTimeOffset.MinValue
    End Try
End Function

Если метод передает строку "9/1/2007 5:32:07 -05:00", представляющую дату и время в часовом поясе пять часов до UTC, он возвращает "9/1.2007 3:32:07 -07:00" для сервера, расположенного в стандартном часовом поясе США.

Класс TimeZoneInfo также включает перегрузку TimeZoneInfo.ConvertTime(DateTimeOffset, TimeZoneInfo) метода, выполняющего преобразования часовых поясов со значениями ToOffset(TimeSpan) . Параметры метода — это значение и ссылка на часовой DateTimeOffset пояс, в который необходимо преобразовать время. Вызов метода возвращает DateTimeOffset значение. Например, ReturnTimeOnServer метод в предыдущем примере может быть перезаписан следующим образом, чтобы вызвать ConvertTime(DateTimeOffset, TimeZoneInfo) метод.

public DateTimeOffset ReturnTimeOnServer(string clientString)
{
   string format = @"M/d/yyyy H:m:s zzz";

   try
   {
      DateTimeOffset clientTime = DateTimeOffset.ParseExact(clientString, format,
                                  CultureInfo.InvariantCulture);
      DateTimeOffset serverTime = TimeZoneInfo.ConvertTime(clientTime,
                                  TimeZoneInfo.Local);
      return serverTime;
   }
   catch (FormatException)
   {
      return DateTimeOffset.MinValue;
   }
}
Public Function ReturnTimeOnServer(clientString As String) As DateTimeOffset
    Dim format As String = "M/d/yyyy H:m:s zzz"

    Try
        Dim clientTime As DateTimeOffset = DateTimeOffset.ParseExact(clientString, format, CultureInfo.InvariantCulture)
        Dim serverTime As DateTimeOffset = TimeZoneInfo.ConvertTime(clientTime, TimeZoneInfo.Local)
        Return serverTime
    Catch e As FormatException
        Return DateTimeOffset.MinValue
    End Try
End Function

См. также