Převádění časů mezi časovými pásmy

Je stále důležitější pro každou aplikaci, která pracuje s daty a časy, aby zvládla rozdíly mezi časovými pásmy. Aplikace již nemůže předpokládat, že všechny časy mohou být vyjádřeny v místním čase, což je čas dostupný ze DateTime struktury. Například webová stránka, která zobrazuje aktuální čas ve východní části USA nebude mít důvěryhodnost zákazníka ve východní Asii. Tento článek vysvětluje, jak převést časy z jednoho časového pásma na jiné a převést DateTimeOffset hodnoty, které mají omezené povědomí o časovém pásmu.

Převod na koordinovaný univerzální čas

Koordinovaný univerzální čas (UTC) je standard s vysokou přesností a atomovým časem. Časová pásma světa jsou vyjádřena jako kladné nebo záporné posuny od UTC. UTC proto poskytuje časovou zónu volného času nebo neutrálního časového pásma. Použití standardu UTC se doporučuje, pokud je důležitá přenositelnost data a času mezi počítači. Podrobnosti a další osvědčené postupy při používání kalendářních dat a časů najdete v tématu Kódování osvědčených postupů pomocí dateTime v rozhraní .NET Framework. Převod jednotlivých časových pásem na UTC usnadňuje porovnání času.

Poznámka:

Můžete také serializovat DateTimeOffset strukturu tak, aby představovala jeden bod v čase jednoznačně. Vzhledem k tomu, že DateTimeOffset objekty ukládají hodnotu data a času spolu s posunem od času UTC, vždy představují konkrétní bod v čase vzhledem k času UTC.

Nejjednodušší způsob, jak převést čas na UTC, je volání static metody (Sharedv jazyce Visual Basic). TimeZoneInfo.ConvertTimeToUtc(DateTime) Přesný převod provedený metodou závisí na hodnotě dateTime vlastnosti parametru Kind , jak ukazuje následující tabulka:

DateTime.Kind Převod
DateTimeKind.Local Převede místní čas na UTC.
DateTimeKind.Unspecified Předpokládá, že dateTime parametr je místní čas a převede místní čas na UTC.
DateTimeKind.Utc dateTime Vrátí parametr beze změny.

Následující kód převede aktuální místní čas na UTC a zobrazí výsledek do konzoly:

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

Pokud hodnota data a času nepředstavuje místní čas nebo UTC, ToUniversalTime metoda pravděpodobně vrátí chybný výsledek. Pomocí metody však můžete TimeZoneInfo.ConvertTimeToUtc převést datum a čas ze zadaného časového pásma. Podrobnosti o načtení objektu TimeZoneInfo , který představuje cílové časové pásmo, naleznete v tématu Vyhledání časových pásem definovaných v místním systému. Následující kód používá metodu k převodu východního standardního TimeZoneInfo.ConvertTimeToUtc času 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 vyvolá chybu ArgumentException , pokud je vlastnost objektu DateTimeKind a časové pásmo neodpovídají. Neshoda nastane, Kind pokud je DateTimeKind.Local vlastnost, ale TimeZoneInfo objekt nepředstavuje místní časové pásmo, nebo pokud Kind je DateTimeKind.Utc vlastnost, ale TimeZoneInfo objekt se nerovná TimeZoneInfo.Utc.

