Поделиться через


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

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

Преобразование в координированное универсальное время

Координированное универсальное время (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, если свойство Kind объекта DateTime и часовой пояс не совпадают. Несоответствие возникает, если свойство Kind — это DateTimeKind.Local, но объект TimeZoneInfo не представляет локальный часовой пояс, или если свойство Kind — это DateTimeKind.Utc, но объект TimeZoneInfo не равен TimeZoneInfo.Utc.

Все эти методы принимают DateTime значения в качестве параметров и возвращают DateTime значение. Для значений DateTimeOffset структура DateTimeOffset имеет метод экземпляра ToUniversalTime, который преобразует дату и время текущего экземпляра в формат 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: {localTime}");
Console.WriteLine();

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

// Convert other time to UTC
universalTime = localTime.ToUniversalTime();
Console.WriteLine($"Universal time: {universalTime}");
Console.WriteLine($"{otherTime} = {universalTime}: {universalTime.Equals(otherTime)}");
Console.WriteLine($"{otherTime} exactly equals {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 (Shared в Visual Basic):

  • ConvertTime

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

  • ConvertTimeBySystemTimeZoneId

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

Оба метода требуют, чтобы Kind свойство значения даты и времени, которое необходимо преобразовать, и TimeZoneInfo объект или идентификатор часового пояса, представляющий его часовой пояс, соответствовали друг другу. В противном случае выбрасывается ArgumentException. Например, если свойство значения даты и времени Kind имеет значение DateTimeKind.Local, возникает исключение, если объект TimeZoneInfo, переданный в метод в качестве параметра, не равен TimeZoneInfo.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 AM -07:00" для сервера, расположенного в Тихоокеанском стандартном времени (PST).

Класс 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

См. также