Converting times between time zones

It's becoming increasingly important for any application that works with dates and times to handle differences between time zones. An application can no longer assume that all times can be expressed in the local time, which is the time available from the DateTime structure. For example, a web page that displays the current time in the eastern part of the United States will lack credibility to a customer in eastern Asia. This article explains how to convert times from one time zone to another and convert DateTimeOffset values that have limited time zone awareness.

Converting to Coordinated Universal Time

Coordinated Universal Time (UTC) is a high-precision, atomic time standard. The world's time zones are expressed as positive or negative offsets from UTC. Thus, UTC provides a time-zone free or time-zone neutral time. The use of UTC is recommended when a date and time's portability across computers is important. For details and other best practices using dates and times, see Coding best practices using DateTime in the .NET Framework. Converting individual time zones to UTC makes time comparisons easy.

Note

You can also serialize a DateTimeOffset structure to represent a single point in time unambiguously. Because DateTimeOffset objects store a date and time value along with its offset from UTC, they always represent a particular point in time in relation to UTC.

The easiest way to convert a time to UTC is to call the static (Shared in Visual Basic) TimeZoneInfo.ConvertTimeToUtc(DateTime) method. The exact conversion performed by the method depends on the value of the dateTime parameter's Kind property, as the following table shows:

DateTime.Kind Conversion
DateTimeKind.Local Converts local time to UTC.
DateTimeKind.Unspecified Assumes the dateTime parameter is local time and converts local time to UTC.
DateTimeKind.Utc Returns the dateTime parameter unchanged.

The following code converts the current local time to UTC and displays the result to the console:

DateTime dateNow = DateTime.Now;
Console.WriteLine($"The date and time are {TimeZoneInfo.ConvertTimeToUtc(dateNow)} UTC.");
Dim dateNow As Date = Date.Now
Console.WriteLine("The date and time are {0} UTC.", _
                  TimeZoneInfo.ConvertTimeToUtc(dateNow))

If the date and time value doesn't represent the local time or UTC, the ToUniversalTime method will likely return an erroneous result. However, you can use the TimeZoneInfo.ConvertTimeToUtc method to convert the date and time from a specified time zone. For details on retrieving a TimeZoneInfo object that represents the destination time zone, see Finding the time zones defined on a local system. The following code uses the TimeZoneInfo.ConvertTimeToUtc method to convert Eastern Standard Time to UTC:

DateTime easternTime = new DateTime(2007, 01, 02, 12, 16, 00);
string easternZoneId = "Eastern Standard Time";
try
{
    TimeZoneInfo easternZone = TimeZoneInfo.FindSystemTimeZoneById(easternZoneId);
    Console.WriteLine($"The date and time are {TimeZoneInfo.ConvertTimeToUtc(easternTime, easternZone)} UTC.");
}
catch (TimeZoneNotFoundException)
{
    Console.WriteLine($"Unable to find the {easternZoneId} zone in the registry.");
}
catch (InvalidTimeZoneException)
{
    Console.WriteLine($"Registry data on the {easternZoneId} zone has been corrupted.");
}
Dim easternTime As New Date(2007, 01, 02, 12, 16, 00)
Dim easternZoneId As String = "Eastern Standard Time"
Try
    Dim easternZone As TimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(easternZoneId)
    Console.WriteLine("The date and time are {0} UTC.", _
                      TimeZoneInfo.ConvertTimeToUtc(easternTime, easternZone))
Catch e As TimeZoneNotFoundException
    Console.WriteLine("Unable to find the {0} zone in the registry.", _
                      easternZoneId)
Catch e As InvalidTimeZoneException
    Console.WriteLine("Registry data on the {0} zone has been corrupted.", _
                      easternZoneId)
End Try

The TimeZoneInfo.ConvertTimeToUtc method throws an ArgumentException if the DateTime object's Kind property and the time zone are mismatched. A mismatch occurs if the Kind property is DateTimeKind.Local but the TimeZoneInfo object doesn't represent the local time zone, or if the Kind property is DateTimeKind.Utc but the TimeZoneInfo object doesn't equal TimeZoneInfo.Utc.

All of these methods take DateTime values as parameters and return a DateTime value. For DateTimeOffset values, the DateTimeOffset structure has a ToUniversalTime instance method that converts the date and time of the current instance to UTC. The following example calls the ToUniversalTime method to convert a local time and several other times to UTC:

