DateTimeとDateTimeOffsetでは、時刻がローカル時刻とUTCのどちらを表すかといった表現や、その扱い方が異なります。 ここではその違いや、時刻をローカル時刻・UTCに変換する方法、その際の注意点などについて見ていきます。 また、タイムゾーン情報を扱うTimeZoneInfoクラスについても見ていきます。

なお、本文中にあるいくつかのサンプルコードについて、実行環境に設定されているタイムゾーン・言語・書式等によって実行結果・出力内容が変わるものもが存在します。 特に明記していない場合は、日本標準時(UTC+9)・日本語の環境での実行結果となります。

§1 時刻の種類・オフセット

§1.1 DateTime.Kind

DateTimeには、時刻がローカル時刻とUTCのどちらを表すのかを判別するためのプロパティKindが用意されています。 ローカル時刻であればKindプロパティの値はDateTimeKind.Local、UTCであればDateTimeKind.Utcとなります。 また、特に指定されていない場合・どちらでもない場合はDateTimeKind.Unspecifiedとなります。

DateTimeで時刻の変換を行う際にはこのプロパティの値が参照され、例えばローカル時刻またはUTCへの変換を行うToUniversalTime・ToLocalTimeメソッドでは、このKindプロパティの値に応じて適切な時刻に値が変換されます。

DateTimeのコンストラクタでは、時刻がローカル時刻・UTCのどちらを表すのか明示するためにDateTimeKindを指定することが出来ます。 また、DateTime.Nowプロパティで取得できる現在日時はDateTimeKind.Local、DateTime.UtcNowプロパティではDateTimeKind.Utcとなります。

using System;

class Sample {
  static void Main()
  {
    DateTime a = new DateTime(2013, 4, 1, 15, 0, 30, 123);                      // 日時の種類を指定しない
    DateTime b = new DateTime(2013, 4, 1, 15, 0, 30, 123, DateTimeKind.Local);  // ローカル時刻として日時を指定
    DateTime c = new DateTime(2013, 4, 1, 15, 0, 30, 123, DateTimeKind.Utc);    // UTCとして日時を指定
    DateTime d = DateTime.Now;
    DateTime e = DateTime.UtcNow;

    foreach (DateTime dt in new DateTime[] {a, b, c, d, e}) {
      Console.WriteLine("{0} {1}", dt, dt.Kind);
    }
  }
}
実行結果例
2013/04/01 15:00:30 Unspecified
2013/04/01 15:00:30 Local
2013/04/01 15:00:30 Utc
2013/04/01 15:30:05 Local
2013/04/01 6:30:05 Utc

このほか、DateTime.Parseメソッドで文字列から日時を読み取りDateTimeを作成する場合にも、文字列に指定されているタイムゾーン部分を判別して適当なDateTimeKindが設定されます。

using System;

class Sample {
  static void Main()
  {
    string a = "2013-04-01T15:00:30";       // オフセット表記の無い日時
    string b = "2013-04-01T15:00:30-05:00"; // オフセット表記のある日時
    string c = "2013-04-01T15:00:30Z";      // UTCでの日時

    foreach (string s in new string[] {a, b, c}) {
      // 文字列からDateTimeに変換
      DateTime dt = DateTime.Parse(s);

      Console.WriteLine("{0} {1}", dt, dt.Kind);
    }
    Console.WriteLine();
  }
}
実行結果
2013/04/01 15:00:30 Unspecified
2013/04/02 5:00:30 Local
2013/04/02 0:00:30 Local

なお、Parseメソッドで文字列からDateTimeを取得する場合、オプションで時刻の種類をどう扱うかを変更することが出来ます。 詳しくは日時・文字列の変換と書式 §.変換時のオプション (DateTimeStyles)で解説します。

§1.1.1 時刻の種類の変更

時刻の種類(Kindプロパティの値)はインスタンス作成時に設定された後は一切変更することが出来ません。 ですが、DateTime.SpecifyKindメソッドを使うと日時部分はそのままで、Kindプロパティの値だけを指定した値に変更したDateTimeを取得することが出来ます。 このメソッドはToUniversalTime・ToLocalTimeメソッドとは異なり、時刻の変換は行いません。

