Sdílet prostřednictvím


Ukládání a obnovování časových pásem

Třída TimeZoneInfo spoléhá na registr k načtení předdefinovaných dat časového pásma. Registr je však dynamická struktura. Kromě toho informace o časovém pásmu, které registr obsahuje, používají operační systém především ke zpracování časových úprav a převodů pro aktuální rok. To má dva hlavní důsledky pro aplikace, které spoléhají na přesná data časového pásma:

  • Časové pásmo, které vyžaduje aplikace, nemusí být definováno v registru nebo bylo přejmenováno nebo odebráno z registru.

  • Časové pásmo definované v registru může obsahovat informace o konkrétních pravidlech úpravy, která jsou nezbytná pro převody historických časových pásem.

Třída TimeZoneInfo řeší tato omezení prostřednictvím své podpory serializace (ukládání) a deserializace (obnovení) dat časového pásma.

Serializace časových pásem a deserializace

Uložení a obnovení časového pásma serializací a deserializací dat časového pásma zahrnuje pouze dvě volání metod:

Scénáře serializace a deserializace

Schopnost uložit (nebo serializovat) TimeZoneInfo objekt do řetězce a obnovit (nebo deserializovat) pro pozdější použití zvyšuje nástroj i flexibilitu TimeZoneInfo třídy. Tato část popisuje některé situace, kdy je nejužitečnější serializace a deserializace.

Serializace a deserializace dat časového pásma v aplikaci

Serializované časové pásmo lze v případě potřeby obnovit z řetězce. Aplikace to může provést, pokud časové pásmo načtené z registru nemůže správně převést datum a čas v určitém rozsahu kalendářních dat. Například data časového pásma v registru systému Windows XP podporují jedno pravidlo úpravy, zatímco časová pásma definovaná v registru Windows Vista obvykle poskytují informace o dvou pravidlech úprav. To znamená, že převody historických časů můžou být nepřesné. Toto omezení může zvládnout serializace a deserializace dat časových pásem.

V následujícím příkladu je definována vlastní TimeZoneInfo třída, která neobsahuje žádná pravidla úpravy, která představuje východní standardní časové pásmo USA od 1883 do roku 1917 před zavedením letního času v USA. Vlastní časové pásmo je serializováno v proměnné, která má globální obor. Metoda převodu časového pásma , ConvertUtcTimeje předána koordinované univerzální čas (UTC) časy pro převod. Pokud datum a čas nastane v roce 1917 nebo starší, vlastní standardní časové pásmo se obnoví ze serializovaného řetězce a nahradí časové pásmo načtené z registru.

using System;

public class TimeZoneSerialization
{
   static string serializedEst;

   public static void Main()
   {
      // Retrieve Eastern Standard Time zone from registry
      try
      {
         TimeZoneSerialization tzs = new TimeZoneSerialization();
         TimeZoneInfo est = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
         // Create custom Eastern Time Zone for historical (pre-1918) conversions
         CreateTimeZone();
         // Call conversion function with one current and one pre-1918 date and time
         Console.WriteLine(ConvertUtcTime(DateTime.UtcNow, est));
         Console.WriteLine(ConvertUtcTime(new DateTime(1900, 11, 15, 9, 32, 00, DateTimeKind.Utc), est));
      }
      catch (TimeZoneNotFoundException)
      {
         Console.WriteLine("The Eastern Standard Time zone is not in the registry.");
      }
      catch (InvalidTimeZoneException)
      {
         Console.WriteLine("Data on the Eastern Standard Time Zone in the registry is corrupted.");
      }
   }

   private static void CreateTimeZone()
   {
      // Create a simple Eastern Standard time zone
      // without adjustment rules for 1883-1918
      TimeZoneInfo earlyEstZone = TimeZoneInfo.CreateCustomTimeZone("Eastern Standard Time",
                                      new TimeSpan(-5, 0, 0),
                                      " (GMT-05:00) Eastern Time (United States)",
                                      "Eastern Standard Time");
      serializedEst = earlyEstZone.ToSerializedString();
   }

