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

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

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

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

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

タイム ゾーンのシリアル化と逆シリアル化

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

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

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

シリアル化と逆シリアル化のシナリオ

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

アプリケーションでのタイム ゾーン データのシリアル化と逆シリアル化

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

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

Module TimeZoneSerialization
   Dim serializedEst As String

   Public Sub Main()
      ' Retrieve Eastern Standard Time zone from registry
      Try
         Dim est As TimeZoneInfo = 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(Date.UtcNow, est))
         Console.WriteLine(ConvertUtcTime(New Date(1900, 11, 15, 9, 32, 00, DateTimeKind.Utc), est))
      Catch e As TimeZoneNotFoundException
         Console.WriteLine("The Eastern Standard Time zone is not in the registry.")
      Catch e As InvalidTimeZoneException
         Console.WriteLine("Data on the Eastern Standard Time Zone in the registry is corrupted.")
      End Try   
   End Sub

   Private Sub CreateTimeZone()
      ' Create a simple Eastern Standard time zone 
      ' without adjustment rules for 1883-1918
      Dim earlyEstZone As TimeZoneInfo = TimeZoneInfo.CreateCustomTimeZone("Eastern Standard Time", _
                                      New TimeSpan(-5, 0, 0), _
                                      " (GMT-05:00) Eastern Time (United States)", _ 
                                      "Eastern Standard Time")
      serializedEst = earlyEstZone.ToSerializedString()                                
   End Sub

   Private Function ConvertUtcTime(utcDate As Date, tz As TimeZoneInfo) As Date
      ' Use time zone object from registry 
      If Year(utcDate) > 1917 Then
         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)
      End If      
   End Function
End Module
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 がスローされる場合は、例外ハンドラーがタイム ゾーンを逆シリアル化します。

Imports System.Collections.Generic

Public Class TimeZoneApplication
   ' Define collection of custom time zones
   Private customTimeZones As New Dictionary(Of String, String)
   Private cst As TimeZoneInfo 

   Public Sub New()
      ' Define custom Central Standard Time
      '
      ' Declare necessary TimeZoneInfo.AdjustmentRule objects for time zone
      Dim customTimeZone As TimeZoneInfo
      Dim delta As New TimeSpan(1, 0, 0)
      Dim adjustment As TimeZoneInfo.AdjustmentRule
      Dim adjustmentList As New List(Of TimeZoneInfo.AdjustmentRule)
      ' Declare transition time variables to hold transition time information
      Dim transitionRuleStart, transitionRuleEnd As TimeZoneInfo.TransitionTime

      ' Define end rule (for 1976-2006)
      transitionRuleEnd = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(#02:00:00AM#, 10, 5, DayOfWeek.Sunday)
      ' Define rule (1976-1986)
      transitionRuleStart = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(#2:00:00AM#, 04, 05, DayOfWeek.Sunday)
      adjustment = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(#01/01/1976#, #12/31/1986#, delta, transitionRuleStart, transitionRuleEnd)
      adjustmentList.Add(adjustment)
      ' Define rule (1987-2006)  
      transitionRuleStart = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(#2:00:00AM#, 04, 01, DayOfWeek.Sunday)
      adjustment = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(#01/01/1987#, #12/31/2006#, delta, transitionRuleStart, transitionRuleEnd)
      adjustmentList.Add(adjustment)
      ' Define rule (2007- )  
      transitionRuleStart = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(#2:00:00AM#, 03, 02, DayOfWeek.Sunday)
      transitionRuleEnd = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(#2:00:00AM#, 11, 01, DayOfWeek.Sunday)
      adjustment = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(#01/01/2007#, Date.MaxValue.Date, delta, transitionRuleStart, transitionRuleEnd)
      adjustmentList.Add(adjustment)
      ' Create custom 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     
   End Sub

   Public Shared Sub Main()
      Dim tza As New TimeZoneApplication()
      tza.AppEntryPoint()
   End Sub

   Private Sub AppEntryPoint()
      Try
         cst = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time")
      Catch e As TimeZoneNotFoundException
         If customTimeZones.ContainsKey("Central Standard Time")
            HandleTimeZoneException("Central Standard Time")
         End If
      Catch e As InvalidTimeZoneException
         If customTimeZones.ContainsKey("Central Standard Time")
            HandleTimeZoneException("Central Standard Time")
         End If
      End Try               
      If cst Is Nothing Then
         Console.WriteLine("Unable to load Central Standard Time zone.")
         Return
      End If
      Dim currentTime As Date = Date.Now
      Console.WriteLine("The current {0} time is {1}.", _
                        IIf(TimeZoneInfo.Local.IsDaylightSavingTime(currentTime), _
                            TimeZoneInfo.Local.StandardName, _
                            TimeZoneInfo.Local.DaylightName), _
                        currentTime.ToString("f"))
      Console.WriteLine("The current {0} time is {1}.", _
                        IIf(cst.IsDaylightSavingTime(currentTime), _
                            cst.StandardName, _
                            cst.DaylightName), _
                        TimeZoneInfo.ConvertTime(currentTime, TimeZoneInfo.Local, cst).ToString("f"))
   End Sub

   Private Sub HandleTimeZoneException(timeZoneName As String)
      Dim tzString As String = customTimeZones.Item(timeZoneName)
      cst = TimeZoneInfo.FromSerializedString(tzString)
   End Sub
End Class
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 つ以上のタイム ゾーンを逆シリアル化できます。

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

参照

その他の技術情報

日付、時刻、およびタイム ゾーン