using System;

class Sample {
  static void Main()
  {
    DateTime a = new DateTime(2013, 4, 1, 15, 0, 30, 123, DateTimeKind.Local);
    DateTime b = new DateTime(2013, 4, 1, 15, 0, 30, 123, DateTimeKind.Utc);
    DateTime c = new DateTime(2013, 4, 1, 15, 0, 30, 123); // DateTimeKind.Unspecified

    DateTimeKind[] kinds = new DateTimeKind[] {
      DateTimeKind.Local,
      DateTimeKind.Utc,
      DateTimeKind.Unspecified,
    };

    foreach (DateTimeKind kind in kinds) {
      Console.WriteLine("SpecifyKind({0})", kind);

      foreach (DateTime dt in new DateTime[] {a, b, c}) {
        // SpecifyKindメソッドでKindの値を変更したDateTimeを取得する
        DateTime dtNew = DateTime.SpecifyKind(dt, kind);

        Console.WriteLine("{0} {1,-12} -> {2} {3}", dt, dt.Kind, dtNew, dtNew.Kind);
      }

      Console.WriteLine();
    }
  }
}
実行結果
SpecifyKind(Local)
2013/04/01 15:00:30 Local        -> 2013/04/01 15:00:30 Local
2013/04/01 15:00:30 Utc          -> 2013/04/01 15:00:30 Local
2013/04/01 15:00:30 Unspecified  -> 2013/04/01 15:00:30 Local

SpecifyKind(Utc)
2013/04/01 15:00:30 Local        -> 2013/04/01 15:00:30 Utc
2013/04/01 15:00:30 Utc          -> 2013/04/01 15:00:30 Utc
2013/04/01 15:00:30 Unspecified  -> 2013/04/01 15:00:30 Utc

SpecifyKind(Unspecified)
2013/04/01 15:00:30 Local        -> 2013/04/01 15:00:30 Unspecified
2013/04/01 15:00:30 Utc          -> 2013/04/01 15:00:30 Unspecified
2013/04/01 15:00:30 Unspecified  -> 2013/04/01 15:00:30 Unspecified

§1.2 DateTimeOffset.Offset

対してDateTimeOffsetでは、時刻は常にいずれかのタイムゾーンでの時刻を表す(UTCからの時差を持つ)ものとして扱われるため、時刻の種類という概念はありません。 そのかわり、タイムゾーンに対応するオフセット値を表すプロパティOffsetが用意されています。

このプロパティはUTCからの時差をTimeSpanとして表し、例えばDateTimeOffsetの表す日時がUTCの場合、Offsetプロパティの値は +00:00:00 (TimeSpan.Zero)になります。 また、実行環境の時刻が日本標準時(UTC+9)に設定されている環境の場合、DateTimeOffset.NowプロパティはOffsetに +09:00:00 が設定された値を返します。

なお、DateTimeOffset.DateTimeプロパティを参照すると、DateTimeOffsetからオフセット値部分を除いて日時部分だけにしたDateTimeを取得できます。 このプロパティで取得できるDateTimeのKindプロパティの値は、DateTimeKind.Unspecifiedとなります。

using System;

class Sample {
  static void Main()
  {
    DateTime dt = new DateTime(2013, 4, 1, 15, 0, 30, 123);
    DateTimeOffset a = new DateTimeOffset(dt, TimeSpan.Zero);   // UTC+0の日時を指定
    DateTimeOffset b = new DateTimeOffset(dt, new TimeSpan(-5, 0, 0));  // UTC-5(東部標準時)の日時を指定
    DateTimeOffset c = new DateTimeOffset(dt, new TimeSpan(+9, 0, 0));  // UTC+9(日本標準時)の日時を指定
    DateTimeOffset d = DateTimeOffset.Now;
    DateTimeOffset e = DateTimeOffset.UtcNow;

    foreach (DateTimeOffset dto in new DateTimeOffset[] {a, b, c, d, e}) {
      Console.WriteLine("{0}, {1}", dto.DateTime, dto.Offset);
    }
  }
}
実行結果例
2013/04/01 15:00:30, 00:00:00
2013/04/01 15:00:30, -05:00:00
2013/04/01 15:00:30, 09:00:00
2013/04/01 15:30:05, 09:00:00
2013/04/01 6:30:05, 00:00:00

