Share via

Facebook x.com LinkedIn Email

How to: Round-trip Date and Time Values

In many applications, a date and time value is intended to unambiguously identify a single point in time. This topic shows how to save and restore a DateTime value, a DateTimeOffset value, and a date and time value with time zone information so that the restored value identifies the same time as the saved value.

To round-trip a DateTime value

  1. Convert the DateTime value to its string representation by calling the DateTime.ToString(String) method with the "o" format specifier.

  2. Save the string representation of the DateTime value to a file, or pass it across a process, application domain, or machine boundary.

  3. Retrieve the string that represents the DateTime value.

  4. Call the DateTime.Parse(String, IFormatProvider, DateTimeStyles) method, and pass DateTimeStyles.RoundtripKind as the value of the styles parameter.

The following example illustrates how to round-trip a DateTime value.

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月12日T18:45:15.0000000-05:00.
' Wrote 2008年06月12日T18: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月12日T18:45:15.0000000-05:00.
// Wrote 2008年06月12日T18:45:15.0000000-05:00 to .\DateFile.txt.
// Read 6/12/2008 6:45:15 PM (Local) from .\DateFile.txt.

When round-tripping a DateTime value, this technique successfully preserves the time for all local and universal times. For example, if a local DateTime value is saved on a system in the U.S. Pacific Standard Time zone and is restored on a system in the U.S. Central Standard Time zone, the restored date and time will be two hours later than the original time, which reflects the time difference between the two time zones. However, this technique is not necessarily accurate for unspecified times. All DateTime values whose Kind property is Unspecified are treated as if they are local times. If this is not the case, the DateTime will not successfully identify the correct point in time. The workaround for this limitation is to tightly couple a date and time value with its time zone for the save and restore operation.

To round-trip a DateTimeOffset value

  1. Convert the DateTimeOffset value to its string representation by calling the DateTimeOffset.ToString(String) method with the "o" format specifier.

  2. Save the string representation of the DateTimeOffset value to a file, or pass it across a process, application domain, or machine boundary.

  3. Retrieve the string that represents the DateTimeOffset value.

  4. Call the DateTimeOffset.Parse(String, IFormatProvider, DateTimeStyles) method, and pass DateTimeStyles.RoundtripKind as the value of the styles parameter.

The following example illustrates how to round-trip a DateTimeOffset value.

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月12日T18:45:15.0000000+07:00.
' Wrote 2008年06月12日T18: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月12日T18:45:15.0000000+07:00.
// Wrote 2008年06月12日T18:45:15.0000000+07:00 to .\DateOff.txt.
// Read 6/12/2008 6:45:15 PM +07:00 from .\DateOff.txt.

This technique always unambiguously identifies a DateTimeOffset value as a single point in time. The value can then be converted to Coordinated Universal Time (UTC) by calling the DateTimeOffset.ToUniversalTime method, or it can be converted to the time in a particular time zone by calling the DateTimeOffset.ToOffset or TimeZoneInfo.ConvertTime(DateTimeOffset, TimeZoneInfo) method. The major limitation of this technique is that date and time arithmetic, when performed on a DateTimeOffset value that represents the time in a particular time zone, may not produce accurate results for that time zone. This is because when a DateTimeOffset value is instantiated, it is disassociated from its time zone. Therefore, that time zone's adjustment rules can no longer be applied when you perform date and time calculations. You can work around this problem by defining a custom type that includes both a date and time value and its accompanying time zone.

To round-trip a date and time value with its time zone

  1. Define a class or a structure with two fields. The first field is either a DateTime or a DateTimeOffset object, and the second is a TimeZoneInfo object. The following example is a simple version of such a type.

    <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. Mark the class with the SerializableAttribute attribute.

  3. Serialize the object using the BinaryFormatter.Serialize method.

  4. Restore the object using the Deserialize method.

  5. Cast (in C#) or convert (in Visual Basic) the deserialized object to an object of the appropriate type.

The following example illustrates how to round-trip an object that stores both date and time and time zone information.

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 

This technique should always unambiguously reflect the correct point of time both before and after it is saved and restored, provided that the implementation of the combined date and time and time zone object does not allow the date value to become out of sync with the time zone value.

Compiling the Code

These examples require:

See Also

Concepts

Performing Formatting Operations

Choosing Between DateTime, DateTimeOffset, and TimeZoneInfo

Standard Date and Time Format Strings


  • Last updated on 2013年02月04日