Procedimiento para guardar zonas horarias en un recurso incrustado

Una aplicación sensible a la zona horaria suele requerir la presencia de una zona horaria determinada. Pero, dado que la disponibilidad de objetos individuales TimeZoneInfo depende de la información almacenada en el registro del sistema local, incluso las zonas horarias disponibles habitualmente pueden estar ausentes. Además, la información sobre las zonas horarias personalizadas cuyas instancias se han creado mediante el método CreateCustomTimeZone no se almacena con otra información de zona horaria en el registro. Para asegurarse de que estas zonas horarias estén disponibles cuando se necesiten, puede serializarlas para guardarlas y, luego, deserializarlas para restaurarlas.

Normalmente, la serialización de un objeto TimeZoneInfo se produce fuera de la aplicación sensible a la zona horaria. En función del almacén de datos usado para contener objetos TimeZoneInfo serializados, los datos de zona horaria se pueden serializar como parte de una rutina de instalación o configuración (por ejemplo, cuando los datos se almacenan en una clave de aplicación del registro) o como parte de una rutina de utilidad que se ejecuta antes de que se compile la aplicación final (por ejemplo, cuando los datos serializados se almacenan en un archivo de recursos XML (.resx) de .NET).

Además de un archivo de recursos que se compila con la aplicación, se pueden usar muchos otros almacenes de datos para la información de zona horaria. que incluyen la siguiente información:

  • El registro. Tenga en cuenta que una aplicación debe usar las subclaves de su propia clave de aplicación para almacenar datos de zona horaria personalizados en lugar de usar las subclaves de HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones.

  • Archivos de configuración.

  • Otros archivos del sistema.

Para guardar una zona horaria, serialícela en un archivo .resx

  1. Recupere una zona horaria existente o cree una nueva.

    Para recuperar una zona horaria existente, vea Procedimiento para obtener acceso a los objetos de zona horaria local y UTC predefinidos y Procedimiento para crear una instancia de un objeto TimeZoneInfo.

    Para crear una nueva zona horaria, llame a una de las sobrecargas del método CreateCustomTimeZone. Para obtener más información, vea Procedimiento para crear zonas horarias sin reglas de ajuste y Procedimiento para crear zonas horarias con reglas de ajuste.

  2. Llame al método ToSerializedString para crear una cadena que contenga los datos de la zona horaria.

  3. Cree una instancia de un objeto StreamWriter; para ello, proporcione el nombre y, opcionalmente, la ruta de acceso del archivo .resx al constructor de clase StreamWriter.

  4. Cree una instancia de un objeto ResXResourceWriter; para ello, pase el objeto StreamWriter al constructor de clase ResXResourceWriter.

  5. Pase la cadena serializada de la zona horaria al método ResXResourceWriter.AddResource.

  6. Llame al método ResXResourceWriter.Generate.

  7. Llame al método ResXResourceWriter.Close.

  8. Cierre el objeto StreamWriter; para ello, llame a su método Close.

  9. Agregue el archivo .resx generado al proyecto de Visual Studio de la aplicación.

  10. Con la ventana Propiedades de Visual Studio, asegúrese de que la propiedad Acción de compilación del archivo .resx está establecida en Recurso incrustado.

Ejemplo

En el ejemplo siguiente se serializa un objeto TimeZoneInfo que representa la hora estándar central y un objeto TimeZoneInfo que representa la hora de la Base Palmer, Antártida, en un archivo de recursos XML de .NET de nombre SerializedTimeZones.resx. La hora estándar central se define normalmente en el registro; Base Palmer, Antártida es una zona horaria personalizada.

