如何:将时区保存到嵌入的资源中
时区感知应用程序通常需要存在特定的时区。 但是,由于单个 TimeZoneInfo 对象的可用性取决于本地系统注册表中存储的信息,因此甚至通常可用的时区也可能不存在。 此外,使用 CreateCustomTimeZone 方法实例化的自定义时区的相关信息不会与注册表中的其他时区信息一起存储。 为了确保在需要时能够使用这些时区,可通过将其序列化来保存它们,然后通过反序列化来还原。
通常,TimeZoneInfo 对象的序列化过程在时区感知应用程序之外单独进行。 根据用于保存序列化 TimeZoneInfo 对象的数据存储,时区数据可以作为设置或安装例程的一部分序列化(例如,当数据存储在注册表的应用程序键中时),也可作为在编译最终版应用程序之前运行的实用程序例程的一部分序列化(例如,当序列化的数据存储在 NET XML 资源 (.resx) 文件中时)。
除了使用应用程序编译的资源文件之外,还可使用其他几个数据存储来保存时区信息。 其中包括:
注册表。 注意,应用程序应使用它自己的应用程序键的子项来存储自定义时区数据,而不是使用 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones 的子项来存储。
配置文件。
其他系统文件。
通过将时区序列化为 resx 文件来保存时区
检索现有时区或创建新时区。
若要检索现有时区,请参阅如何:访问预定义 UTC 和本地时区对象和如何:实例化 TimeZoneInfo 对象。
若要创建新时区,请调用 CreateCustomTimeZone 方法的重载之一。 有关详细信息,请参阅如何:创建不含调整规则的时区和如何:创建含调整规则的时区。
若要创建包含时区数据的字符串,可调用 ToSerializedString 方法。
若要实例化 StreamWriter 对象,可向 StreamWriter 类构造函数提供 .resx 文件的名称和(可选)路径。
若要实例化 ResXResourceWriter 对象,可将 StreamWriter 对象传递给 ResXResourceWriter 类构造函数。
将时区的序列化字符串传递给 ResXResourceWriter.AddResource 方法。
调用 ResXResourceWriter.Generate 方法。
调用 ResXResourceWriter.Close 方法。
若要关闭 StreamWriter 对象,可调用其 Close 方法。
将生成的 .resx 文件添加到应用程序的 Visual Studio 项目中。
使用 Visual Studio 中的“属性”窗口,确保将 .resx 文件的“生成操作”属性设置为“嵌入的资源”。
示例
下面的示例将表示中央标准时间的 TimeZoneInfo 对象和表示南极洲帕尔默站时间的 TimeZoneInfo 对象序列化为一个名为 SerializedTimeZones.resx 的 .NET XML 资源文件。 中央标准时间通常在注册表中定义;南极洲帕尔默站是一个自定义时区。
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
此示例序列化 TimeZoneInfo 对象,以便在编译时可以在资源文件中使用它们。
由于 ResXResourceWriter.Generate 方法负责将完整标头信息添加到 .NET XML 资源文件中,因此它不能用于将资源添加到现有文件中。 该示例通过检查 SerializedTimeZones.resx 文件来处理此问题,如果存在,则会将其所有资源(两个序列化时区除外)存储到泛型 Dictionary<TKey,TValue> 对象。 然后删除现有文件,将现有资源添加到新的 SerializedTimeZones.resx 文件。 经过序列化处理的时区数据也会添加到此文件中。
资源的关键(或“名称”)字段不应包含嵌入空格。 调用 Replace(String, String) 方法,在将时区标识符分配给资源文件之前删除这些嵌入的空间。
编译代码
此示例需要:
向项目添加对 System.Windows.Forms.dll and System.Core.dll 的引用。
导入以下命名空间:
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