   private static DateTime ConvertUtcTime(DateTime utcDate, TimeZoneInfo tz)
   {
      // Use time zone object from registry
      if (utcDate.Year > 1917)
      {
         return TimeZoneInfo.ConvertTimeFromUtc(utcDate, tz);
      }
      // Handle dates before introduction of DST
      else
      {
         // Restore serialized time zone object
         tz = TimeZoneInfo.FromSerializedString(serializedEst);
         return TimeZoneInfo.ConvertTimeFromUtc(utcDate, tz);
      }
   }
}
Module TimeZoneSerialization
    Dim serializedEst As String

    Public Sub Main()
        ' Retrieve Eastern Standard Time zone from registry
        Try
            Dim est As TimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time")
            ' Create custom Eastern Time Zone for historical (pre-1918) conversions
            CreateTimeZone()
            ' Call conversion function with one current and one pre-1918 date and time
            Console.WriteLine(ConvertUtcTime(Date.UtcNow, est))
            Console.WriteLine(ConvertUtcTime(New Date(1900, 11, 15, 9, 32, 00, DateTimeKind.Utc), est))
        Catch e As TimeZoneNotFoundException
            Console.WriteLine("The Eastern Standard Time zone is not in the registry.")
        Catch e As InvalidTimeZoneException
            Console.WriteLine("Data on the Eastern Standard Time Zone in the registry is corrupted.")
        End Try
    End Sub

    Private Sub CreateTimeZone()
        ' Create a simple Eastern Standard time zone 
        ' without adjustment rules for 1883-1918
        Dim earlyEstZone As TimeZoneInfo = TimeZoneInfo.CreateCustomTimeZone("Eastern Standard Time", _
                                        New TimeSpan(-5, 0, 0), _
                                        " (GMT-05:00) Eastern Time (United States)", _
                                        "Eastern Standard Time")
        serializedEst = earlyEstZone.ToSerializedString()
    End Sub

    Private Function ConvertUtcTime(utcDate As Date, tz As TimeZoneInfo) As Date
        ' Use time zone object from registry 
        If Year(utcDate) > 1917 Then
            Return TimeZoneInfo.ConvertTimeFromUtc(utcDate, tz)
            ' Handle dates before introduction of DST
        Else
            ' Restore serialized time zone object
            tz = TimeZoneInfo.FromSerializedString(serializedEst)
            Return TimeZoneInfo.ConvertTimeFromUtc(utcDate, tz)
        End If
    End Function
End Module

Zpracování výjimek časových pásem

Vzhledem k tomu, že registr je dynamická struktura, podléhá její obsah náhodné nebo úmyslné úpravě. To znamená, že časové pásmo, které by mělo být definováno v registru a které je nutné pro úspěšné spuštění aplikace, nemusí chybět. Bez podpory serializace a deserializace časových pásem máte malou volbu, ale zpracovat výsledek TimeZoneNotFoundException ukončením aplikace. Pomocí serializace a deserializace časových pásem však můžete zpracovat neočekávané TimeZoneNotFoundException obnovením požadovaného časového pásma ze serializovaného řetězce a aplikace bude nadále spuštěna.

Následující příklad vytvoří a serializuje vlastní centrální standardní časové pásmo. Pak se pokusí načíst centrální standardní časové pásmo z registru. Pokud operace načítání vyvolá buď a TimeZoneNotFoundException nebo , InvalidTimeZoneExceptionobslužná rutina výjimky deserializuje časové pásmo.

using System;
using System.Collections.Generic;

public class TimeZoneApplication
{
   // Define collection of custom time zones
   private Dictionary<string, string> customTimeZones = new Dictionary<string, string>();
   private TimeZoneInfo cst;