DateTimeOffset localTime, otherTime, universalTime;

// Define local time in local time zone
localTime = new DateTimeOffset(new DateTime(2007, 6, 15, 12, 0, 0));
Console.WriteLine("Local time: {0}", localTime);
Console.WriteLine();

// Convert local time to offset 0 and assign to otherTime
otherTime = localTime.ToOffset(TimeSpan.Zero);
Console.WriteLine("Other time: {0}", otherTime);
Console.WriteLine("{0} = {1}: {2}",
                  localTime, otherTime,
                  localTime.Equals(otherTime));
Console.WriteLine("{0} exactly equals {1}: {2}",
                  localTime, otherTime,
                  localTime.EqualsExact(otherTime));
Console.WriteLine();

// Convert other time to UTC
universalTime = localTime.ToUniversalTime();
Console.WriteLine("Universal time: {0}", universalTime);
Console.WriteLine("{0} = {1}: {2}",
                  otherTime, universalTime,
                  universalTime.Equals(otherTime));
Console.WriteLine("{0} exactly equals {1}: {2}",
                  otherTime, universalTime,
                  universalTime.EqualsExact(otherTime));
Console.WriteLine();
// The example produces the following output to the console:
//    Local time: 6/15/2007 12:00:00 PM -07:00
//
//    Other time: 6/15/2007 7:00:00 PM +00:00
//    6/15/2007 12:00:00 PM -07:00 = 6/15/2007 7:00:00 PM +00:00: True
//    6/15/2007 12:00:00 PM -07:00 exactly equals 6/15/2007 7:00:00 PM +00:00: False
//
//    Universal time: 6/15/2007 7:00:00 PM +00:00
//    6/15/2007 7:00:00 PM +00:00 = 6/15/2007 7:00:00 PM +00:00: True
//    6/15/2007 7:00:00 PM +00:00 exactly equals 6/15/2007 7:00:00 PM +00:00: True
Dim localTime, otherTime, universalTime As DateTimeOffset

