方法: 埋め込みリソースにタイム ゾーンを保存する

タイム ゾーンに対応するアプリケーションでは、多くの場合、特定のタイム ゾーンが存在する必要があります。 ただし、個々の 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. 名前を指定し、必要に応じて .resx ファイルへのパスを StreamWriter クラス コンストラクターに指定して、StreamWriter オブジェクトをインスタンス化します。

  4. StreamWriter オブジェクトを ResXResourceWriter クラス コンストラクターに渡すことによって、ResXResourceWriter オブジェクトをインスタンス化します。

  5. タイム ゾーンのシリアル化された文字列を ResXResourceWriter.AddResource メソッドに渡します。

  6. ResXResourceWriter.Generate メソッドを呼び出します。

  7. ResXResourceWriter.Close メソッドを呼び出します。

  8. Close メソッドを呼び出して、StreamWriter オブジェクトを閉じます。

  9. 生成された .resx ファイルをアプリケーションの Visual Studio プロジェクトに追加します。

  10. 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 ファイルを確認し、存在する場合は、2 つのシリアル化されたタイム ゾーン以外のすべてのリソースを汎用 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
    

関連項目