DateTimeOffset.Parseメソッドで文字列から日時を読み取りDateTimeOffsetを作成する場合、文字列中にタイムゾーンが指定されていれば、その値がOffsetプロパティの値となります。

using System;

class Sample {
  static void Main()
  {
    string a = "2013-04-01T15:00:30";       // オフセット表記の無い日時
    string b = "2013-04-01T15:00:30-05:00"; // オフセット表記のある日時
    string c = "2013-04-01T15:00:30Z";      // UTCでの日時

    foreach (string s in new string[] {a, b, c}) {
      // 文字列からDateTimeOffsetに変換
      DateTimeOffset dto = DateTimeOffset.Parse(s);

      Console.WriteLine("{0}, {1}", dto.DateTime, dto.Offset);
    }
    Console.WriteLine();
  }
}
実行結果
2013/04/01 15:00:30, 09:00:00
2013/04/01 15:00:30, -05:00:00
2013/04/01 15:00:30, 09:00:00


§2 ローカル時刻・UTCへの変換

DateTime・DateTimeOffsetの表す時刻をローカル時刻に変換するにはToLocalTimeメソッド、UTCに変換するにはToUniversalTimeメソッドを使うことが出来ます。 このメソッドは、インスタンスの表す時刻をローカル時刻・UTCに変換した結果を返します(元のインスタンスの状態は変化しません)。 DateTime・DateTimeOffsetではローカル時刻・UTCの表現に違いがあるため、時刻の変換を行う場合にもその動作に違いがあります。

DateTimeOffsetではローカル時刻・UTCへの変換の他に、オフセット値を指定して時刻を変換した値を取得するメソッドToOffsetを使うこともできます。

§2.1 DateTime

DateTimeの表す時刻をToLocalTime・ToUniversalTimeメソッドで変換する場合、変換結果はKindプロパティの値によって変わります。 例えば、既にローカル時刻であるとされている値に対してToLocalTimeメソッドを呼び出しても得られる値は元の時刻と変わりませんが、ToUniversalTimeメソッドを呼び出せばUTCに変換された時刻が得られます。 DateTime.Kindの値とToLocalTime・ToUniversalTimeメソッドの結果は次のようになります。

DateTime.Kindの値とToLocalTime・ToUniversalメソッドの呼び出し結果
DateTime.Kindの値 ToLocalTimeの結果 ToUniversalTimeの結果
DateTimeKind.Local 元の値と同じ UTCに変換された値
DateTimeKind.Utc ローカル時刻に変換された値 元の値と同じ
DateTimeKind.Unspecified ローカル時刻に変換された値 UTCに変換された値

なお、ToLocalTimeメソッドが返すDateTimeのKindプロパティは当然DateTimeKind.Localとなります。 同様にToUniversalTimeはKindがDateTimeKind.UtcのDateTimeを返します。

以下の例はDateTimeKindが異なる日時のDateTimeに対してToLocalTime・ToUniversalTimeメソッドを呼び出した結果の違いを表示するものです。 なお、実行結果はタイムゾーンにUTC+9(日本標準時)が設定されている環境でのものです。

using System;

class Sample {
  static void Main()
  {
    DateTime a = new DateTime(2013, 4, 1, 15, 0, 30, 123, DateTimeKind.Local);
    DateTime b = new DateTime(2013, 4, 1, 15, 0, 30, 123, DateTimeKind.Utc);
    DateTime c = new DateTime(2013, 4, 1, 15, 0, 30, 123); // DateTimeKind.Unspecified

    Console.WriteLine("[ToLocalTime]");
    foreach (DateTime dt in new DateTime[] {a, b, c}) {
      DateTime local = dt.ToLocalTime(); // 時刻をローカル時刻に変換

      Console.WriteLine("{0} {1,-12} -> {2} {3}", dt, dt.Kind, local, local.Kind);
    }
    Console.WriteLine();

    Console.WriteLine("[ToUniversalTime]");
    foreach (DateTime dt in new DateTime[] {a, b, c}) {
      DateTime utc = dt.ToUniversalTime(); // 時刻をUTCに変換

      Console.WriteLine("{0} {1,-12} -> {2} {3}", dt, dt.Kind, utc, utc.Kind);
    }
  }
}
UTC+9での実行結果
[ToLocalTime]
2013/04/01 15:00:30 Local        -> 2013/04/01 15:00:30 Local
2013/04/01 15:00:30 Utc          -> 2013/04/02 0:00:30 Local
2013/04/01 15:00:30 Unspecified  -> 2013/04/02 0:00:30 Local

