方法 : 日付と時刻の値をラウンドトリップさせる

ある特定の時点を明確に表すように日付と時刻の値を保つことは、多くのアプリケーションに共通する要件です。 このトピックでは、DateTime 値、DateTimeOffset 値、およびタイム ゾーン情報を持った日時値を保存する方法、および、保存した日時とまったく同じ時点を表すように復元する方法について説明します。

DateTime 値をラウンドトリップさせるには

  1. DateTime.ToString(String) メソッドと "o" 書式指定子を使用して、DateTime 値を対応する文字列形式に変換します。

  2. DateTime 値の文字列形式をファイルに保存するか、プロセス、アプリケーション ドメイン、またはマシン境界に渡します。

  3. DateTime 値を表す文字列を取得します。

  4. DateTime.Parse(String, IFormatProvider, DateTimeStyles) メソッドを呼び出し、styles パラメーターの値として DateTimeStyles.RoundtripKind を渡します。

DateTime の値をラウンドトリップさせる方法を次の例に示します。

Const fileName As String = ".\DateFile.txt"

Dim outFile As New StreamWriter(fileName)

' Save DateTime value.
Dim dateToSave As Date = DateTime.SpecifyKind(#06/12/2008 6:45:15 PM#, _
                                              DateTimeKind.Local)
Dim dateString As String = dateToSave.ToString("o")      
Console.WriteLine("Converted {0} ({1}) to {2}.", dateToSave.ToString(), _
                  dateToSave.Kind.ToString(), dateString)      
outFile.WriteLine(dateString)
Console.WriteLine("Wrote {0} to {1}.", dateString, fileName)
outFile.Close()   

' Restore DateTime value.
Dim restoredDate As Date

Dim inFile As New StreamReader(fileName)
dateString = inFile.ReadLine()
inFile.Close()
restoredDate = DateTime.Parse(dateString, Nothing, DateTimeStyles.RoundTripKind)
Console.WriteLine("Read {0} ({2}) from {1}.", restoredDate.ToString(), _
                  fileName, restoredDAte.Kind.ToString())
' The example displays the following output:
'    Converted 6/12/2008 6:45:15 PM (Local) to 2008-06-12T18:45:15.0000000-05:00.
'    Wrote 2008-06-12T18:45:15.0000000-05:00 to .\DateFile.txt.
'    Read 6/12/2008 6:45:15 PM (Local) from .\DateFile.txt.
const string fileName = @".\DateFile.txt";

StreamWriter outFile = new StreamWriter(fileName);

// Save DateTime value.
DateTime dateToSave = DateTime.SpecifyKind(new DateTime(2008, 6, 12, 18, 45, 15), 
                                           DateTimeKind.Local);
string dateString = dateToSave.ToString("o");      
Console.WriteLine("Converted {0} ({1}) to {2}.", 
                  dateToSave.ToString(), 
                  dateToSave.Kind.ToString(), 
                  dateString);      
outFile.WriteLine(dateString);
Console.WriteLine("Wrote {0} to {1}.", dateString, fileName);
outFile.Close();

// Restore DateTime value.
DateTime restoredDate;

StreamReader inFile = new StreamReader(fileName);
dateString = inFile.ReadLine();
inFile.Close();
restoredDate = DateTime.Parse(dateString, null, DateTimeStyles.RoundtripKind);
Console.WriteLine("Read {0} ({2}) from {1}.", restoredDate.ToString(), 
                                              fileName, 
                                              restoredDate.Kind.ToString());
// The example displays the following output:
//    Converted 6/12/2008 6:45:15 PM (Local) to 2008-06-12T18:45:15.0000000-05:00.
//    Wrote 2008-06-12T18:45:15.0000000-05:00 to .\DateFile.txt.
//    Read 6/12/2008 6:45:15 PM (Local) from .\DateFile.txt.

