Salvataggio e ripristino dei fusi orari

La classe TimeZoneInfo si basa sul Registro di sistema per recuperare i dati predefiniti dei fusi orari. Tuttavia, il Registro di sistema è una struttura dinamica. Inoltre, le informazioni sul fuso orario contenute nel Registro di sistema vengono usate principalmente dal sistema operativo per gestire le modifiche e le conversioni temporali per l'anno corrente. Ciò ha due implicazioni principali per le applicazioni che si basano su dati accurati del fuso orario:

  • Un fuso orario richiesto da un'applicazione potrebbe non essere definito nel Registro di sistema o potrebbe essere stato rinominato o rimosso.

  • Un fuso orario definito nel Registro di sistema potrebbe non avere informazioni sulle regole di rettifica specifiche necessarie per le conversioni del fuso orario cronologico.

La classe TimeZoneInfo risolve queste limitazioni tramite il supporto per la serializzazione (salvataggio) e la deserializzazione (ripristino) dei dati del fuso orario.

Serializzazione e deserializzazione dei fusi orari

Il salvataggio e il ripristino di un fuso orario tramite la serializzazione e la deserializzazione dei dati del fuso orario comportano solo due chiamate di metodo:

  • È possibile serializzare un oggetto TimeZoneInfo chiamando il metodo ToSerializedString dell'oggetto. Il metodo non accetta parametri e restituisce una stringa contenente informazioni sul fuso orario.

  • È possibile deserializzare un oggetto TimeZoneInfo da una stringa serializzata passando tale stringa al metodo static (Shared in Visual Basic) TimeZoneInfo.FromSerializedString.

Scenari di serializzazione e deserializzazione

La possibilità di salvare (o serializzare) un oggetto TimeZoneInfo in una stringa e di ripristinarlo (o deserializzarlo) per un uso successivo aumenta sia l'utilità che la flessibilità della classe TimeZoneInfo. In questa sezione vengono esaminate alcune delle situazioni in cui la serializzazione e la deserializzazione sono più utili.

Serializzazione e deserializzazione dei dati del fuso orario in un'applicazione

Un fuso orario serializzato può essere ripristinato da una stringa quando necessario. Un'applicazione potrebbe eseguire questa operazione se il fuso orario recuperato dal Registro di sistema non è in grado di convertire correttamente una data e un'ora all'interno di un determinato intervallo di date. Ad esempio, i dati del fuso orario nel Registro di sistema di Windows XP supportano una singola regola di rettifica, mentre i fusi orari definiti nel Registro di sistema di Windows Vista in genere forniscono informazioni su due regole di rettifica. Ciò significa che le conversioni temporali cronologiche potrebbero non essere accurate. La serializzazione e la deserializzazione dei dati del fuso orario possono gestire questa limitazione.

Nell'esempio seguente, una classe TimeZoneInfo personalizzata senza regole di modifica viene definita per rappresentare il fuso orario standard degli Stati Uniti orientali dal 1883 al 1917, prima dell'introduzione dell'ora legale negli Stati Uniti. Il fuso orario personalizzato viene serializzato in una variabile con ambito globale. Il metodo di conversione del fuso orario, ConvertUtcTime, viene passato all'ora UTC (Coordinated Universal Time) per la conversione. Se la data e l'ora risalgono al 1917 o a prima ancora, l'Ora solare fuso orientale personalizzata viene ripristinata da una stringa serializzata e sostituisce il fuso orario recuperato dal Registro di sistema.

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

Gestione delle eccezioni del fuso orario

Poiché il Registro di sistema è una struttura dinamica, il relativo contenuto è soggetto a modifiche accidentali o intenzionali. Ciò significa che un fuso orario che deve essere definito nel Registro di sistema e che è necessario per l'esecuzione corretta di un'applicazione potrebbe non essere presente. Senza il supporto per la serializzazione e la deserializzazione del fuso orario, è possibile scegliere se gestire l'oggetto TimeZoneNotFoundException risultante terminando l'applicazione. Tuttavia, usando la serializzazione del fuso orario e la deserializzazione, è possibile gestire un'operazione TimeZoneNotFoundException imprevista ripristinando il fuso orario richiesto da una stringa serializzata: l'applicazione continuerà a essere eseguita.

Nell'esempio seguente viene creato e serializzato un fuso orario centrale personalizzato e tenta quindi di recuperare il fuso orario standard centrale dal Registro di sistema. Se l'operazione di recupero genera TimeZoneNotFoundException o InvalidTimeZoneException, il gestore di eccezioni deserializza il fuso orario.

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

Archiviazione di una stringa serializzata e ripristino quando necessario

Negli esempi precedenti sono state archiviate informazioni sul fuso orario in una variabile stringa e ripristinate quando necessario. Tuttavia, la stringa che contiene informazioni sul fuso orario serializzato può essere archiviata in un supporto di archiviazione, ad esempio un file esterno, un file di risorse incorporato nell'applicazione o il Registro di sistema. Si noti che le informazioni sui fusi orari personalizzati devono essere archiviate separatamente dalle chiavi del fuso orario del sistema nel Registro di sistema.

L'archiviazione di una stringa di fuso orario serializzato in questo modo separa anche la routine di creazione del fuso orario dall'applicazione stessa. Ad esempio, una routine di creazione del fuso orario può essere eseguita per creare un file di dati che contiene informazioni cronologiche sul fuso orario che un'applicazione potrà poi usare. Il file di dati può quindi essere installato con l'applicazione ed essere aperto. Uno o più fusi orari potranno così essere deserializzati quando l'applicazione li richiede.

Per un esempio che usa una risorsa incorporata per archiviare i dati del fuso orario serializzati, vedere Procedura: Salvare fusi orari in una risorsa incorporata e Procedura: Ripristinare fusi orari da una risorsa incorporata.

Vedi anche