In many applications, a date and time value is intended to unambiguously identify a single point in time. This article shows how to save and restore a System.DateTime value, a System.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.
Round-trip a DateTime value
Convert the System.DateTime value to its string representation by calling the System.DateTime.ToString%28System.String%29 method with the "o" format specifier.
Save the string representation of the System.DateTime value to a file, or pass it across a process, application domain, or machine boundary.
Retrieve the string that represents the System.DateTime value.
Call the System.DateTime.Parse%28System.String%2CSystem.IFormatProvider%2CSystem.Globalization.DateTimeStyles%29 method, and pass System.Globalization.DateTimeStyles.RoundtripKind as the value of the
stylesparameter.
The following example illustrates how to round-trip a System.DateTime value.
sample · csharpsamples/snippets/csharp/VS_Snippets_CLR/Formatting.HowTo.RoundTrip/cs/RoundTrip.cs1
using System;
using System.Globalization;
using System.IO;
public class Class1
{
public static void Main()
{
RoundTripDateTime();
Console.WriteLine();
RoundTripDateTimeOffset();
Console.WriteLine();
}
private static void RoundTripDateTime()
{
// <Snippet1>
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 {dateToSave.ToString()} ({dateToSave.Kind.ToString()}) to {dateString}.");
outFile.WriteLine(dateString);
Console.WriteLine($"Wrote {dateString} to {fileName}.");
outFile.Close();
// Restore DateTime value.
DateTime restoredDate;
using StreamReader inFile = new StreamReader(fileName);
dateString = inFile.ReadLine();
if (dateString is not null)
{
restoredDate = DateTime.Parse(dateString, null, DateTimeStyles.RoundtripKind);
Console.WriteLine($"Read {restoredDate.ToString()} ({restoredDate.Kind.ToString()}) from {fileName}.");
}
// 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.
// </Snippet1>
}
private static void RoundTripDateTimeOffset()
{
// <Snippet2>
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 {dateToSave.ToString()} to {dateString}.");
outFile.WriteLine(dateString);
Console.WriteLine($"Wrote {dateString} to {fileName}.");
outFile.Close();
// Restore DateTime value.
DateTimeOffset restoredDateOff;
using StreamReader inFile = new StreamReader(fileName);
dateString = inFile.ReadLine();
if (dateString is not null)
{
restoredDateOff = DateTimeOffset.Parse(dateString, null,
DateTimeStyles.RoundtripKind);
Console.WriteLine($"Read {restoredDateOff.ToString()} from {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.
// </Snippet2>
}
}
// <Snippet3>
[Serializable]
public class DateInTimeZone
{
private TimeZoneInfo? tz;
private DateTimeOffset thisDate;
public DateInTimeZone() { }
public DateInTimeZone(DateTimeOffset date, TimeZoneInfo timeZone)
{
if (timeZone is null)
throw new ArgumentNullException("timeZone", "The time zone cannot be null.");
this.thisDate = date;
this.tz = timeZone;
}
public DateTimeOffset DateAndTime
{
get
{
return thisDate;
}
set
{
if ((tz is not null) && (value.Offset != tz.GetUtcOffset(value)))
thisDate = TimeZoneInfo.ConvertTime(value, tz);
else
thisDate = value;
}
}
public TimeZoneInfo? TimeZone => tz;
}
// </Snippet3>sample · vbsamples/snippets/visualbasic/VS_Snippets_CLR/Formatting.HowTo.RoundTrip/vb/RoundTrip.vb1
' Visual Basic .NET Document
Option Strict On
Imports System.Globalization
Imports System.IO
Module modMain
Public Sub Main()
RoundTripDateTime()
Console.WriteLine()
RoundTripDateTimeOffset()
Console.WriteLine()
End Sub
Private Sub RoundTripDateTime()
' <Snippet1>
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.
' </Snippet1>
End Sub
Private Sub RoundTripDateTimeOffset()
' <Snippet2>
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.
' </Snippet2>
End Sub
End Module
' <Snippet3>
<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("timeZone", "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
' </Snippet3>When round-tripping a System.DateTime value, this technique successfully preserves the time for all local and universal times. For example, if a local System.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 System.DateTime values whose System.DateTime.Kind property is System.DateTimeKind.Unspecified are treated as if they are local times. If it's not a local time, the System.DateTime doesn't 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.
Round-trip a DateTimeOffset value
Convert the System.DateTimeOffset value to its string representation by calling the System.DateTimeOffset.ToString%28System.String%29 method with the "o" format specifier.
Save the string representation of the System.DateTimeOffset value to a file, or pass it across a process, application domain, or machine boundary.
Retrieve the string that represents the System.DateTimeOffset value.
Call the System.DateTimeOffset.Parse%28System.String%2CSystem.IFormatProvider%2CSystem.Globalization.DateTimeStyles%29 method, and pass System.Globalization.DateTimeStyles.RoundtripKind as the value of the
stylesparameter.
The following example illustrates how to round-trip a System.DateTimeOffset value.
sample · csharpsamples/snippets/csharp/VS_Snippets_CLR/Formatting.HowTo.RoundTrip/cs/RoundTrip.cs2
using System;
using System.Globalization;
using System.IO;
public class Class1
{
public static void Main()
{
RoundTripDateTime();
Console.WriteLine();
RoundTripDateTimeOffset();
Console.WriteLine();
}
private static void RoundTripDateTime()
{
// <Snippet1>
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 {dateToSave.ToString()} ({dateToSave.Kind.ToString()}) to {dateString}.");
outFile.WriteLine(dateString);
Console.WriteLine($"Wrote {dateString} to {fileName}.");
outFile.Close();
// Restore DateTime value.
DateTime restoredDate;
using StreamReader inFile = new StreamReader(fileName);
dateString = inFile.ReadLine();
if (dateString is not null)
{
restoredDate = DateTime.Parse(dateString, null, DateTimeStyles.RoundtripKind);
Console.WriteLine($"Read {restoredDate.ToString()} ({restoredDate.Kind.ToString()}) from {fileName}.");
}
// 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.
// </Snippet1>
}
private static void RoundTripDateTimeOffset()
{
// <Snippet2>
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 {dateToSave.ToString()} to {dateString}.");
outFile.WriteLine(dateString);
Console.WriteLine($"Wrote {dateString} to {fileName}.");
outFile.Close();
// Restore DateTime value.
DateTimeOffset restoredDateOff;
using StreamReader inFile = new StreamReader(fileName);
dateString = inFile.ReadLine();
if (dateString is not null)
{
restoredDateOff = DateTimeOffset.Parse(dateString, null,
DateTimeStyles.RoundtripKind);
Console.WriteLine($"Read {restoredDateOff.ToString()} from {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.
// </Snippet2>
}
}
// <Snippet3>
[Serializable]
public class DateInTimeZone
{
private TimeZoneInfo? tz;
private DateTimeOffset thisDate;
public DateInTimeZone() { }
public DateInTimeZone(DateTimeOffset date, TimeZoneInfo timeZone)
{
if (timeZone is null)
throw new ArgumentNullException("timeZone", "The time zone cannot be null.");
this.thisDate = date;
this.tz = timeZone;
}
public DateTimeOffset DateAndTime
{
get
{
return thisDate;
}
set
{
if ((tz is not null) && (value.Offset != tz.GetUtcOffset(value)))
thisDate = TimeZoneInfo.ConvertTime(value, tz);
else
thisDate = value;
}
}
public TimeZoneInfo? TimeZone => tz;
}
// </Snippet3>sample · vbsamples/snippets/visualbasic/VS_Snippets_CLR/Formatting.HowTo.RoundTrip/vb/RoundTrip.vb2
' Visual Basic .NET Document
Option Strict On
Imports System.Globalization
Imports System.IO
Module modMain
Public Sub Main()
RoundTripDateTime()
Console.WriteLine()
RoundTripDateTimeOffset()
Console.WriteLine()
End Sub
Private Sub RoundTripDateTime()
' <Snippet1>
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.
' </Snippet1>
End Sub
Private Sub RoundTripDateTimeOffset()
' <Snippet2>
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.
' </Snippet2>
End Sub
End Module
' <Snippet3>
<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("timeZone", "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
' </Snippet3>This technique always unambiguously identifies a System.DateTimeOffset value as a single point in time. The value can then be converted to Coordinated Universal Time (UTC) by calling the System.DateTimeOffset.ToUniversalTime method, or it can be converted to the time in a particular time zone by calling the System.DateTimeOffset.ToOffset or System.TimeZoneInfo.ConvertTime%28System.DateTimeOffset%2CSystem.TimeZoneInfo%29 method. The major limitation of this technique is that date and time arithmetic, when performed on a System.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 System.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.
Compile the code
These examples require that the following namespaces be imported with C# using directives or Visual Basic Imports statements:
- System (C# only)
- System.Globalization
- System.IO