' Define local time in local time zone
localTime = New DateTimeOffset(#6/15/2007 12:00:00PM#)
Console.WriteLine("Local time: {0}", localTime)
Console.WriteLine()

' Convert local time to offset 0 and assign to otherTime
otherTime = localTime.ToOffset(TimeSpan.Zero)
Console.WriteLine("Other time: {0}", otherTime)
Console.WriteLine("{0} = {1}: {2}", _
                  localTime, otherTime, _
                  localTime.Equals(otherTime))
Console.WriteLine("{0} exactly equals {1}: {2}", _
                  localTime, otherTime, _
                  localTime.EqualsExact(otherTime))
Console.WriteLine()

' Convert other time to UTC
universalTime = localTime.ToUniversalTime()
Console.WriteLine("Universal time: {0}", universalTime)
Console.WriteLine("{0} = {1}: {2}", _
                  otherTime, universalTime, _
                  universalTime.Equals(otherTime))
Console.WriteLine("{0} exactly equals {1}: {2}", _
                  otherTime, universalTime, _
                  universalTime.EqualsExact(otherTime))
Console.WriteLine()
' The example produces the following output to the console:
'    Local time: 6/15/2007 12:00:00 PM -07:00
'    
'    Other time: 6/15/2007 7:00:00 PM +00:00
'    6/15/2007 12:00:00 PM -07:00 = 6/15/2007 7:00:00 PM +00:00: True
'    6/15/2007 12:00:00 PM -07:00 exactly equals 6/15/2007 7:00:00 PM +00:00: False
'    
'    Universal time: 6/15/2007 7:00:00 PM +00:00
'    6/15/2007 7:00:00 PM +00:00 = 6/15/2007 7:00:00 PM +00:00: True
'    6/15/2007 7:00:00 PM +00:00 exactly equals 6/15/2007 7:00:00 PM +00:00: True   

Converting UTC to a designated time zone

To convert UTC to local time, see the Converting UTC to local time section that follows. To convert UTC to the time in any time zone that you designate, call the ConvertTimeFromUtc method. The method takes two parameters:

  • The UTC to convert. This must be a DateTime value whose Kind property is set to Unspecified or Utc.

  • The time zone to convert the UTC to.

The following code converts UTC to Central Standard Time:

DateTime timeUtc = DateTime.UtcNow;
try
{
    TimeZoneInfo cstZone = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
    DateTime cstTime = TimeZoneInfo.ConvertTimeFromUtc(timeUtc, cstZone);
    Console.WriteLine("The date and time are {0} {1}.",
                      cstTime,
                      cstZone.IsDaylightSavingTime(cstTime) ?
                              cstZone.DaylightName : cstZone.StandardName);
}
catch (TimeZoneNotFoundException)
{
    Console.WriteLine("The registry does not define the Central Standard Time zone.");
}
catch (InvalidTimeZoneException)
{
    Console.WriteLine("Registry data on the Central Standard Time zone has been corrupted.");
}
Dim timeUtc As Date = Date.UtcNow
Try
    Dim cstZone As TimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time")
    Dim cstTime As Date = TimeZoneInfo.ConvertTimeFromUtc(timeUtc, cstZone)
    Console.WriteLine("The date and time are {0} {1}.", _
                      cstTime, _
                      IIf(cstZone.IsDaylightSavingTime(cstTime), _
                          cstZone.DaylightName, cstZone.StandardName))
Catch e As TimeZoneNotFoundException
    Console.WriteLine("The registry does not define the Central Standard Time zone.")
Catch e As InvalidTimeZoneException
    Console.WriteLine("Registry data on the Central Standard Time zone has been corrupted.")
End Try

Converting UTC to local time

To convert UTC to local time, call the ToLocalTime method of the DateTime object whose time you want to convert. The exact behavior of the method depends on the value of the object's Kind property, as the following table shows:

DateTime.Kind Conversion
DateTimeKind.Local Returns the DateTime value unchanged.
DateTimeKind.Unspecified Assumes that the DateTime value is UTC and converts the UTC to local time.
DateTimeKind.Utc Converts the DateTime value to local time.

Note

The TimeZone.ToLocalTime method behaves identically to the DateTime.ToLocalTime method. It takes a single parameter, which is the date and time value, to convert.

You can also convert the time in any designated time zone to local time by using the static (Shared in Visual Basic) TimeZoneInfo.ConvertTime method. This technique is discussed in the next section.

Converting between any two time zones

You can convert between any two time zones by using either of the following two static (Shared in Visual Basic) methods of the TimeZoneInfo class:

  • ConvertTime

    This method's parameters are the date and time value to convert, a TimeZoneInfo object that represents the time zone of the date and time value, and a TimeZoneInfo object that represents the time zone to convert the date and time value to.

  • ConvertTimeBySystemTimeZoneId

    This method's parameters are the date and time value to convert, the identifier of the date and time value's time zone, and the identifier of the time zone to convert the date and time value to.

Both methods require that the Kind property of the date and time value to convert and the TimeZoneInfo object or time zone identifier that represents its time zone correspond to one another. Otherwise, an ArgumentException is thrown. For example, if the Kind property of the date and time value is DateTimeKind.Local, an exception is thrown if the TimeZoneInfo object passed as a parameter to the method isn't equal to TimeZoneInfo.Local. An exception is also thrown if the identifier passed as a parameter to the method isn't equal to TimeZoneInfo.Local.Id.

The following example uses the ConvertTime method to convert from Hawaiian Standard Time to local time:

DateTime hwTime = new DateTime(2007, 02, 01, 08, 00, 00);
try
{
    TimeZoneInfo hwZone = TimeZoneInfo.FindSystemTimeZoneById("Hawaiian Standard Time");
    Console.WriteLine("{0} {1} is {2} local time.",
            hwTime,
            hwZone.IsDaylightSavingTime(hwTime) ? hwZone.DaylightName : hwZone.StandardName,
            TimeZoneInfo.ConvertTime(hwTime, hwZone, TimeZoneInfo.Local));
}
catch (TimeZoneNotFoundException)
{
    Console.WriteLine("The registry does not define the Hawaiian Standard Time zone.");
}
catch (InvalidTimeZoneException)
{
    Console.WriteLine("Registry data on the Hawaiian Standard Time zone has been corrupted.");
}
Dim hwTime As Date = #2/01/2007 8:00:00 AM#
Try
    Dim hwZone As TimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Hawaiian Standard Time")
    Console.WriteLine("{0} {1} is {2} local time.", _
                      hwTime, _
                      IIf(hwZone.IsDaylightSavingTime(hwTime), hwZone.DaylightName, hwZone.StandardName), _
                      TimeZoneInfo.ConvertTime(hwTime, hwZone, TimeZoneInfo.Local))
Catch e As TimeZoneNotFoundException
    Console.WriteLine("The registry does not define the Hawaiian Standard Time zone.")
Catch e As InvalidTimeZoneException
    Console.WriteLine("Registry data on the Hawaiian Standard Time zone has been corrupted.")
End Try

Converting DateTimeOffset values

Date and time values represented by DateTimeOffset objects aren't fully time-zone aware because the object is disassociated from its time zone at the time it's instantiated. However, in many cases, an application simply needs to convert a date and time based on two different offsets from UTC rather than on time in particular time zones. To perform this conversion, you can call the current instance's ToOffset method. The method's single parameter is the offset of the new date and time value the method will return.

For example, if the date and time of a user request for a web page is known and is serialized as a string in the format MM/dd/yyyy hh:mm:ss zzzz, the following ReturnTimeOnServer method converts this date and time value to the date and time on the web server:

public DateTimeOffset ReturnTimeOnServer(string clientString)
{
   string format = @"M/d/yyyy H:m:s zzz";
   TimeSpan serverOffset = TimeZoneInfo.Local.GetUtcOffset(DateTimeOffset.Now);

   try
   {
      DateTimeOffset clientTime = DateTimeOffset.ParseExact(clientString, format, CultureInfo.InvariantCulture);
      DateTimeOffset serverTime = clientTime.ToOffset(serverOffset);
      return serverTime;
   }
   catch (FormatException)
   {
      return DateTimeOffset.MinValue;
   }
}
Public Function ReturnTimeOnServer(clientString As String) As DateTimeOffset
    Dim format As String = "M/d/yyyy H:m:s zzz"
    Dim serverOffset As TimeSpan = TimeZoneInfo.Local.GetUtcOffset(DateTimeOffset.Now)

    Try
        Dim clientTime As DateTimeOffset = DateTimeOffset.ParseExact(clientString, format, CultureInfo.InvariantCulture)
        Dim serverTime As DateTimeOffset = clientTime.ToOffset(serverOffset)
        Return serverTime
    Catch e As FormatException
        Return DateTimeOffset.MinValue
    End Try
End Function

If the method passes the string "9/1/2007 5:32:07 -05:00," which represents the date and time in a time zone five hours earlier than UTC, it returns "9/1/2007 3:32:07 AM -07:00" for a server located in the U.S. Pacific Standard Time zone.

The TimeZoneInfo class also includes an overload of the TimeZoneInfo.ConvertTime(DateTimeOffset, TimeZoneInfo) method that performs time zone conversions with ToOffset(TimeSpan) values. The method's parameters are a DateTimeOffset value and a reference to the time zone to which the time is to be converted. The method call returns a DateTimeOffset value. For example, the ReturnTimeOnServer method in the previous example could be rewritten as follows to call the ConvertTime(DateTimeOffset, TimeZoneInfo) method.

public DateTimeOffset ReturnTimeOnServer(string clientString)
{
   string format = @"M/d/yyyy H:m:s zzz";

   try
   {
      DateTimeOffset clientTime = DateTimeOffset.ParseExact(clientString, format,
                                  CultureInfo.InvariantCulture);
      DateTimeOffset serverTime = TimeZoneInfo.ConvertTime(clientTime,
                                  TimeZoneInfo.Local);
      return serverTime;
   }
   catch (FormatException)
   {
      return DateTimeOffset.MinValue;
   }
}
Public Function ReturnTimeOnServer(clientString As String) As DateTimeOffset
    Dim format As String = "M/d/yyyy H:m:s zzz"

    Try
        Dim clientTime As DateTimeOffset = DateTimeOffset.ParseExact(clientString, format, CultureInfo.InvariantCulture)
        Dim serverTime As DateTimeOffset = TimeZoneInfo.ConvertTime(clientTime, TimeZoneInfo.Local)
        Return serverTime
    Catch e As FormatException
        Return DateTimeOffset.MinValue
    End Try
End Function

See also