DateTime の値をラウンドトリップさせる場合、この方法により、あらゆる現地時刻と協定世界時刻を適切に保存できます。 たとえば、現地時間の DateTime 値を 米国太平洋標準時ゾーンのシステムで保存し、米国中部標準時ゾーンの システムで復元できます。復元された日時は、2 つのタイム ゾーン間の時差を反映し、元の時刻の 2 時間遅れの時刻となります。 ただし、タイム ゾーンが指定されていない時刻の場合、この方法では必ずしも正確な結果を得ることができません。 Kind プロパティが Unspecified である DateTime 値はすべて現地時刻として扱われます。 現地時刻以外では、DateTime が間違った時点を示すことになります。 この制限は、日付と時刻の値に、該当するタイム ゾーンを確実に関連付けた上で、保存操作と復元操作を行うことによって回避できます。

DateTimeOffset 値をラウンドトリップさせるには

  1. DateTimeOffset.ToString(String) メソッドと "o" 書式指定子を使用して、DateTimeOffset 値を対応する文字列形式に変換します。

  2. DateTimeOffset 値の文字列形式をファイルに保存するか、プロセス、アプリケーション ドメイン、またはマシン境界に渡します。

  3. DateTimeOffset 値を表す文字列を取得します。

  4. DateTimeOffset.Parse(String, IFormatProvider, DateTimeStyles) メソッドを呼び出し、styles パラメーターの値として DateTimeStyles.RoundtripKind を渡します。

DateTimeOffset の値をラウンドトリップさせる方法を次の例に示します。

Const fileName As String = ".\DateOff.txt"

Dim outFile As New StreamWriter(fileName)

' Save DateTime value.
Dim dateToSave As New DateTimeOffset(2008, 6, 12, 18, 45, 15, _
                                     New TimeSpan(7, 0, 0))
Dim dateString As String = dateToSave.ToString("o")      
Console.WriteLine("Converted {0} to {1}.", dateToSave.ToString(), dateString)      
outFile.WriteLine(dateString)
Console.WriteLine("Wrote {0} to {1}.", dateString, fileName)
outFile.Close()   

' Restore DateTime value.
Dim restoredDateOff As DateTimeOffset

Dim inFile As New StreamReader(fileName)
dateString = inFile.ReadLine()
inFile.Close()
restoredDateOff = DateTimeOffset.Parse(dateString, Nothing, DateTimeStyles.RoundTripKind)
Console.WriteLine("Read {0} from {1}.", restoredDateOff.ToString(), fileName)
' The example displays the following output:
'    Converted 6/12/2008 6:45:15 PM +07:00 to 2008-06-12T18:45:15.0000000+07:00.
'    Wrote 2008-06-12T18:45:15.0000000+07:00 to .\DateOff.txt.
'    Read 6/12/2008 6:45:15 PM +07:00 from .\DateOff.txt.
const string fileName = @".\DateOff.txt";

StreamWriter outFile = new StreamWriter(fileName);

// Save DateTime value.
DateTimeOffset dateToSave = new DateTimeOffset(2008, 6, 12, 18, 45, 15, 
                                               new TimeSpan(7, 0, 0));
string dateString = dateToSave.ToString("o");      
Console.WriteLine("Converted {0} to {1}.", dateToSave.ToString(), 
                  dateString);      
outFile.WriteLine(dateString);
Console.WriteLine("Wrote {0} to {1}.", dateString, fileName);
outFile.Close();

// Restore DateTime value.
DateTimeOffset restoredDateOff;

StreamReader inFile = new StreamReader(fileName);
dateString = inFile.ReadLine();
inFile.Close();
restoredDateOff = DateTimeOffset.Parse(dateString, null, 
                                       DateTimeStyles.RoundtripKind);
Console.WriteLine("Read {0} from {1}.", restoredDateOff.ToString(), 
                  fileName);
// The example displays the following output:
//    Converted 6/12/2008 6:45:15 PM +07:00 to 2008-06-12T18:45:15.0000000+07:00.
//    Wrote 2008-06-12T18:45:15.0000000+07:00 to .\DateOff.txt.
//    Read 6/12/2008 6:45:15 PM +07:00 from .\DateOff.txt.