[ToUniversalTime]
2013/04/01 15:00:30 Local        -> 2013/04/01 6:00:30 Utc
2013/04/01 15:00:30 Utc          -> 2013/04/01 15:00:30 Utc
2013/04/01 15:00:30 Unspecified  -> 2013/04/01 6:00:30 Utc

日時の変換を行わずにKindプロパティの設定値のみを変更したい場合は、SpecifyKindメソッドを使います。

§2.2 DateTimeOffset

DateTimeOffsetでは、LocalDateTimeおよびUtcDateTimeプロパティを参照することでローカル時刻・UTCでの値を取得することが出来ます。 ただし、このプロパティではDateTimeでの値が返さるため、オフセット情報は消失します。 LocalDateTimeプロパティではDateTime.KindプロパティにDateTimeKind.Local、UtcDateTimeプロパティではDateTimeKind.UtcがセットされたDateTimeが返されます。

using System;

class Sample {
  static void Main()
  {
    DateTimeOffset dto = new DateTimeOffset(2013, 4, 1, 15, 0, 30, 123, new TimeSpan(-5, 0, 0)); // UTC-5(EST、東部標準時)での時刻

    Console.WriteLine(dto);
    Console.WriteLine(dto.LocalDateTime);
    Console.WriteLine(dto.UtcDateTime);
  }
}
UTC+9での実行結果
2013/04/01 15:00:30 -05:00
2013/04/02 5:00:30
2013/04/01 20:00:30

もちろん、DateTimeOffsetでも、DateTimeの場合と同様にToLocalTime・ToUniversalTimeメソッドで時刻を変換することができます。 さらに、ToOffsetメソッドで任意のオフセット値に変更して時刻を変換することも出来ます。

ToLocalTimeメソッドが返すDateTimeOffsetのOffsetプロパティは当然ローカル時刻のオフセット値と同じになります。 同様にToUniversalTimeはOffsetが 00:00:00 のDateTimeOffsetを返し、ToOffsetは指定したオフセット値が設定されたDateTimeOffsetを返します。

以下の例は、オフセット値が異なる日時のDateTimeOffsetに対してToLocalTime・ToUniversalTime・ToOffsetの各メソッドを呼び出した結果の違いを表示するものです。 なお、実行結果はタイムゾーンにUTC+9(日本標準時)が設定されている環境でのものです。

using System;

class Sample {
  static void Main()
  {
    DateTimeOffset a = new DateTimeOffset(2013, 4, 1, 15, 0, 30, 123, new TimeSpan(+9, 0, 0));
    DateTimeOffset b = new DateTimeOffset(2013, 4, 1, 15, 0, 30, 123, new TimeSpan(-5, 0, 0));
    DateTimeOffset c = new DateTimeOffset(2013, 4, 1, 15, 0, 30, 123, TimeSpan.Zero);

    Console.WriteLine("[ToLocalTime]");
    foreach (DateTimeOffset dto in new DateTimeOffset[] {a, b, c}) {
      DateTimeOffset local = dto.ToLocalTime(); // 時刻をローカル時刻に変換

      Console.WriteLine("{0} -> {1}", dto, local);
    }
    Console.WriteLine();

    Console.WriteLine("[ToUniversalTime]");
    foreach (DateTimeOffset dto in new DateTimeOffset[] {a, b, c}) {
      DateTimeOffset utc = dto.ToUniversalTime();

      Console.WriteLine("{0} -> {1}", dto, utc); // 時刻をUTCに変換
    }
    Console.WriteLine();

    Console.WriteLine("[ToOffset]");
    foreach (DateTimeOffset dto in new DateTimeOffset[] {a, b, c}) {
      DateTimeOffset est = dto.ToOffset(new TimeSpan(-5, 0, 0)); // 時刻をUTC-5(EST、東部標準時)に変換

      Console.WriteLine("{0} -> {1}", dto, est);
    }
  }
}
UTC+9での実行結果
[ToLocalTime]
2013/04/01 15:00:30 +09:00 -> 2013/04/01 15:00:30 +09:00
2013/04/01 15:00:30 -05:00 -> 2013/04/02 5:00:30 +09:00
2013/04/01 15:00:30 +00:00 -> 2013/04/02 0:00:30 +09:00

