作法:將時區儲存到內嵌資源
時區感知應用程式經常需要有特定時區存在。 不過,因為個別 TimeZoneInfo 物件的可用性取決於儲存在本機系統登錄中的資訊,因此就連自訂可用的時區都可能不存在。 此外,使用 CreateCustomTimeZone 方法具現化之自訂時區的相關資訊,不會與其他時區資訊一起儲存在登錄中。 若要確保這些時區可在需要時使用,您可以藉由序列化它們來儲存它們,稍後再還原序列化它們來加以還原。
一般而言,序列化 TimeZoneInfo 物件與時區感知應用程式分開進行。 根據用來保存序列化 TimeZoneInfo 物件的資料存放區,時區資料可能會序列化為安裝程式或安裝常式的一部分 (例如,當資料儲存在登錄的應用程式機碼中),或作為在編譯最終應用程式之前執行之公用程式常式的一部分 (例如,當序列化的資料儲存在 .NET XML 資源 (.resx) 檔案時)。
除了使用應用程式編譯的資源檔之外,其他數個資料存放區也可用於時區資訊。 其中包括下列各項:
登錄。 請注意,應用程式應該使用其自己的應用程式機碼來儲存自訂時區資料,而不是使用 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones 的子機碼。
設定檔。
其他系統檔案。
將時區序列化為 .resx 檔案以儲存時區
擷取現有的時區,或建立新的時區。
若要擷取現有的時區,請參閱如何:存取預先定義的 UTC 和當地時區物件和如何:具現化 TimeZoneInfo 物件。
若要建立新的時區,請呼叫 CreateCustomTimeZone 方法的其中一個多載。 如需詳細資訊,請參閱 如何:建立不使用調整規則的時區和如何:使用有調整規則建立時區。
呼叫 ToSerializedString 方法,以建立包含時區資料的字串。
藉由提供 .resx 檔案的名稱以及 (選擇性地) .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 資源檔。 中央標準時間通常會定義於登錄中;南極洲帕麥站是自訂時區。
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 和 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