   public TimeZoneApplication()
   {
      // Create custom Central Standard Time
      //
      // Declare necessary TimeZoneInfo.AdjustmentRule objects for time zone
      TimeZoneInfo customTimeZone;
      TimeSpan delta = new TimeSpan(1, 0, 0);
      TimeZoneInfo.AdjustmentRule adjustment;
      List<TimeZoneInfo.AdjustmentRule> adjustmentList = new List<TimeZoneInfo.AdjustmentRule>();
      // Declare transition time variables to hold transition time information
      TimeZoneInfo.TransitionTime transitionRuleStart, transitionRuleEnd;

      // Define end rule (for 1976-2006)
      transitionRuleEnd = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 2, 0, 0), 10, 5, DayOfWeek.Sunday);
      // Define rule (1976-1986)
      transitionRuleStart = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 2, 0, 0), 04, 05, DayOfWeek.Sunday);
      adjustment = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(new DateTime(1976, 1, 1), new DateTime(1986, 12, 31), delta, transitionRuleStart, transitionRuleEnd);
      adjustmentList.Add(adjustment);
      // Define rule (1987-2006)
      transitionRuleStart = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 2, 0, 0), 04, 01, DayOfWeek.Sunday);
      adjustment = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(new DateTime(1987, 1, 1), new DateTime(2006, 12, 31), delta, transitionRuleStart, transitionRuleEnd);
      adjustmentList.Add(adjustment);
      // Define rule (2007- )
      transitionRuleStart = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 2, 0, 0), 03, 02, DayOfWeek.Sunday);
      transitionRuleEnd = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 2, 0, 0), 11, 01, DayOfWeek.Sunday);
      adjustment = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(new DateTime(2007, 01, 01), DateTime.MaxValue.Date, delta, transitionRuleStart, transitionRuleEnd);
      adjustmentList.Add(adjustment);

      // Create custom U.S. Central Standard Time zone
      customTimeZone = TimeZoneInfo.CreateCustomTimeZone("Central Standard Time",
                      new TimeSpan(-6, 0, 0),
                      "(GMT-06:00) Central Time (US Only)", "Central Standard Time",
                      "Central Daylight Time", adjustmentList.ToArray());
      // Add time zone to collection
      customTimeZones.Add(customTimeZone.Id, customTimeZone.ToSerializedString());

      // Create any other required time zones
   }

   public static void Main()
   {
      TimeZoneApplication tza = new TimeZoneApplication();
      tza.AppEntryPoint();
   }

   private void AppEntryPoint()
   {
      try
      {
         cst = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
      }
      catch (TimeZoneNotFoundException)
      {
         if (customTimeZones.ContainsKey("Central Standard Time"))
            HandleTimeZoneException("Central Standard Time");
      }
      catch (InvalidTimeZoneException)
      {
         if (customTimeZones.ContainsKey("Central Standard Time"))
            HandleTimeZoneException("Central Standard Time");
      }
      if (cst == null)
      {
         Console.WriteLine("Unable to load Central Standard Time zone.");
         return;
      }
      DateTime currentTime = DateTime.Now;
      Console.WriteLine("The current {0} time is {1}.",
                        TimeZoneInfo.Local.IsDaylightSavingTime(currentTime) ?
                            TimeZoneInfo.Local.StandardName :
                            TimeZoneInfo.Local.DaylightName,
                        currentTime.ToString("f"));
      Console.WriteLine("The current {0} time is {1}.",
                        cst.IsDaylightSavingTime(currentTime) ?
                            cst.StandardName :
                            cst.DaylightName,
                        TimeZoneInfo.ConvertTime(currentTime, TimeZoneInfo.Local, cst).ToString("f"));
   }

   private void HandleTimeZoneException(string timeZoneName)
   {
      string tzString = customTimeZones[timeZoneName];
      cst = TimeZoneInfo.FromSerializedString(tzString);
   }
}
Imports System.Collections.Generic

