Skip to main content

Ordinarily, when you perform date and time arithmetic using System.DateTime or System.DateTimeOffset values, the result does not reflect any time zone adjustment rules. This is true even when the time zone of the date and time value is clearly identifiable (for example, when the System.DateTime.Kind property is set to System.DateTimeKind.Local). This topic shows how to perform arithmetic operations on date and time values that belong to a particular time zone. The results of the arithmetic operations will reflect the time zone’s adjustment rules.

---
title: "Use Time Zones in Date and Time Arithmetic in C#"
subtitle: date and time arithmetic operations
author: Microsoft
date: April 9, 2017
source: https://docs.microsoft.com/en-us/dotnet/standard/datetime/use-time-zones-in-arithmetic
notoc: true
---

Ordinarily, when you perform date and time arithmetic using `System.DateTime` or
`System.DateTimeOffset` values, the result does not reflect any time zone
adjustment rules. This is true even when the time zone of the date and time
value is clearly identifiable (for example, when the `System.DateTime.Kind`
property is set to `System.DateTimeKind.Local`). This topic shows how to perform
arithmetic operations on date and time values that belong to a particular time
zone. The results of the arithmetic operations will reflect the time zone's
adjustment rules.

### To apply adjustment rules to date and time arithmetic

1. Implement some method of closely coupling a date and time value with the time
   zone to which it belongs. For example, declare a structure that includes both
   the date and time value and its time zone. The following example uses this
   approach to link a `System.DateTime` value with its time zone.

    ```cs
    // Define a structure for DateTime values for internal use only
    internal struct TimeWithTimeZone
    {
       TimeZoneInfo TimeZone;
       DateTime Time;
    }
    ```

2. Convert a time to Coordinated Universal Time (UTC) by calling either the
   `System.TimeZoneInfo.ConvertTimeToUtc` method or the
   `System.TimeZoneInfo.ConvertTime` method.

3. Perform the arithmetic operation on the UTC time.

4. Convert the time from UTC to the original time's associated time zone by calling the `TimeZoneInfo.ConvertTime(DateTime, TimeZoneInfo)` method.

## Example

The following example adds two hours and thirty minutes to March 9, 2008, at
1:30 A.M. Central Standard Time. The time zone's transition to daylight saving
time occurs thirty minutes later, at 2:00 A.M. on March 9, 2008. Because the
example follows the four steps listed in the previous section, it correctly
reports the resulting time as 5:00 A.M. on March 9, 2008.

```cs
using System;

public struct TimeZoneTime
{
   public TimeZoneInfo TimeZone;
   public DateTime Time;

   public TimeZoneTime(TimeZoneInfo tz, DateTime time)
   {
      if (tz == null)
         throw new ArgumentNullException("The time zone cannot be a null reference.");

      this.TimeZone = tz;
      this.Time = time;
   }

   public TimeZoneTime AddTime(TimeSpan interval)
   {
      // Convert time to UTC
      DateTime utcTime = TimeZoneInfo.ConvertTimeToUtc(this.Time, this.TimeZone);
      // Add time interval to time
      utcTime = utcTime.Add(interval);
      // Convert time back to time in time zone
      return new TimeZoneTime(this.TimeZone, TimeZoneInfo.ConvertTime(utcTime,
                              TimeZoneInfo.Utc, this.TimeZone));
   }
}

public class TimeArithmetic
{
   public const string tzName = "Central Standard Time";

   public static void Main()
   {
      try
      {
         TimeZoneTime cstTime1, cstTime2;

         TimeZoneInfo cst = TimeZoneInfo.FindSystemTimeZoneById(tzName);
         DateTime time1 = new DateTime(2008, 3, 9, 1, 30, 0);
         TimeSpan twoAndAHalfHours = new TimeSpan(2, 30, 0);

         cstTime1 = new TimeZoneTime(cst, time1);
         cstTime2 = cstTime1.AddTime(twoAndAHalfHours);
         Console.WriteLine("{0} + {1} hours = {2}", cstTime1.Time,
                                                    twoAndAHalfHours.ToString(),
                                                    cstTime2.Time);
      }
      catch
      {
         Console.WriteLine("Unable to find {0}.", tzName);
      }
   }
}
```

Both `System.DateTime` and `System.DateTimeOffset` values are disassociated from
any time zone to which they might belong. To perform date and time arithmetic in
a way that automatically applies a time zone's adjustment rules, the time zone
to which any date and time value belongs must be immediately identifiable. This
means that a date and time and its associated time zone must be tightly coupled.
There are several ways to do this, which include the following:

* Assume that all times used in an application belong to a particular time zone.
  Although appropriate in some cases, this approach offers limited flexibility
  and possibly limited portability.
* Define a type that tightly couples a date and time with its associated time
  zone by including both as fields of the type. This approach is used in the
  code example, which defines a structure to store the date and time and the
  time zone in two member fields.

The example illustrates how to perform arithmetic operations on
`System.DateTime` values so that time zone adjustment rules are applied to the
result. However, `System.DateTimeOffset` values can be used just as easily. The
following example illustrates how the code in the original example might be
adapted to use `System.DateTimeOffset` instead of `System.DateTime` values.

```cs
using System;

public struct TimeZoneTime
{
   public TimeZoneInfo TimeZone;
   public DateTimeOffset Time;

   public TimeZoneTime(TimeZoneInfo tz, DateTimeOffset time)
   {
      if (tz == null)
         throw new ArgumentNullException("The time zone cannot be a null reference.");

      this.TimeZone = tz;
      this.Time = time;
   }

   public TimeZoneTime AddTime(TimeSpan interval)
   {
      // Convert time to UTC
      DateTimeOffset utcTime = TimeZoneInfo.ConvertTime(this.Time, TimeZoneInfo.Utc);
      // Add time interval to time
      utcTime = utcTime.Add(interval);
      // Convert time back to time in time zone
      return new TimeZoneTime(this.TimeZone, TimeZoneInfo.ConvertTime(utcTime, this.TimeZone));
   }
}

public class TimeArithmetic
{
   public const string tzName = "Central Standard Time";

   public static void Main()
   {
      try
      {
         TimeZoneTime cstTime1, cstTime2;

         TimeZoneInfo cst = TimeZoneInfo.FindSystemTimeZoneById(tzName);
         DateTime time1 = new DateTime(2008, 3, 9, 1, 30, 0);
         TimeSpan twoAndAHalfHours = new TimeSpan(2, 30, 0);

         cstTime1 = new TimeZoneTime(cst,
                        new DateTimeOffset(time1, cst.GetUtcOffset(time1)));
         cstTime2 = cstTime1.AddTime(twoAndAHalfHours);
         Console.WriteLine("{0} + {1} hours = {2}", cstTime1.Time,
                                                    twoAndAHalfHours.ToString(),
                                                    cstTime2.Time);
      }
      catch
      {
         Console.WriteLine("Unable to find {0}.", tzName);
      }
   }
}
```

Note that if this addition is simply performed on the `System.DateTimeOffset`
value without first converting it to UTC, the result reflects the correct point
in time but its offset does not reflect that of the designated time zone for
that time.

## Compiling the code

This example requires:

* That a reference to System.Core.dll be added to the project.
* That the `System` namespace be imported with the `using` statement (required in C# code).

## See also

* [Dates, times, and time zones](https://docs.microsoft.com/en-us/dotnet/standard/datetime/index)
* [Performing arithmetic operations with dates and times](https://docs.microsoft.com/en-us/dotnet/standard/datetime/performing-arithmetic-operations)