HOW TO:將時區儲存到內嵌資源
更新:2007 年 11 月
時區感知應用程式通常需要特定的時區。但是,因為個別 TimeZoneInfo 物件的可用性必須視本機系統登錄中所儲存的資訊而定,所以即使自訂可用的時區也可能不存在。此外,使用 CreateCustomTimeZone 方法具現化的自訂時區相關資訊,並不會和其他時區資訊一起儲存在登錄中。如果要確保這些時區在需要時可以使用,您可以將時區序列化加以儲存,稍後再還原序列化以還原時區。
一般而言,將 TimeZoneInfo 物件序列化與時區感知應用程式無關。視用來保存序列化 TimeZoneInfo 物件的資料存放區而定,時區資料可能會被序列化成為設定或安裝常式的一部分 (例如,當資料儲存在登錄的應用程式索引鍵中),或成為編譯最終應用程式前所執行的公用程式常式的一部分 (例如,當序列化資料存放在 .NET XML 資源 (.resx) 檔中時)。
除了以應用程式編譯的資源檔外,還有幾個其他資料存放區可用來存放時區資訊。這些控制項包括下列各項:
登錄。請注意,應用程式應使用自己的應用程式索引鍵的子機碼,儲存自訂時區資料,而不是使用 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones 的子機碼。
組態檔。
其他系統檔案。
將時區序列化成 .resx 檔案以儲存時區
擷取現有時區,或建立新的時區。
如果要擷取現有時區,請參閱 HOW TO:存取預先定義的 UTC 和本機時區物件及 HOW TO:具現化 TimeZoneInfo 物件。
如果要建立新的時區,請呼叫下列 CreateCustomTimeZone 方法的其中一個多載。如需詳細資訊,請參閱 HOW TO:建立沒有調整規則的時區和 HOW TO:建立有調整規則的時區。
呼叫 ToSerializedString 方法以建立內含時區資料的字串。
提供 .resx 檔案的名稱 (或路徑) 給 StreamWriter 類別建構函式,將 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;