この方法により、DateTimeOffset の値が、常にある特定の時点として明確に識別されます。 その後、DateTimeOffset.ToUniversalTime メソッドを呼び出すことによって、この値を世界協定時刻 (UTC) に変換できます。また、DateTimeOffset.ToOffset メソッドまたは TimeZoneInfo.ConvertTime(DateTimeOffset, TimeZoneInfo) メソッドを呼び出すことによって、特定のタイム ゾーンの時刻に変換することもできます。 この方法には、特定のタイム ゾーンの時刻を表す DateTimeOffset の値に対して日付と時刻の演算を行った場合、そのタイム ゾーンにおける正確な結果を得ることができないという重要な制限があります。 これは、DateTimeOffset の値をインスタンス化すると、対応するタイム ゾーンとの関連付けが解除されるためです。 したがって、日付と時刻の計算を実行した場合、タイム ゾーンの調整規則は適用されません。 この問題は、日付値と時刻値、および付随するタイム ゾーンを保持するカスタム型を定義することによって回避できます。

対応するタイム ゾーンと共に日付と時刻の値をラウンドトリップさせるには

  1. 2 つのフィールドを持ったクラスまたは構造体を定義します。 1 つ目のフィールドは DateTime オブジェクトまたは DateTimeOffset オブジェクトに、2 つ目のフィールドは TimeZoneInfo オブジェクトにします。 これを踏まえた単純な型の例を次に示します。

    <Serializable> Public Class DateInTimeZone
       Private tz As TimeZoneInfo
       Private thisDate As DateTimeOffset
    
       Public Sub New()
       End Sub
    
       Public Sub New(date1 As DateTimeOffset, timeZone As TimeZoneInfo)
          If timeZone Is Nothing Then 
             Throw New ArgumentNullException("The time zone cannot be null.")
          End If
          Me.thisDate = date1
          Me.tz = timeZone
       End Sub
    
       Public Property DateAndTime As DateTimeOffset
          Get
             Return Me.thisDate
          End Get
          Set
             If Value.Offset <> Me.tz.GetUtcOffset(Value) Then
                Me.thisDate = TimeZoneInfo.ConvertTime(Value, tz)
             Else
                Me.thisDate = Value
             End If
          End Set
       End Property
    
       Public ReadOnly Property TimeZone As TimeZoneInfo
          Get
             Return tz
          End Get
       End Property
    End Class
    
    [Serializable] public class DateInTimeZone
    {
       private TimeZoneInfo tz;
       private DateTimeOffset thisDate;
    
       public DateInTimeZone() {}
    
       public DateInTimeZone(DateTimeOffset date, TimeZoneInfo timeZone)
       {
          if (timeZone == null) 
             throw new ArgumentNullException("The time zone cannot be null.");
    
          this.thisDate = date;
          this.tz = timeZone;
       }
    
       public DateTimeOffset DateAndTime
       {
          get { 
             return this.thisDate;
          }
          set {
             if (value.Offset != this.tz.GetUtcOffset(value)) 
                this.thisDate = TimeZoneInfo.ConvertTime(value, tz);
             else
                this.thisDate = value;
          }
       }
    
       public TimeZoneInfo TimeZone
       {
          get {
             return this.tz;
          }
       }
    }
    
  2. このクラスに SerializableAttribute 属性を適用します。

  3. BinaryFormatter.Serialize メソッドを使用してオブジェクトをシリアル化します。

  4. Deserialize メソッドを使用してオブジェクトを復元します。

  5. 逆シリアル化したオブジェクトを適切な型のオブジェクトにキャスト (C# の場合) または変換 (Visual Basic の場合) します。

日付値と時刻値およびタイム ゾーン情報が格納されたオブジェクトをラウンドトリップさせる方法を次の例に示します。

Const fileName As String = ".\DateWithTz.dat"

Dim tempDate As Date = #9/3/2008 7:00:00 PM#
Dim tempTz As TimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time")
Dim dateWithTz As New DateInTimeZone(New DateTimeOffset(tempDate, _
                                         tempTz.GetUtcOffset(tempDate)), _
                                     tempTz)

' Store DateInTimeZone value to a file
Dim outFile As New FileStream(fileName, FileMode.Create)
Try
   Dim formatter As New BinaryFormatter()
   formatter.Serialize(outFile, dateWithTz)
   Console.WriteLine("Saving {0} {1} to {2}", dateWithTz.DateAndTime, _
           IIf(dateWithTz.TimeZone.IsDaylightSavingTime(dateWithTz.DateAndTime), _
               dateWithTz.TimeZone.DaylightName, dateWithTz.TimeZone.DaylightName), _
           fileName)
Catch e As SerializationException
   Console.WriteLine("Unable to serialize time data to {0}.", fileName)
Finally   
   outFile.Close()
End Try

' Retrieve DateInTimeZone value
If File.Exists(fileName) Then
   Dim inFile As New FileStream(fileName, FileMode.Open)
   Dim dateWithTz2 As New DateInTimeZone()
   Try
      Dim formatter As New BinaryFormatter()
      dateWithTz2 = DirectCast(formatter.Deserialize(inFile), DateInTimeZone)         
      Console.WriteLine("Restored {0} {1} from {2}", dateWithTz2.DateAndTime, _
                        IIf(dateWithTz2.TimeZone.IsDaylightSavingTime(dateWithTz2.DateAndTime), _
                        dateWithTz2.TimeZone.DaylightName, dateWithTz2.TimeZone.DaylightName), _
                        fileName)
   Catch e As SerializationException
      Console.WriteLine("Unable to retrieve date and time information from {0}", _
                        fileName)
   Finally            
      inFile.Close
   End Try
End If
' This example displays the following output to the console:
'    Saving 9/3/2008 7:00:00 PM -05:00 Central Daylight Time to .\DateWithTz.dat
'    Restored 9/3/2008 7:00:00 PM -05:00 Central Daylight Time from .\DateWithTz.dat      
const string fileName = @".\DateWithTz.dat";

DateTime tempDate = new DateTime(2008, 9, 3, 19, 0, 0);
TimeZoneInfo tempTz = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
DateInTimeZone dateWithTz = new DateInTimeZone(new DateTimeOffset(tempDate, 
                                tempTz.GetUtcOffset(tempDate)), 
                                tempTz);

// Store DateInTimeZone value to a file
FileStream outFile = new FileStream(fileName, FileMode.Create);
try
{
   BinaryFormatter formatter = new BinaryFormatter();
   formatter.Serialize(outFile, dateWithTz);
   Console.WriteLine("Saving {0} {1} to {2}", dateWithTz.DateAndTime, 
                     dateWithTz.TimeZone.IsDaylightSavingTime(dateWithTz.DateAndTime) ? 
                     dateWithTz.TimeZone.DaylightName : dateWithTz.TimeZone.DaylightName, 
                     fileName);
}
catch (SerializationException)
{
   Console.WriteLine("Unable to serialize time data to {0}.", fileName);
}
finally
{         
   outFile.Close();
}

// Retrieve DateInTimeZone value
if (File.Exists(fileName))
{
   FileStream inFile = new FileStream(fileName, FileMode.Open);
   DateInTimeZone dateWithTz2 = new DateInTimeZone();
   try
   {
      BinaryFormatter formatter = new BinaryFormatter();
      dateWithTz2 = formatter.Deserialize(inFile) as DateInTimeZone;   
      Console.WriteLine("Restored {0} {1} from {2}", dateWithTz2.DateAndTime, 
                        dateWithTz2.TimeZone.IsDaylightSavingTime(dateWithTz2.DateAndTime) ? 
                        dateWithTz2.TimeZone.DaylightName : dateWithTz2.TimeZone.DaylightName, 
                        fileName);
   }
   catch (SerializationException)
   {
      Console.WriteLine("Unable to retrieve date and time information from {0}", 
                        fileName);
   }
   finally
   {
      inFile.Close();
   }
}
// This example displays the following output to the console:
//    Saving 9/3/2008 7:00:00 PM -05:00 Central Daylight Time to .\DateWithTz.dat
//    Restored 9/3/2008 7:00:00 PM -05:00 Central Daylight Time from .\DateWithTz.dat      

この方法では、日付、時刻、およびタイム ゾーンの組み合わせを保持するオブジェクトが、日付値とタイム ゾーン値を確実に同期するように実装されている場合、保存と復元の前後で正確な時点が常に明確に反映されます。

コードのコンパイル

これらの例では、次の項目が必要です。

参照

概念

書式設定操作の実行

DateTime、DateTimeOffset、および TimeZoneInfo の使い分け

標準の日付と時刻の書式指定文字列