[ToUniversalTime]
2013/04/01 15:00:30 +09:00 -> 2013/04/01 6:00:30 +00:00
2013/04/01 15:00:30 -05:00 -> 2013/04/01 20:00:30 +00:00
2013/04/01 15:00:30 +00:00 -> 2013/04/01 15:00:30 +00:00

[ToOffset]
2013/04/01 15:00:30 +09:00 -> 2013/04/01 1:00:30 -05:00
2013/04/01 15:00:30 -05:00 -> 2013/04/01 15:00:30 -05:00
2013/04/01 15:00:30 +00:00 -> 2013/04/01 10:00:30 -05:00

なお、ToLocalTimeメソッドでは、実行環境に設定されているタイムゾーンに従って夏時間等の時間調整を考慮した変換が行われます。 一方、ToOffsetメソッドでは、単に指定されたオフセット値への変換のみが行われ、夏時間等の考慮はされません。 夏時間等の調整が必要なタイムゾーンへの変換を行う場合は、TimeZoneInfo.ConvertTimeメソッドを使う必要があります。

§3 TimeZoneInfo

TimeZoneInfoクラスは、.NET Framework 3.5から導入されたクラスです。 TimeZoneInfoでは特定のタイムゾーンに関する情報を扱い、日時をそのタイムゾーンでのものに変換するメソッドなどが用意されています。

§3.1 タイムゾーンとその情報の取得

TimeZoneInfoクラスでは、タイムゾーンの名称(StandardNameプロパティ)、タイムゾーンにおける標準時とUTCとの差(オフセット値、BaseUtcOffsetプロパティ)、タイムゾーンに夏時間が存在するかどうか(SupportsDaylightSavingTimeプロパティ)、といった情報を参照することが出来ます。

次の例では、実行環境に設定されているタイムゾーンをLocalプロパティで取得し、その情報を表示しています。

using System;

class Sample {
  static void Main()
  {
    // ローカルタイムゾーンの情報を取得
    TimeZoneInfo local = TimeZoneInfo.Local;

    Console.WriteLine("StandardName: {0}", local.StandardName);
    Console.WriteLine("DisplayName: {0}", local.DisplayName);
    Console.WriteLine("BaseUtcOffset: {0}", local.BaseUtcOffset);
    Console.WriteLine("SupportsDaylightSavingTime: {0}", local.SupportsDaylightSavingTime);
  }
}
.NET Framework・タイムゾーンが「大阪、札幌、東京」での実行結果例
StandardName: 東京 (標準時)
DisplayName: (UTC+09:00) 大阪、札幌、東京
BaseUtcOffset: 09:00:00
SupportsDaylightSavingTime: False
Mono・Asia/Tokyoでの実行結果例
StandardName: JST
DisplayName: Local
BaseUtcOffset: 09:00:00
SupportsDaylightSavingTime: True

ローカルタイムゾーン以外を表すTimeZoneInfoを取得したい場合は、FindSystemTimeZoneByIdメソッドに目的のタイムゾーンのIDを指定して取得します。 Windowsでは、レジストリに格納されている情報を元にTimeZoneInfoが取得されます。 FindSystemTimeZoneByIdメソッドでは、該当するタイムゾーンが見つからない場合、例外TimeZoneNotFoundExceptionがスローされます。

