この記事の英語版を表示するには、[英語] のチェック ボックスをオンにしてください。また、テキストにマウス ポインターを合わせると、ポップアップ ウィンドウに英語のテキストを表示することもできます。
翻訳
英語

タイム ゾーンの保存と復元

 

TimeZoneInfo クラスは、レジストリを利用して、定義されているタイム ゾーン データを取得します。 ただし、レジストリは動的な構造です。 さらに、レジストリに格納されているタイム ゾーンの情報は、主として、現在の年の時刻の調整と変換を行うためにオペレーティング システムによって使用されます。 このため、正確なタイム ゾーン データに依存するアプリケーションには 2 つの大きな影響があります。

  • アプリケーションが必要とするタイム ゾーンが、レジストリで定義されていない、レジストリで名前が変更されている、またはレジストリから削除されている可能性があります。

  • レジストリで定義されているタイム ゾーンに、過去のタイム ゾーンの変換に必要な特定の調整ルールに関する情報が含まれない可能性があります。

TimeZoneInfo クラスは、これらの制約に対処するため、タイム ゾーン データのシリアル化 (保存) と逆シリアル化 (復元) をサポートします。

タイム ゾーン データのシリアル化と逆シリアル化によるタイム ゾーンの保存と復元に関係するメソッド呼び出しは 2 つだけです。

  • TimeZoneInfo オブジェクトをシリアル化するには、オブジェクトの ToSerializedString メソッドを呼び出します。 このメソッドはパラメーターを受け取らず、タイム ゾーン情報が格納された文字列を返します。

  • シリアル化された文字列から TimeZoneInfo オブジェクトを逆シリアル化するには、その文字列を static (Visual Basic では Shared) TimeZoneInfo.FromSerializedString メソッドに渡します。

TimeZoneInfo オブジェクトを文字列に保存 (シリアル化) し、後で使用するときに復元 (逆シリアル化) できると、TimeZoneInfo クラスの実用性と柔軟性が向上します。 このセクションでは、シリアル化と逆シリアル化が最も役に立ついくつかの状況を調べます。

シリアル化されたタイム ゾーンは、必要に応じて文字列から復元できます。 レジストリから取得したタイム ゾーンが特定の日付範囲内の日時を正しく変換できない場合は、アプリケーションでこれを行うことがあります。 たとえば、Windows XP のレジストリのタイム ゾーン データは単一の調整規則をサポートしますが、Windows Vista のレジストリで定義されているタイム ゾーンは通常 2 つの調整規則についての情報を提供します。 つまり、過去の時刻変換が正しく行われない可能性があります。 タイム ゾーン データのシリアル化と逆シリアル化により、このような制約に対処できます。

次の例では、米国の中部標準時ゾーンが導入される前に 1883 ~ 1917 年まで米国東部標準時タイム ゾーンを表すように、調整規則のない TimeZoneInfo のカスタム クラスを定義します。 このカスタム タイム ゾーンを、グローバル スコープを持つ変数にシリアル化します。 タイム ゾーン変換メソッド ConvertUtcTime に世界協定時刻 (UTC) を渡して変換します。 1917 年以前の日時の場合は、カスタム東部標準時ゾーンがシリアル化された文字列から復元されて、レジストリから取得されたタイム ゾーンの代わりに使用されます。

using System;

public class TimeZoneSerialization
{
   static string serializedEst;

   public static void Main()
   {
      // Retrieve Eastern Standard Time zone from registry
      try
      {
         TimeZoneSerialization tzs = new TimeZoneSerialization();
         TimeZoneInfo est = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
         // Create custom Eastern Time Zone for historical (pre-1918) conversions
         CreateTimeZone();
         // Call conversion function with one current and one pre-1918 date and time
         Console.WriteLine(ConvertUtcTime(DateTime.UtcNow, est));
         Console.WriteLine(ConvertUtcTime(new DateTime(1900, 11, 15, 9, 32, 00, DateTimeKind.Utc), est));
      }
      catch (TimeZoneNotFoundException)
      {
         Console.WriteLine("The Eastern Standard Time zone is not in the registry.");
      }
      catch (InvalidTimeZoneException)
      {
         Console.WriteLine("Data on the Eastern Standard Time Zone in the registry is corrupted.");
      }   
   }

