サマータイムの調整時間をTimeZoneInfo.ConvertTimeで変換した
サマータイムについて話をしていて、仮に実施されるとなると結局UTCオフセット時間ありきで考えるのが良い*1という話になり、 既存のDateTime型データはTimeSpanを使用してDateTimeOffset型として扱うという方針でその場が何となく纏まりました。 その話の流れから、そもそも調整範囲の時間を変換するとどういう動きをするのか見てみたかったので試してみました。
環境
タイムゾーンが日本(UTC+09:00)だとサマータイムが設定されていないので、 ローカル環境のタイムゾーンを太平洋標準時(UTC-08:00)に変更しました。
太平洋標準時でのサマータイム
期間は2007年以降3月の第2日曜日午前2:00から11月の第1日曜日午前2:00の間、 適用期間中は調整時間として1時間早く進み、期間終了時に1時間巻き戻るようになっています。 2018年は3月11日(日)午前2:00から11月4日(日)午前2:00までのあいだ適用されます。
TimeZoneInfo
タイムゾーンを太平洋標準時に設定しているのでTimeZoneInfoで取得できるローカルの情報は(UTC-08:00) 太平洋標準時です。
TimeZoneInfo timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(TimeZoneInfo.Local.Id);
名前 | 値 |
---|---|
timeZoneInfo | (UTC-08:00) 太平洋標準時 (米国およびカナダ) |
BaseUtcOffset | -08:00:00 |
DaylightName | 太平洋夏時間 |
DisplayName | (UTC-08:00) 太平洋標準時 (米国およびカナダ) |
Id | Pacific Standard Time |
StandardName | 太平洋標準時 |
SupportsDaylightSavingTime | true |
SupportsDaylightSavingTimeプロパティは指定したタイムゾーンでのサマータイムの設定有無になります。 設定がある場合はTimeZoneInfo.GetAdjustmentRulesでサマータイム期間内の調整情報を取得することができます。 現時点(2018/8)で太平洋標準時のGetAdjustmentRulesの結果は2件取得できます。 これは2007年にサマータイムの適用ルールが変更されているためです。
また、現時点日本標準時でサマータイムは導入されていないのでTimeZoneInfo.SupportsDaylightSavingTimeはfalse、TimeZoneInfo.GetAdjustmentRulesは存在しません。
TimeZoneInfo.ConvertTime
2018年3月11日 02:00:00から2:59:59の間をDateTimeに指定してTimeZoneInfo.ConvertTimeを実行したところ、 DateTimeKindがLocalとUnspecifiedの値を持つオブジェクトは例外を出力しました。 太平洋標準時のサマータイムの調整情報が適用され、存在しない時刻として判断されるようです。
System.ArgumentException: '指定された DateTime は無効な時間を表しています。 たとえば、時計を進めると、進めた分の時間が無効になります。
DateTimeKindがUtcのデータは、DateTimeオブジェクトを宣言した際に世界標準時として2018/3/11 2:00:00を設定しているので、 TimeZoneInfo.ConvertTimeを実行したときにローカルタイムゾーンの太平洋標準時(UTC-08:00)との差分が適用され、8時間巻き戻った結果が出力されています。
// 例外が発生する DateTime dateTimeLocalConvert = TimeZoneInfo.ConvertTime(dateTimeLocal, timeZoneInfo); // UTC上の2018/3/11 2:00:00からローカルタイムゾーンの太平洋標準時(UTC-08:00)分巻き戻った時刻が出力される DateTime dateTimeUtcConvert = TimeZoneInfo.ConvertTime(dateTimeUtc, timeZoneInfo); // 例外が発生する DateTime dateTimeUnspecifiedConvert = TimeZoneInfo.ConvertTime(dateTimeUnspecified, timeZoneInfo);
名前 | 値(UTC) |
---|---|
dateTimeUtcConvert | {2018/03/10 18:00:00} |
Date | {2018/03/10 0:00:00} |
Day | 10 |
DayOfWeek | Saturday |
DayOfYear | 69 |
Hour | 18 |
Kind | Unspecified |
Millisecond | 0 |
Minute | 0 |
Month | 3 |
Second | 0 |
Ticks | 636563016000000000 |
TimeOfDay | {18:00:00} |
Year | 2018 |
同様に時刻を3:00に変更して結果を見てみます。2018/03/11 03:00:00はサマータイム適用後にも存在する時刻(実際は2018/03/11 2:00で1時間調整された時刻 )になるので例外は発生しませんでした。
// サマータイム適用後にも存在する時刻なので正常な結果が出力される DateTime dateTimeLocalConvert = TimeZoneInfo.ConvertTime(new DateTime(2018, 3, 11, 3, 0, 0, DateTimeKind.Local), timeZoneInfo); // UTC上の2018/3/11 2:00:00からローカルタイムゾーンの太平洋標準時(UTC-08:00)分巻き戻った時刻が出力される DateTime dateTimeUtcConvert = TimeZoneInfo.ConvertTime(new DateTime(2018, 3, 11, 3, 0, 0, DateTimeKind.Utc), timeZoneInfo); // サマータイム適用後にも存在する時刻なので正常な結果が出力される DateTime dateTimeUnspecifiedConvert = TimeZoneInfo.ConvertTime(new DateTime(2018, 3, 11, 3, 0, 0, DateTimeKind.Unspecified), timeZoneInfo);
名前 | 値(Local) | 値(Utc) | 値(Unspecified) |
---|---|---|---|
Date | {2018/03/11 0:00:00} | {2018/03/10 0:00:00} | {2018/03/11 0:00:00} |
Day | 11 | 10 | 11 |
DayOfWeek | Sunday | Saturday | Sunday |
DayOfYear | 70 | 69 | 70 |
Hour | 3 | 19 | 3 |
Kind | Unspecified | Unspecified | Unspecified |
Millisecond | 0 | 0 | 0 |
Minute | 0 | 0 | 0 |
Month | 3 | 3 | 3 |
Second | 0 | 0 | 0 |
Ticks | 636563340000000000 | 636563052000000000 | 636563340000000000 |
TimeOfDay | {03:00:00} | {19:00:00} | {03:00:00} |
Year | 2018 | 2018 | 2018 |
TimeZoneInfo.ConvertTimeで調整時間に該当する時刻を設定した場合は勝手に調整してくれるとうれしいんですがそうもいかないみたいです。
*1:あくまで某所で動いている何かの話