Public Class TimeZoneApplication
    ' Define collection of custom time zones
    Private customTimeZones As New Dictionary(Of String, String)
    Private cst As TimeZoneInfo

    Public Sub New()
        ' Define custom Central Standard Time
        '
        ' Declare necessary TimeZoneInfo.AdjustmentRule objects for time zone
        Dim customTimeZone As TimeZoneInfo
        Dim delta As New TimeSpan(1, 0, 0)
        Dim adjustment As TimeZoneInfo.AdjustmentRule
        Dim adjustmentList As New List(Of TimeZoneInfo.AdjustmentRule)
        ' Declare transition time variables to hold transition time information
        Dim transitionRuleStart, transitionRuleEnd As TimeZoneInfo.TransitionTime

        ' Define end rule (for 1976-2006)
        transitionRuleEnd = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(#02:00:00AM#, 10, 5, DayOfWeek.Sunday)
        ' Define rule (1976-1986)
        transitionRuleStart = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(#2:00:00AM#, 04, 05, DayOfWeek.Sunday)
        adjustment = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(#01/01/1976#, #12/31/1986#, delta, transitionRuleStart, transitionRuleEnd)
        adjustmentList.Add(adjustment)
        ' Define rule (1987-2006)  
        transitionRuleStart = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(#2:00:00AM#, 04, 01, DayOfWeek.Sunday)
        adjustment = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(#01/01/1987#, #12/31/2006#, delta, transitionRuleStart, transitionRuleEnd)
        adjustmentList.Add(adjustment)
        ' Define rule (2007- )  
        transitionRuleStart = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(#2:00:00AM#, 03, 02, DayOfWeek.Sunday)
        transitionRuleEnd = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(#2:00:00AM#, 11, 01, DayOfWeek.Sunday)
        adjustment = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(#01/01/2007#, Date.MaxValue.Date, delta, transitionRuleStart, transitionRuleEnd)
        adjustmentList.Add(adjustment)
        ' Create custom time zone
        customTimeZone = TimeZoneInfo.CreateCustomTimeZone("Central Standard Time", _
                        New TimeSpan(-6, 0, 0), _
                        "(GMT-06:00) Central Time (US Only)", "Central Standard Time", _
                        "Central Daylight Time", adjustmentList.ToArray())
        ' Add time zone to collection
        customTimeZones.Add(customTimeZone.Id, customTimeZone.ToSerializedString())

        ' Create any other required time zones     
    End Sub

    Public Shared Sub Main()
        Dim tza As New TimeZoneApplication()
        tza.AppEntryPoint()
    End Sub

    Private Sub AppEntryPoint()
        Try
            cst = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time")
        Catch e As TimeZoneNotFoundException
            If customTimeZones.ContainsKey("Central Standard Time")
                HandleTimeZoneException("Central Standard Time")
            End If
        Catch e As InvalidTimeZoneException
            If customTimeZones.ContainsKey("Central Standard Time")
                HandleTimeZoneException("Central Standard Time")
            End If
        End Try
        If cst Is Nothing Then
            Console.WriteLine("Unable to load Central Standard Time zone.")
            Return
        End If
        Dim currentTime As Date = Date.Now
        Console.WriteLine("The current {0} time is {1}.", _
                          IIf(TimeZoneInfo.Local.IsDaylightSavingTime(currentTime), _
                              TimeZoneInfo.Local.StandardName, _
                              TimeZoneInfo.Local.DaylightName), _
                          currentTime.ToString("f"))
        Console.WriteLine("The current {0} time is {1}.", _
                          IIf(cst.IsDaylightSavingTime(currentTime), _
                              cst.StandardName, _
                              cst.DaylightName), _
                          TimeZoneInfo.ConvertTime(currentTime, TimeZoneInfo.Local, cst).ToString("f"))
    End Sub

    Private Sub HandleTimeZoneException(timeZoneName As String)
        Dim tzString As String = customTimeZones.Item(timeZoneName)
        cst = TimeZoneInfo.FromSerializedString(tzString)
    End Sub
End Class

Uložení serializovaného řetězce a jeho obnovení v případě potřeby

Předchozí příklady uložily informace o časovém pásmu do proměnné řetězce a v případě potřeby je obnovily. Řetězec, který obsahuje serializované informace o časovém pásmu, se však může uložit do některého média úložiště, jako je externí soubor, soubor prostředku vložený do aplikace nebo registr. (Všimněte si, že informace o vlastních časových pásmech by se měly ukládat kromě klíčů časového pásma systému v registru.)

Uložení serializovaného řetězce časového pásma tímto způsobem také odděluje rutinu vytváření časového pásma od samotné aplikace. Například rutina vytváření časového pásma může spustit a vytvořit datový soubor, který obsahuje historické informace o časovém pásmu, které může aplikace používat. Datový soubor lze pak nainstalovat s aplikací a dá se otevřít a jedno nebo více časových pásem může být deserializováno, když je aplikace vyžaduje.

Příklad, který používá vložený prostředek k ukládání serializovaných dat časového pásma, viz Postupy: Úspora časových pásem na vložený prostředek a Postupy: Obnovení časových pásem z vloženého prostředku.

Viz také