如何:将时区保存到嵌入的资源中
更新:2007 年 11 月
时区识别应用程序通常要求存在某个特定的时区。但是,由于各个 TimeZoneInfo 对象的可用性取决于本地系统的注册表中存储的信息,因此即使是通常可用的时区也可能不存在。此外,对于使用 CreateCustomTimeZone 方法实例化的自定义时区,其相关信息在注册表中并不与其他时区信息存储在一起。若要确保可在需要时获得这些时区,可以通过序列化来保存它们,以后再通过反序列化来还原它们。
通常,序列化 TimeZoneInfo 对象的过程在时区识别应用程序之外单独进行。根据用于保存序列化 TimeZoneInfo 对象的数据存储区,可以将时区数据序列化为设置或安装例程的一部分(例如,当数据存储在注册表的应用程序键中时),或将其序列化为在编译最终应用程序之前运行的实用工具例程的一部分(例如,当序列化数据存储在 .NET XML 资源 (.resx) 文件中时)。
除了与应用程序一起编译的资源文件外,还有其他一些数据存储区可用于保存时区信息。这些功能包括:
注册表。请注意,应用程序应使用自己应用程序键的子项来存储自定义时区数据,而不应使用 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones 的子项。
配置文件。
其他系统文件。
通过将时区序列化到 .resx 文件中来保存时区
检索一个现有的时区或创建一个新时区。
若要检索现有的时区,请参见如何:访问预定义的 UTC 和本地时区对象和如何:实例化 TimeZoneInfo 对象。
若要创建新时区,请调用 CreateCustomTimeZone 方法的重载之一。有关更多信息,请参见如何:创建不带调整规则的时区和如何:创建带有调整规则的时区。
调用 ToSerializedString 方法,以创建包含时区数据的字符串。
通过向 StreamWriter 类构造函数提供 .resx 文件的名称和路径(可选)来实例化 StreamWriter 对象。
通过将 StreamWriter 对象传递给 ResXResourceWriter 类构造函数来实例化 ResXResourceWriter 对象。
将时区的序列化字符串传递给 ResXResourceWriter.AddResource 方法。
调用 ResXResourceWriter.Generate 方法。
调用 ResXResourceWriter.Close 方法。
通过调用 StreamWriter 对象的 Close 方法来关闭该对象。
将生成的 .resx 文件添加到应用程序的 Visual Studio 项目中。
使用 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;