如何:将时区保存到嵌入的资源中

更新:2007 年 11 月

时区识别应用程序通常要求存在某个特定的时区。但是,由于各个 TimeZoneInfo 对象的可用性取决于本地系统的注册表中存储的信息,因此即使是通常可用的时区也可能不存在。此外,对于使用 CreateCustomTimeZone 方法实例化的自定义时区,其相关信息在注册表中并不与其他时区信息存储在一起。若要确保可在需要时获得这些时区,可以通过序列化来保存它们,以后再通过反序列化来还原它们。

通常,序列化 TimeZoneInfo 对象的过程在时区识别应用程序之外单独进行。根据用于保存序列化 TimeZoneInfo 对象的数据存储区,可以将时区数据序列化为设置或安装例程的一部分(例如,当数据存储在注册表的应用程序键中时),或将其序列化为在编译最终应用程序之前运行的实用工具例程的一部分(例如,当序列化数据存储在 .NET XML 资源 (.resx) 文件中时)。

除了与应用程序一起编译的资源文件外,还有其他一些数据存储区可用于保存时区信息。这些功能包括:

  • 注册表。请注意,应用程序应使用自己应用程序键的子项来存储自定义时区数据,而不应使用 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones 的子项。

  • 配置文件。

  • 其他系统文件。

通过将时区序列化到 .resx 文件中来保存时区

  1. 检索一个现有的时区或创建一个新时区。

    若要检索现有的时区,请参见如何:访问预定义的 UTC 和本地时区对象如何:实例化 TimeZoneInfo 对象

    若要创建新时区,请调用 CreateCustomTimeZone 方法的重载之一。有关更多信息,请参见如何:创建不带调整规则的时区如何:创建带有调整规则的时区

  2. 调用 ToSerializedString 方法,以创建包含时区数据的字符串。

  3. 通过向 StreamWriter 类构造函数提供 .resx 文件的名称和路径(可选)来实例化 StreamWriter 对象。

  4. 通过将 StreamWriter 对象传递给 ResXResourceWriter 类构造函数来实例化 ResXResourceWriter 对象。

  5. 将时区的序列化字符串传递给 ResXResourceWriter.AddResource 方法。

  6. 调用 ResXResourceWriter.Generate 方法。

  7. 调用 ResXResourceWriter.Close 方法。

  8. 通过调用 StreamWriter 对象的 Close 方法来关闭该对象。

  9. 将生成的 .resx 文件添加到应用程序的 Visual Studio 项目中。

  10. 使用 Visual Studio 中的“属性”窗口,确保 .resx 文件的“生成操作”属性已设置为“嵌入的资源”。

示例

下面的示例将一个表示中部标准时间的 TimeZoneInfo 对象以及一个表示南极帕默站时间的 TimeZoneInfo 对象序列化到一个名为 SerializedTimeZones.resx 的 .NET XML 资源文件中。中部标准时间通常在注册表中定义,而南极帕默站则是一个自定义时区。

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
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();
}

此示例序列化了 TimeZoneInfo 对象,这样便可在编译时从资源文件中获得它们。

由于 ResXResourceWriter.Generate 方法向 .NET XML 资源文件中添加了完整的头信息,因此无法将其用于向现有文件添加资源。为了处理这一问题,该示例会检查 SerializedTimeZones.resx 文件,如果该文件存在,则将其所有资源(两个序列化的时区除外)存储在泛型 Dictionary<TKey, TValue> 对象中。随后,该示例会删除现有文件,并将现有资源添加到新的 SerializedTimeZones.resx 文件中。序列化的时区数据也会被添加到此文件中。

资源的键(或“名称”)字段不应包含嵌入的空格。在将时区标识符分配给资源文件之前,应调用 Replace(String, String) 方法移除时区标识符中嵌入的所有空格。

编译代码

此示例要求:

  • 在项目中添加一个对 System.Windows.Forms.dll 和 System.Core.dll 的引用。

  • 导入下列命名空间:

    Imports System.Globalization
    Imports System.IO
    Imports System.Reflection
    Imports System.Resources
    
    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;
    

请参见

任务

如何:从嵌入的资源还原时区

概念

时区概述

其他资源

时间和时区