   private static void CreateTimeZone()
   {
      // Create a simple Eastern Standard time zone 
      // without adjustment rules for 1883-1918
      TimeZoneInfo earlyEstZone = TimeZoneInfo.CreateCustomTimeZone("Eastern Standard Time", 
                                      new TimeSpan(-5, 0, 0), 
                                      " (GMT-05:00) Eastern Time (United States)",  
                                      "Eastern Standard Time");
      serializedEst = earlyEstZone.ToSerializedString();                        
   }

   private static DateTime ConvertUtcTime(DateTime utcDate, TimeZoneInfo tz) 
   {
      // Use time zone object from registry 
      if (utcDate.Year > 1917)
      {
         return TimeZoneInfo.ConvertTimeFromUtc(utcDate, tz);
      }   
      // Handle dates before introduction of DST
      else
      {
         // Restore serialized time zone object
         tz = TimeZoneInfo.FromSerializedString(serializedEst);
         return TimeZoneInfo.ConvertTimeFromUtc(utcDate, tz);
      }      
   }
}

レジストリは動的な構造なので、その内容は偶然に、または意図的に変更される可能性があります。 タイム ゾーンはレジストリで定義されている必要があり、アプリケーションの正常な実行に必要ですが、つまり、これが存在しなくなる可能性があります。 タイム ゾーンのシリアル化と逆シリアル化がサポートされていないと、TimeZoneNotFoundException が発生した場合は、アプリケーションを終了してこれを処理するしか方法がありません。 一方、タイム ゾーンのシリアル化と逆シリアル化を使用すると、予期しない TimeZoneNotFoundException が発生しても、シリアル化された文字列から必要なタイム ゾーンを復元することで対処でき、アプリケーションは実行を継続できます。

次の例では、カスタムの中部標準時ゾーンを作成してシリアル化します。 その後、レジストリから中部標準時ゾーンの取得を試みます。 取得操作で TimeZoneNotFoundException または InvalidTimeZoneException がスローされる場合は、例外ハンドラーがタイム ゾーンを逆シリアル化します。

using System;
using System.Collections.Generic;

public class TimeZoneApplication
{
   // Define collection of custom time zones
   private Dictionary<string, string> customTimeZones = new Dictionary<string, string>();
   private TimeZoneInfo cst;