このほか、Utcプロパティを参照すれば、UTCのタイムゾーンを表すTimeZoneInfoを取得することが出来ます。

using System;

class Sample {
  static void Main()
  {
    TimeZoneInfo[] timezones = new TimeZoneInfo[] {
      TimeZoneInfo.Local,
      TimeZoneInfo.Utc,
    };

    foreach (TimeZoneInfo tz in timezones) {
      Console.WriteLine("{0} ({1}) {2}", tz.StandardName, tz.BaseUtcOffset, tz.DisplayName);
    }
    Console.WriteLine();

    // IDからTimeZoneInfoを取得
    string[] ids = new string[] {
      "JST", "Tokyo Standard Time", "Asia/Tokyo",
      "EST", "Eastern Standard Time", "America/New_York",
      "GMT", "GMT Standard Time", "Europe/London",
    };

    foreach (string id in ids) {
      Console.Write("{0,-25} -> ", id);

      try {
        TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById(id);

        Console.WriteLine("{0} ({1}) {2}", tz.StandardName, tz.BaseUtcOffset, tz.DisplayName);
      }
      catch (TimeZoneNotFoundException) {
        Console.WriteLine("(time zone not found)");
      }
    }
  }
}
.NET Framewor 4・Windows 7での実行結果
東京 (標準時) (09:00:00) (UTC+09:00) 大阪、札幌、東京
UTC (00:00:00) UTC

JST                       -> (time zone not found)
Tokyo Standard Time       -> 東京 (標準時) (09:00:00) (UTC+09:00) 大阪、札幌、東京
Asia/Tokyo                -> (time zone not found)
EST                       -> (time zone not found)
Eastern Standard Time     -> 東部標準時 (-05:00:00) (UTC-05:00) 東部標準時 (米国およびカナダ)
America/New_York          -> (time zone not found)
GMT                       -> (time zone not found)
GMT Standard Time         -> GMT 標準時 (00:00:00) (UTC) ダブリン、エジンバラ、リスボン、ロンドン
Europe/London             -> (time zone not found)
Mono・Ubuntu 12.04での実行結果
JST (09:00:00) Local
UTC (00:00:00) UTC

JST                       -> (time zone not found)
Tokyo Standard Time       -> (time zone not found)
Asia/Tokyo                -> JST (09:00:00) Asia/Tokyo
EST                       -> EST (-05:00:00) EST
Eastern Standard Time     -> (time zone not found)
America/New_York          -> EST (-05:00:00) America/New_York
GMT                       -> GMT (00:00:00) GMT
GMT Standard Time         -> (time zone not found)
Europe/London             -> GMT (00:00:00) Europe/London

なお、システムで使用可能なすべてのタイムゾーンを取得するには、GetSystemTimeZonesメソッドを使います。

§3.2 日時の変換

ConvertTimeメソッドを使うと、DateTime・DateTimeOffsetの値を異なるタイムゾーンでの日時に変換することが出来ます。 DateTimeOffset.Offsetメソッドではオフセット値の変更はできますが、この際、夏時間などタイムゾーン内での時間調整は一切行われません。 一方TimeZoneInfo.ConvertTimeメソッドでは、変換に際してそのタイムゾーン内での時刻の変換規則に基づいた時間調整が行われます。 DateTimeOffsetだけでなく、DateTimeに対しても同様に時間調整が行われます。

なお、あるタイムゾーンにて日時が夏時間の期間中かどうかを調べるには、IsDaylightSavingTimeメソッドが使えます。

using System;

