Zapisywanie i przywracanie stref czasowych

Klasa TimeZoneInfo opiera się na rejestrze w celu pobrania wstępnie zdefiniowanych danych strefy czasowej. Rejestr jest jednak dynamiczną strukturą. Ponadto informacje o strefie czasowej, które zawiera rejestr, są używane przez system operacyjny przede wszystkim do obsługi korekt czasu i konwersji w bieżącym roku. Ma to dwa główne konsekwencje dla aplikacji, które opierają się na dokładnych danych strefy czasowej:

  • Strefa czasowa wymagana przez aplikację może nie być zdefiniowana w rejestrze lub mogła zostać zmieniona lub usunięta z rejestru.

  • Strefa czasowa zdefiniowana w rejestrze może nie zawierać informacji o określonych regułach korekty, które są niezbędne do historycznych konwersji strefy czasowej.

Klasa TimeZoneInfo rozwiązuje te ograniczenia dzięki obsłudze serializacji (zapisywania) i deserializacji (przywracania) danych strefy czasowej.

Serializacja i deserializacja strefy czasowej

Zapisywanie i przywracanie strefy czasowej przez serializowanie i deserializacji danych strefy czasowej obejmuje tylko dwa wywołania metody:

Scenariusze serializacji i deserializacji

Możliwość zapisywania (lub serializowania) TimeZoneInfo obiektu w ciągu i przywracania (lub deserializowania) do późniejszego użycia zwiększa zarówno narzędzie, jak i elastyczność TimeZoneInfo klasy. W tej sekcji opisano niektóre sytuacje, w których najbardziej przydatna jest serializacja i deserializacja.

Serializowanie i deserializacji danych strefy czasowej w aplikacji

W razie potrzeby można przywrócić serializowaną strefę czasową z ciągu. Aplikacja może to zrobić, jeśli strefa czasowa pobrana z rejestru nie może poprawnie przekonwertować daty i godziny w określonym zakresie dat. Na przykład dane strefy czasowej w rejestrze systemu Windows XP obsługują pojedynczą regułę korekty, podczas gdy strefy czasowe zdefiniowane w rejestrze systemu Windows Vista zwykle zawierają informacje o dwóch regułach korekty. Oznacza to, że historyczne konwersje czasu mogą być niedokładne. Serializacja i deserializacja danych strefy czasowej może obsłużyć to ograniczenie.

W poniższym przykładzie klasa niestandardowaTimeZoneInfo, która nie ma reguł korekty, jest zdefiniowana tak, aby reprezentować wschodnią strefę czasową (wschodnią) od 1883 do 1917, przed wprowadzeniem czasu letniego w Stany Zjednoczone. Niestandardowa strefa czasowa jest serializowana w zmiennej, która ma zakres globalny. Metoda konwersji strefy czasowej , ConvertUtcTimejest przekazywana czas uniwersalny koordynowany (UTC) do konwersji. Jeśli data i godzina przypada w 1917 roku lub starszej, niestandardowa strefa czasowa Wschodnia (standardowy) zostanie przywrócona z serializacji ciągu i zastąpi strefę czasową pobraną z rejestru.

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

Obsługa wyjątków strefy czasowej

Ponieważ rejestr jest strukturą dynamiczną, jego zawartość podlega przypadkowej lub celowej modyfikacji. Oznacza to, że strefa czasowa, która powinna być zdefiniowana w rejestrze i która jest wymagana do pomyślnego wykonania aplikacji, może być nieobecna. Bez obsługi serializacji i deserializacji strefy czasowej nie masz możliwości obsługi wynikowej TimeZoneNotFoundException przez zakończenie aplikacji. Jednak przy użyciu serializacji i deserializacji strefy czasowej można obsłużyć nieoczekiwane TimeZoneNotFoundException działanie, przywracając wymaganą strefę czasową z serializacji ciągu, a aplikacja będzie nadal działać.

Poniższy przykład tworzy i serializuje niestandardową centralną strefę czasową w warstwie Standardowa. Następnie próbuje pobrać centralną strefę czasową standardową z rejestru. Jeśli operacja pobierania zgłasza wyjątek lub InvalidTimeZoneException, program obsługi wyjątków TimeZoneNotFoundException deserializuje strefę czasową.

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

Przechowywanie serializowanego ciągu i przywracanie go w razie potrzeby

Poprzednie przykłady przechowywały informacje o strefie czasowej do zmiennej ciągu i przywracały je w razie potrzeby. Jednak ciąg zawierający serializowane informacje o strefie czasowej może być przechowywany na pewnym nośniku magazynu, takim jak plik zewnętrzny, plik zasobu osadzony w aplikacji lub rejestr. (Należy pamiętać, że informacje o niestandardowych strefach czasowych powinny być przechowywane poza kluczami strefy czasowej systemu w rejestrze).

Przechowywanie serializowanego ciągu strefy czasowej w ten sposób oddziela również procedurę tworzenia strefy czasowej od samej aplikacji. Na przykład procedurę tworzenia strefy czasowej można wykonać i utworzyć plik danych zawierający historyczne informacje o strefie czasowej, których może używać aplikacja. Plik danych można następnie zainstalować z aplikacją i można go otworzyć, a co najmniej jedna ze stref czasowych może zostać zdeserializowana, gdy aplikacja ich wymaga.

Przykład użycia osadzonego zasobu do przechowywania serializowanych danych strefy czasowej można znaleźć w temacie How to: Save time zones to an embedded resource (Jak zapisywać strefy czasowe w zasobie osadzonym) i How to: Restore time zones from an embedded resource (Jak zapisywać strefy czasowe w osadzonym zasobie).

Zobacz też