TimeZoneSerialization()
{
   TextWriter writeStream;
   Dictionary<string, string> resources = new Dictionary<string, string>();
   // Determine if .resx file exists
   if (File.Exists(resxName))
   {
      // Open reader
      TextReader readStream = new StreamReader(resxName);
      ResXResourceReader resReader = new ResXResourceReader(readStream);
      foreach (DictionaryEntry item in resReader)
      {
         if (! (((string) item.Key) == "CentralStandardTime" ||
                ((string) item.Key) == "PalmerStandardTime" ))
            resources.Add((string)item.Key, (string) item.Value);
      }
      readStream.Close();
      // Delete file, since write method creates duplicate xml headers
      File.Delete(resxName);
   }

   // Open stream to write to .resx file
   try
   {
      writeStream = new StreamWriter(resxName, true);
   }
   catch (FileNotFoundException e)
   {
      // Handle failure to find file
      Console.WriteLine("{0}: The file {1} could not be found.", e.GetType().Name, resxName);
      return;
   }

   // Get resource writer
   ResXResourceWriter resWriter = new ResXResourceWriter(writeStream);

   // Add resources from existing file
   foreach (KeyValuePair<string, string> item in resources)
   {
      resWriter.AddResource(item.Key, item.Value);
   }

   // Serialize Central Standard Time
   try
   {
      TimeZoneInfo cst = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
      resWriter.AddResource(cst.Id.Replace(" ", string.Empty), cst.ToSerializedString());
   }
   catch (TimeZoneNotFoundException)
   {
      Console.WriteLine("The Central Standard Time zone could not be found.");
   }

   // Create time zone for Palmer, Antarctica
   //
   // Define transition times to/from DST
   TimeZoneInfo.TransitionTime startTransition = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 4, 0, 0),
                                                                                              10, 2, DayOfWeek.Sunday);
   TimeZoneInfo.TransitionTime endTransition = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 3, 0, 0),
                                                                                            3, 2, DayOfWeek.Sunday);
   // Define adjustment rule
   TimeSpan delta = new TimeSpan(1, 0, 0);
   TimeZoneInfo.AdjustmentRule adjustment = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(new DateTime(1999, 10, 1),
                                         DateTime.MaxValue.Date, delta, startTransition, endTransition);
   // Create array for adjustment rules
   TimeZoneInfo.AdjustmentRule[] adjustments = {adjustment};
   // Define other custom time zone arguments
   string DisplayName = "(GMT-04:00) Antarctica/Palmer Time";
   string standardName = "Palmer Standard Time";
   string daylightName = "Palmer Daylight Time";
   TimeSpan offset = new TimeSpan(-4, 0, 0);
   TimeZoneInfo palmer = TimeZoneInfo.CreateCustomTimeZone(standardName, offset, DisplayName, standardName, daylightName, adjustments);
   resWriter.AddResource(palmer.Id.Replace(" ", String.Empty), palmer.ToSerializedString());

   // Save changes to .resx file
   resWriter.Generate();
   resWriter.Close();
   writeStream.Close();
}
Private Sub SerializeTimeZones()
    Dim writeStream As TextWriter
    Dim resources As New Dictionary(Of String, String)
    ' Determine if .resx file exists
    If File.Exists(resxName) Then
        ' Open reader
        Dim readStream As TextReader = New StreamReader(resxName)
        Dim resReader As New ResXResourceReader(readStream)
        For Each item As DictionaryEntry In resReader
            If Not (CStr(item.Key) = "CentralStandardTime" Or _
                    CStr(item.Key) = "PalmerStandardTime") Then
                resources.Add(CStr(item.Key), CStr(item.Value))
            End If
        Next
        readStream.Close()
        ' Delete file, since write method creates duplicate xml headers
        File.Delete(resxName)
    End If

    ' Open stream to write to .resx file
    Try
        writeStream = New StreamWriter(resxName, True)
    Catch e As FileNotFoundException
        ' Handle failure to find file
        Console.WriteLine("{0}: The file {1} could not be found.", e.GetType().Name, resxName)
        Exit Sub
    End Try

    ' Get resource writer
    Dim resWriter As ResXResourceWriter = New ResXResourceWriter(writeStream)

    ' Add resources from existing file
    For Each item As KeyValuePair(Of String, String) In resources
        resWriter.AddResource(item.Key, item.Value)
    Next
    ' Serialize Central Standard Time
    Try
        Dim cst As TimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time")
        resWriter.AddResource(cst.Id.Replace(" ", String.Empty), cst.ToSerializedString())
    Catch
        Console.WriteLine("The Central Standard Time zone could not be found.")
    End Try

    ' Create time zone for Palmer, Antarctica
    '
    ' Define transition times to/from DST
    Dim startTransition As TimeZoneInfo.TransitionTime = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(#4:00:00 AM#, 10, 2, DayOfWeek.Sunday)
    Dim endTransition As TimeZoneInfo.TransitionTime = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(#3:00:00 AM#, 3, 2, DayOfWeek.Sunday)
    ' Define adjustment rule
    Dim delta As TimeSpan = New TimeSpan(1, 0, 0)
    Dim adjustment As TimeZoneInfo.AdjustmentRule = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(#10/1/1999#, Date.MaxValue.Date, delta, startTransition, endTransition)
    ' Create array for adjustment rules
    Dim adjustments() As TimeZoneInfo.AdjustmentRule = {adjustment}
    ' Define other custom time zone arguments
    Dim DisplayName As String = "(GMT-04:00) Antarctica/Palmer Time"
    Dim standardName As String = "Palmer Standard Time"
    Dim daylightName As String = "Palmer Daylight Time"
    Dim offset As TimeSpan = New TimeSpan(-4, 0, 0)
    Dim palmer As TimeZoneInfo = TimeZoneInfo.CreateCustomTimeZone(standardName, offset, DisplayName, standardName, daylightName, adjustments)
    resWriter.AddResource(palmer.Id.Replace(" ", String.Empty), palmer.ToSerializedString())

    ' Save changes to .resx file 
    resWriter.Generate()
    resWriter.Close()
    writeStream.Close()
End Sub

En este ejemplo se serializan los objetos TimeZoneInfo para que estén disponibles en un archivo de recursos en tiempo de compilación.

Dado que el método ResXResourceWriter.Generate agrega información de encabezado completa a un archivo de recursos XML de .NET, no se puede usar para agregar recursos a un archivo existente. El ejemplo controla esto mediante la comprobación del archivo SerializedTimeZones.resx y, si existe, almacena todos sus recursos distintos de las dos zonas horarias serializadas en un objeto genérico Dictionary<TKey,TValue>. A continuación, se elimina el archivo existente y los recursos existentes se agregan a un nuevo archivo SerializedTimeZones.resx. Los datos de zona horaria serializados también se agregan a este archivo.

Los campos de clave (o Nombre) de los recursos no deben contener espacios insertados. Se llama al método Replace(String, String) para quitar todos los espacios insertados en los identificadores de zona horaria antes de asignarlos al archivo de recursos.

Compilación del código

Para este ejemplo se necesita:

  • Que se agregue una referencia a System.Windows.Forms.dll y System.Core.dll al proyecto.

  • Que se importen los siguientes espacios de nombres:

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Globalization;
    using System.IO;
    using System.Reflection;
    using System.Resources;
    using System.Windows.Forms;
    
    Imports System.Globalization
    Imports System.IO
    Imports System.Reflection
    Imports System.Resources
    

Consulte también