Všechny tyto metody přebírají DateTime hodnoty jako parametry a vracejí DateTime hodnotu. Pro DateTimeOffset hodnoty DateTimeOffset má struktura metodu ToUniversalTime instance, která převede datum a čas aktuální instance na UTC. Následující příklad volá metodu ToUniversalTime pro převod místního času a několikrát jindy 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: {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   

Převod UTC na určené časové pásmo

Pokud chcete převést UTC na místní čas, přečtěte si část Převod UTC na místní čas , která následuje. Pokud chcete převést utc na čas v libovolném časovém pásmu, které určíte, zavolejte metodu ConvertTimeFromUtc . Metoda má dva parametry:

  • Utc, který chcete převést. Musí to být DateTime hodnota, jejíž Kind vlastnost je nastavena na Unspecified nebo Utc.

  • Časové pásmo, na které se má čas UTC převést.

Následující kód převede UTC na standardní čas (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

Převod času UTC na místní čas

Chcete-li převést UTC na místní čas, zavolejte ToLocalTime metodu objektu DateTime , jehož čas chcete převést. Přesné chování metody závisí na hodnotě vlastnosti objektu Kind , jak ukazuje následující tabulka:

DateTime.Kind Převod
DateTimeKind.Local DateTime Vrátí hodnotu beze změny.
DateTimeKind.Unspecified Předpokládá, že DateTime hodnota je UTC a převede utc na místní čas.
DateTimeKind.Utc DateTime Převede hodnotu na místní čas.

Poznámka:

Metoda TimeZone.ToLocalTime se chová stejně jako DateTime.ToLocalTime metoda. Převod trvá jeden parametr, což je hodnota data a času.

Pomocí metody (Sharedv jazyce Visual Basic) TimeZoneInfo.ConvertTime můžete také převést čas v libovolném určeném časovém pásmu na místní čas static . Tato technika je popsána v další části.

Převod mezi dvěma časovými pásmy

Mezi libovolnými dvěma časovými pásmy můžete převést některou z následujících dvou static metod TimeZoneInfo (Sharedv jazyce Visual Basic) třídy:

  • ConvertTime

    Parametry této metody jsou hodnota data a času, která se má převést, TimeZoneInfo objekt, který představuje časové pásmo hodnoty data a času, a TimeZoneInfo objekt, který představuje časové pásmo pro převod hodnoty data a času na.

  • ConvertTimeBySystemTimeZoneId

    Parametry této metody jsou hodnota data a času, která se má převést, identifikátor časového pásma hodnoty data a času a identifikátor časového pásma pro převod hodnoty data a času na.

Obě metody vyžadují, aby Kind vlastnost hodnoty data a času převeďte a identifikátor objektu TimeZoneInfo nebo časového pásma, který představuje jeho časové pásmo, vzájemně odpovídá. ArgumentException V opačném případě je vyvolán. Pokud je například Kind vlastnost hodnoty DateTimeKind.Localdata a času , je vyvolán výjimka, pokud TimeZoneInfo objekt předán jako parametr metodě není rovna TimeZoneInfo.Local. Výjimka se vyvolá také v případě, že identifikátor předaný jako parametr metodě není rovna TimeZoneInfo.Local.Id.

Následující příklad používá metodu ConvertTime pro převod z havajského standardního času na místní čas:

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

Převod hodnot DateTimeOffset

Hodnoty data a času reprezentované DateTimeOffset objekty nejsou plně v časovém pásmu, protože objekt je oddělený od časového pásma v okamžiku vytvoření instance. V mnoha případech ale aplikace potřebuje jednoduše převést datum a čas na základě dvou různých posunů oproti času UTC, nikoli podle času v konkrétních časových pásmech. Chcete-li provést tento převod, můžete volat metodu ToOffset aktuální instance. Jediný parametr metody je posun hodnoty nového data a času, kterou metoda vrátí.

Pokud je například známo datum a čas požadavku uživatele na webovou stránku a je serializován jako řetězec ve formátu MM/dd/rrrr hh:mm:ss zzzz, následující ReturnTimeOnServer metoda převede tuto hodnotu data a času na datum a čas na webovém serveru:

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

Pokud metoda předá řetězec "9/1/2007 5:32:07 -05:00", který představuje datum a čas v časovém pásmu pět hodin dříve než UTC, vrátí "9/1/2007 3:32:07 AM -07:00" pro server umístěný v americkém standardním časovém pásmu.

Třída TimeZoneInfo také obsahuje přetížení TimeZoneInfo.ConvertTime(DateTimeOffset, TimeZoneInfo) metody, která provádí převody časových pásem s ToOffset(TimeSpan) hodnotami. Parametry metody jsou DateTimeOffset hodnota a odkaz na časové pásmo, na které se má čas převést. Volání metody vrátí DateTimeOffset hodnotu. Například metodu ReturnTimeOnServer v předchozím příkladu lze přepsat následujícím způsobem, aby volala metodu 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

Viz také