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
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.
Llame al método ToSerializedString para crear una cadena que contenga los datos de la zona horaria.
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.
Cree una instancia de un objeto ResXResourceWriter; para ello, pase el objeto StreamWriter al constructor de clase ResXResourceWriter.
Pase la cadena serializada de la zona horaria al método ResXResourceWriter.AddResource.
Llame al método ResXResourceWriter.Generate.
Llame al método ResXResourceWriter.Close.
Cierre el objeto StreamWriter; para ello, llame a su método Close.
Agregue el archivo .resx generado al proyecto de Visual Studio de la aplicación.
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