   public TimeZoneApplication()
   {
      // Create custom Central Standard Time 
      //
      // Declare necessary TimeZoneInfo.AdjustmentRule objects for time zone
      TimeZoneInfo customTimeZone;
      TimeSpan delta = new TimeSpan(1, 0, 0);
      TimeZoneInfo.AdjustmentRule adjustment;
      List<TimeZoneInfo.AdjustmentRule> adjustmentList = new List<TimeZoneInfo.AdjustmentRule>();
      // Declare transition time variables to hold transition time information
      TimeZoneInfo.TransitionTime transitionRuleStart, transitionRuleEnd;

      // Define end rule (for 1976-2006)
      transitionRuleEnd = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 2, 0, 0), 10, 5, DayOfWeek.Sunday);
      // Define rule (1976-1986)
      transitionRuleStart = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 2, 0, 0), 04, 05, DayOfWeek.Sunday);
      adjustment = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(new DateTime(1976, 1, 1), new DateTime(1986, 12, 31), delta, transitionRuleStart, transitionRuleEnd);
      adjustmentList.Add(adjustment);
      // Define rule (1987-2006)  
      transitionRuleStart = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 2, 0, 0), 04, 01, DayOfWeek.Sunday);
      adjustment = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(new DateTime(1987, 1, 1), new DateTime(2006, 12, 31), delta, transitionRuleStart, transitionRuleEnd);
      adjustmentList.Add(adjustment);
      // Define rule (2007- )  
      transitionRuleStart = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 2, 0, 0), 03, 02, DayOfWeek.Sunday);
      transitionRuleEnd = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 2, 0, 0), 11, 01, DayOfWeek.Sunday);
      adjustment = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(new DateTime(2007, 01, 01), DateTime.MaxValue.Date, delta, transitionRuleStart, transitionRuleEnd);
      adjustmentList.Add(adjustment);

      // Create custom U.S. Central Standard Time zone         
      customTimeZone = TimeZoneInfo.CreateCustomTimeZone("Central Standard Time", 
                      new TimeSpan(-6, 0, 0), 
                      "(GMT-06:00) Central Time (US Only)", "Central Standard Time", 
                      "Central Daylight Time", adjustmentList.ToArray());       
      // Add time zone to collection
      customTimeZones.Add(customTimeZone.Id, customTimeZone.ToSerializedString());                           

      // Create any other required time zones     
   }

   public static void Main()
   {
      TimeZoneApplication tza = new TimeZoneApplication();
      tza.AppEntryPoint();
   }

   private void AppEntryPoint()
   {
      try
      {
         cst = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
      }   
      catch (TimeZoneNotFoundException)
      {
         if (customTimeZones.ContainsKey("Central Standard Time"))
            HandleTimeZoneException("Central Standard Time");
      }
      catch (InvalidTimeZoneException)
      {
         if (customTimeZones.ContainsKey("Central Standard Time"))
            HandleTimeZoneException("Central Standard Time");
      }               
      if (cst == null)
      {
         Console.WriteLine("Unable to load Central Standard Time zone.");
         return;
      }
      DateTime currentTime = DateTime.Now;
      Console.WriteLine("The current {0} time is {1}.", 
                        TimeZoneInfo.Local.IsDaylightSavingTime(currentTime) ? 
                            TimeZoneInfo.Local.StandardName : 
                            TimeZoneInfo.Local.DaylightName, 
                        currentTime.ToString("f"));
      Console.WriteLine("The current {0} time is {1}.", 
                        cst.IsDaylightSavingTime(currentTime) ? 
                            cst.StandardName : 
                            cst.DaylightName, 
                        TimeZoneInfo.ConvertTime(currentTime, TimeZoneInfo.Local, cst).ToString("f"));
   }

   private void HandleTimeZoneException(string timeZoneName)
   {
      string tzString = customTimeZones[timeZoneName];
      cst = TimeZoneInfo.FromSerializedString(tzString);
   }
}

前の例では、タイム ゾーン情報を文字列変数に格納し、必要ときはこれを復元しました。 ただし、シリアル化されたタイム ゾーン情報を格納している文字列自体は、外部ファイル、アプリケーションに埋め込まれたリソース ファイル、レジストリなどストレージ メディアに格納できます (カスタム タイム ゾーンに関する情報は、レジストリではシステムのタイム ゾーン キーとは別に格納する必要があります)。

シリアル化されたタイム ゾーン文字列をこの方法で格納すると、タイム ゾーン作成ルーチンもアプリケーション自体とは別になります。 たとえば、タイム ゾーン作成ルーチンは、アプリケーションが使用できる過去のタイム ゾーンの履歴情報を格納するデータ ファイルを実行および作成できます。 データ ファイルは、アプリケーションと共にインストールできます。また、アプリケーションでタイム ゾーンが必要になったときは、データ ファイルを開き、格納されている 1 つ以上のタイム ゾーンを逆シリアル化できます。

埋め込みリソースを使用してシリアル化されたタイム ゾーン データを格納する例については、「方法 : 埋め込みリソースにタイム ゾーンを保存する」および「方法 : 埋め込みリソースからタイム ゾーンを復元する」を参照してください。

表示: