Udostępnij przez


Konwertowanie czasów między strefami czasowymi

Staje się coraz ważniejsza dla każdej aplikacji, która współpracuje z datami i godzinami, aby obsługiwać różnice między strefami czasowymi. Aplikacja nie może już zakładać, że wszystkie czasy można wyrazić w czasie lokalnym, co jest czasem dostępnym ze struktury DateTime. Na przykład strona internetowa, która wyświetla bieżący czas we wschodniej części Stanów Zjednoczonych, nie będzie wiarygodna dla klienta we wschodniej Azji. W tym artykule wyjaśniono, jak konwertować czasy z jednej strefy czasowej na inną i konwertować wartości DateTimeOffset, które mają ograniczoną świadomość strefy czasowej.

Konwertowanie na uniwersalny czas koordynowany

Uniwersalny czas koordynowany (UTC) to standard czasu atomowego o wysokiej precyzji. Strefy czasowe świata są wyrażane jako dodatnie lub ujemne przesunięcia z czasu UTC. W związku z tym, czas UTC zapewnia czas wolny od stref czasowych lub czas neutralny względem stref czasowych. Użycie czasu UTC jest zalecane, gdy ważna jest przenośność daty i godziny na komputerach. Aby uzyskać szczegółowe informacje i inne najlepsze rozwiązania dotyczące korzystania z dat i godzin, zobacz Kodowanie najlepszych rozwiązań dotyczących używania daty/godziny w programie .NET Framework. Konwertowanie poszczególnych stref czasowych na czas UTC sprawia, że porównania czasu są łatwe.

Uwaga

Można również serializować strukturę DateTimeOffset w celu jednoznacznego reprezentowania pojedynczego punktu w czasie. Ponieważ obiekty DateTimeOffset przechowują wartość daty i godziny wraz z przesunięciem od czasu UTC, zawsze reprezentują określony punkt w czasie w odniesieniu do czasu UTC.

Najprostszym sposobem przekonwertowania czasu na utc jest wywołanie metody static (Shared w języku Visual Basic TimeZoneInfo.ConvertTimeToUtc(DateTime)). Dokładna konwersja wykonywana przez metodę zależy od wartości właściwości dateTime parametru Kind, jak pokazano w poniższej tabeli:

DateTime.Kind Konwersja
DateTimeKind.Local Konwertuje czas lokalny na czas UTC.
DateTimeKind.Unspecified Przyjęto założenie, że parametr dateTime to czas lokalny i konwertuje czas lokalny na czas UTC.
DateTimeKind.Utc Zwraca parametr dateTime bez zmian.

Poniższy kod konwertuje bieżący czas lokalny na czas UTC i wyświetla wynik w konsoli:

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))

Jeśli wartość daty i godziny nie reprezentuje czasu lokalnego ani czasu UTC, metoda ToUniversalTime prawdopodobnie zwróci błędny wynik. Można jednak użyć metody TimeZoneInfo.ConvertTimeToUtc, aby przekonwertować datę i godzinę z określonej strefy czasowej. Aby uzyskać szczegółowe informacje na temat pobierania obiektu TimeZoneInfo reprezentującego docelową strefę czasową, zobacz Znajdowanie stref czasowych zdefiniowanych w systemie lokalnym. Poniższy kod używa metody TimeZoneInfo.ConvertTimeToUtc, aby przekonwertować wschodni czas standardowy na 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

Metoda TimeZoneInfo.ConvertTimeToUtc zgłasza ArgumentException, jeśli właściwość DateTime obiektu Kind jest niezgodna ze strefą czasową. Niezgodność występuje, jeśli właściwość Kind jest DateTimeKind.Local, ale obiekt TimeZoneInfo nie reprezentuje lokalnej strefy czasowej lub jeśli właściwość Kind jest DateTimeKind.Utc, ale obiekt TimeZoneInfo nie jest równy TimeZoneInfo.Utc.

Wszystkie te metody przyjmują wartości DateTime jako parametry i zwracają wartość DateTime. W przypadku wartości DateTimeOffset struktura DateTimeOffset ma metodę instancji ToUniversalTime, która konwertuje datę i godzinę bieżącego wystąpienia na UTC. Poniższy przykład wywołuje metodę ToUniversalTime, aby przekonwertować czas lokalny i kilka innych godzin na 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   

Konwertowanie czasu UTC na wyznaczoną strefę czasową

Aby przekonwertować czas UTC na lokalny, zobacz sekcję Konwertowanie czasu UTC na czas lokalny poniżej. Aby przekonwertować czas UTC na czas w dowolnej wyznaczonej strefie czasowej, wywołaj metodę ConvertTimeFromUtc. Metoda przyjmuje dwa parametry:

  • UTC, który należy przekonwertować. Musi to być wartość DateTime, której właściwość Kind jest ustawiona na wartość Unspecified lub Utc.

  • Strefa czasowa, do której ma być konwertowany czas UTC.

Poniższy kod konwertuje czas UTC na środkowy czas standardowy:

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

Konwertowanie czasu UTC na czas lokalny

Aby przekonwertować czas UTC na lokalny, wywołaj metodę ToLocalTime obiektu DateTime, którego czas chcesz przekonwertować. Dokładne zachowanie metody zależy od wartości właściwości Kind obiektu, jak pokazano w poniższej tabeli:

DateTime.Kind Konwersja
DateTimeKind.Local Zwraca wartość DateTime bez zmian.
DateTimeKind.Unspecified Przyjęto założenie, że wartość DateTime to UTC i konwertuje czas UTC na czas lokalny.
DateTimeKind.Utc Konwertuje wartość DateTime na czas lokalny.

Uwaga

Metoda TimeZone.ToLocalTime zachowuje się identycznie z metodą DateTime.ToLocalTime. Przyjmuje jeden parametr, który jest wartością daty i godziny do przekonwertowania.

Możesz również przekonwertować czas w dowolnej wyznaczonej strefie czasowej na czas lokalny przy użyciu metody static (Shared w języku Visual Basic TimeZoneInfo.ConvertTime). Ta technika została omówiona w następnej sekcji.

Konwertowanie między dowolnymi dwiema strefami czasowymi

Można przekonwertować między dwiema strefami czasowymi przy użyciu jednej z następujących dwóch metod static (Shared w języku Visual Basic) klasy TimeZoneInfo:

  • ConvertTime

    Parametry tej metody to wartość daty i godziny, która ma być konwertowana, obiekt TimeZoneInfo reprezentujący strefę czasową wartości daty i godziny oraz obiekt TimeZoneInfo reprezentujący strefę czasową w celu przekonwertowania wartości daty i godziny na.

  • ConvertTimeBySystemTimeZoneId

    Parametry tej metody to wartość daty i godziny, która ma być konwertowana, identyfikator strefy czasowej wartości daty i godziny oraz identyfikator strefy czasowej w celu przekonwertowania wartości daty i godziny na.

Obie metody wymagają, aby właściwość Kind wartości daty i godziny do przekonwertowania oraz TimeZoneInfo obiektu lub identyfikatora strefy czasowej reprezentującej jej strefę czasową odpowiadały sobie nawzajem. W przeciwnym razie ArgumentException zostanie wyrzucony. Jeśli na przykład właściwość Kind wartości daty i godziny jest DateTimeKind.Local, zgłaszany jest wyjątek, jeśli obiekt TimeZoneInfo przekazany jako parametr do metody nie jest równy TimeZoneInfo.Local. Wyjątek jest również zgłaszany, jeśli identyfikator przekazany jako parametr do metody nie jest równy TimeZoneInfo.Local.Id.

W poniższym przykładzie użyto metody ConvertTime do konwersji z hawajskiego czasu standardowego na czas lokalny:

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

Konwertowanie wartości DateTimeOffset

Wartości daty i godziny reprezentowane przez obiekty DateTimeOffset nie są w pełni zgodne ze strefą czasową, ponieważ obiekt jest rozdzielony od strefy czasowej w momencie jego utworzenia. Jednak w wielu przypadkach aplikacja musi po prostu przekonwertować datę i godzinę na podstawie dwóch różnych przesunięć od UTC, a nie czasu w określonych strefach czasowych. Aby wykonać tę konwersję, możesz wywołać metodę ToOffset bieżącego wystąpienia. Pojedynczy parametr metody jest przesunięciem nowej wartości daty i godziny zwracanej przez metodę.

Jeśli na przykład data i godzina żądania użytkownika dla strony internetowej jest znana i jest serializowana jako ciąg w formacie MM/dd/rrrr hh:mm:ss zzzz, następująca metoda ReturnTimeOnServer konwertuje tę wartość daty i godziny na datę i godzinę na serwerze internetowym:

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

Jeśli metoda przekazuje ciąg "9/1/2007 5:32:07 -05:00", który reprezentuje datę i godzinę w strefie czasowej pięć godzin wcześniej niż UTC, zwraca wartość "9/1/2007 3:32:07 AM -07:00" dla serwera znajdującego się w strefie czasowej Pacyficznej Stanów Zjednoczonych.

Klasa TimeZoneInfo zawiera również przeciążenie metody TimeZoneInfo.ConvertTime(DateTimeOffset, TimeZoneInfo), która wykonuje konwersje strefy czasowej z wartościami ToOffset(TimeSpan). Parametry metody są wartością DateTimeOffset i odwołaniem do strefy czasowej, do której ma zostać przekonwertowana godzina. Wywołanie metody zwraca wartość DateTimeOffset. Na przykład metoda ReturnTimeOnServer w poprzednim przykładzie może zostać przepisana w następujący sposób, aby wywołać metodę 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

Zobacz też