class Sample {
  static void Main()
  {
    // 東部標準時のTimeZoneInfoを取得
    TimeZoneInfo est = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");

    // 冬期・夏期の日付 (DateTime)
    DateTime dtWinter = new DateTime(2013, 1, 1, 12, 0, 0, DateTimeKind.Local);
    DateTime dtSummer = new DateTime(2013, 7, 1, 12, 0, 0, DateTimeKind.Local);

    Console.WriteLine("{0} -> {1} {2}",
                      dtWinter,
                      est.IsDaylightSavingTime(dtWinter) ? est.DaylightName : est.StandardName,
                      TimeZoneInfo.ConvertTime(dtWinter, est));
    Console.WriteLine("{0} -> {1} {2}",
                      dtSummer,
                      est.IsDaylightSavingTime(dtSummer) ? est.DaylightName : est.StandardName,
                      TimeZoneInfo.ConvertTime(dtSummer, est));
    Console.WriteLine();

    // 冬期・夏期の日付 (DateTimeOffset)
    DateTimeOffset dtoWinter = new DateTimeOffset(2013, 1, 1, 12, 0, 0, TimeZoneInfo.Local.BaseUtcOffset);
    DateTimeOffset dtoSummer = new DateTimeOffset(2013, 7, 1, 12, 0, 0, TimeZoneInfo.Local.BaseUtcOffset);

    Console.WriteLine("{0} -> {1} {2}",
                      dtoWinter,
                      est.IsDaylightSavingTime(dtoWinter) ? est.DaylightName : est.StandardName,
                      TimeZoneInfo.ConvertTime(dtoWinter, est));
    Console.WriteLine("{0} -> {1} {2}",
                      dtoSummer,
                      est.IsDaylightSavingTime(dtoSummer) ? est.DaylightName : est.StandardName,
                      TimeZoneInfo.ConvertTime(dtoSummer, est));
  }
}
日本標準時が設定されている環境での実行結果
2013/01/01 12:00:00 -> 東部標準時 2012/12/31 22:00:00
2013/07/01 12:00:00 -> 東部夏時間 2013/06/30 23:00:00

2013/01/01 12:00:00 +09:00 -> 東部標準時 2012/12/31 22:00:00 -05:00
2013/07/01 12:00:00 +09:00 -> 東部夏時間 2013/06/30 23:00:00 -04:00

さらに、ConvertTimeBySystemTimeZoneIdメソッドを使えば、目的のタイムゾーンを表すTimeZoneInfoの取得を省略することが出来ます。 このメソッドは、FindSystemTimeZoneByIdメソッドとConvertTimeメソッドの組み合わせと同等であるため、呼び出しが1度であるならこちらのメソッドを使った方が記述が少なくなります。

using System;

class Sample {
  static void Main()
  {
    // 東部標準時のID
    const string est = "Eastern Standard Time";

    // 冬期・夏期の日付 (DateTime)
    DateTime dtWinter = new DateTime(2013, 1, 1, 12, 0, 0, DateTimeKind.Local);
    DateTime dtSummer = new DateTime(2013, 7, 1, 12, 0, 0, DateTimeKind.Local);

    Console.WriteLine("{0} -> {1}",
                      dtWinter,
                      TimeZoneInfo.ConvertTimeBySystemTimeZoneId(dtWinter, est));
    Console.WriteLine("{0} -> {1}",
                      dtSummer,
                      TimeZoneInfo.ConvertTimeBySystemTimeZoneId(dtSummer, est));
    Console.WriteLine();

    // 冬期・夏期の日付 (DateTimeOffset)
    DateTimeOffset dtoWinter = new DateTimeOffset(2013, 1, 1, 12, 0, 0, TimeZoneInfo.Local.BaseUtcOffset);
    DateTimeOffset dtoSummer = new DateTimeOffset(2013, 7, 1, 12, 0, 0, TimeZoneInfo.Local.BaseUtcOffset);

    Console.WriteLine("{0} -> {1}",
                      dtoWinter,
                      TimeZoneInfo.ConvertTimeBySystemTimeZoneId(dtoWinter, est));
    Console.WriteLine("{0} -> {1}",
                      dtoSummer,
                      TimeZoneInfo.ConvertTimeBySystemTimeZoneId(dtoSummer, est));
  }
}
日本標準時が設定されている環境での実行結果
2013/01/01 12:00:00 -> 2012/12/31 22:00:00
2013/07/01 12:00:00 -> 2013/06/30 23:00:00

2013/01/01 12:00:00 +09:00 -> 2012/12/31 22:00:00 -05:00
2013/07/01 12:00:00 +09:00 -> 2013/06/30 23:00:00 -04:00