TL; DR
- 用語を区別する必要があります。
- UTC-ゾーンの現地時間+00:00、DST効果なし
- DateTimeOffset-UTC±NNからのオフセットを使用したローカル時間:NN。オフセットは、DST効果なしのUTCからのベースオフセットです(C#TimeZoneInfo.BaseUtcOffset)
- DateTime-タイムゾーン情報のない現地時間(Kindフラグは無視します)
- 外部と内部への使用の分割:
- API、メッセージ、ファイルのエクスポート/インポートを介した受信および送信データは、厳密にUTC(DateTime型)でなければなりません
- システム内では、データはオフセット(DateTimeOffset型)とともに保存されます
- 古いコードでの使用を非DBコード(C#、JS)とDBに分離:
- 非DBコードはローカル値(DateTimeタイプ)でのみ動作します
- DBはローカル値+オフセット(DateTimeOffset型)で動作します
- 新しいプロジェクト(コンポーネント)はDateTimeOffsetを使用します。
- データベースでは、DateTimeタイプは単純にDateTimeOffsetに変更されます。
- テーブルフィールドタイプ
- ストレージオプションで
- 互換性のない構造はコードで修正されています
- 到着した値にオフセット情報が追加されます(単純な連結)
- 非DBコードに戻る前に、値はローカルに減らされます
- DB以外のコードへの変更はありません
- DSTは、CLRストアドプロシージャを使用して解決されます(SQL Server 2016では、 AT TIME ZONEを使用できます)。
今、克服する困難についてより詳細に。
「刺繍された」標準IT産業
オフセットを使用して現地時間に日付を保存することから人々を救うのにかなり長い時間がかかりました。 しばらく前に、経験のあるプログラマーに「タイムゾーンのサポート方法」を尋ねると、唯一のオプションは「UTCを使用し、ショーの直前に現地時間に変換する」だけでした。 通常の操作では、オフセットやタイムゾーン名などの追加情報が依然として必要であるという事実は、実装の裏に隠れていました。 DateTimeOffsetの出現により、そのような詳細が明らかになりましたが、「プログラミングエクスペリエンス」の慣性により、「UTCからのベースオフセットを使用したローカル日付の保存」はUTCの保存と同じではありません。 どこでもDateTimeOffsetを使用することのもう1つの利点は、.NET FrameworkおよびSQL Serverのタイムゾーンへの準拠の制御を委任することを可能にし、システムからのデータの入力と出力の瞬間だけを人間が制御できるようにします。 人間の制御とは、日付/時刻の値を操作するためにプログラマーによって書かれたコードを意味します。
この恐怖を克服するために、私は説明、例、および概念実証を用いて複数のセッションを実施しなければなりませんでした。 プロジェクトで解決されるタスクに例が単純で近いほど、より良い結果が得られます。 「一般に」議論し始めると、これは理解の複雑化と無駄な時間の浪費につながります。 要するに:理論が少なく、実践が多い。 UTCおよびDateTimeOffsetに対する引数は、次の2つのカテゴリに起因します。
- 「常にUTC」が標準であり、残りは機能しません
- UTCはDSTの問題を解決します
UTCまたはDateTimeOffsetは、ゾーン間の変換規則に関する情報を使用せずにDSTの問題を解決しないことに注意してください。これは、C#のTimeZoneInfoクラスからアクセスできます。
簡易モデル
上記のように、古いコードでは、変更はデータベースでのみ発生します。 これがどのように機能するかは、簡単な例を使用して評価できます。
T-SQLのサンプルモデル
-- 1) -- , declare @input_user1 datetime = '2017-10-27 10:00:00' -- declare @timezoneOffset_user1 varchar(10) = '+03:00' declare @storedValue datetimeoffset -- set @storedValue = TODATETIMEOFFSET(@input_user1, @timezoneOffset_user1) -- select @storedValue 'stored' -- 2) -- 2 declare @timezoneOffset_user2 varchar(10) = '-05:00' -- -- select @storedValue 'stored value', CONVERT(DATETIME, SWITCHOFFSET(@storedValue, @timezoneOffset_user1)) 'user1 Moscow', CONVERT(DATETIME, SWITCHOFFSET(@storedValue, @timezoneOffset_user2)) 'user2 NY' -- 3) 2 declare @input_user2 datetime -- , - set @input_user2 = '2017-10-27 02:00:00.000' -- set @storedValue = TODATETIMEOFFSET(@input_user2, @timezoneOffset_user2) select @storedValue 'stored' -- 4) select @storedValue 'stored value', CONVERT(DATETIME, SWITCHOFFSET(@storedValue, @timezoneOffset_user1)) 'user1 Moscow', CONVERT(DATETIME, SWITCHOFFSET(@storedValue, @timezoneOffset_user2)) 'user2 NY'
スクリプトの結果は次のようになります。
例からわかるように、このモデルを使用すると、データベースでのみ変更を行うことができ、欠陥のリスクが大幅に削減されます。
日付/時刻値を処理するための関数の例
-- - DateTimeOffset , +00:00, , . DateTime -- DateTime DateTimeOffset, create function fn_ConcatinateWithTimeOffset(@dto datetimeoffset, @userId int) returns DateTimeOffset as begin declare @user_time_zone varchar(10) set @user_time_zone = '-05:00' -- @userId return todatetimeoffset(convert(datetime, @dto), @user_time_zone) end -- DateTimeOffset DateTime, , DateTime, create function fn_GetUserDateTime(@dto datetimeoffset, @userId int) returns DateTime as begin declare @user_time_zone varchar(10) set @user_time_zone = '-05:00' -- @userId return convert(datetime, switchoffset(@dto, @user_time_zone)) end
小さなアーティファクト
SQLコードの適応中に、DateTimeで機能するがDateTimeOffsetと互換性のないものがいくつか発見されました。
- GETDATE()+ 1はDATEADD(day、1、SYSDATETIMEOFFSET())に置き換える必要があります
- DEFAULTキーワードはDateTimeOffsetと互換性がないため、SYSDATETIMEOFFSET()を使用してください
- ISNULLコンストラクト(date_field、NULL)> 0 "DateTimeで動作しますが、DateTimeOffsetの場合、" date_field IS NOT NULL "を置き換える必要があります
結論またはUTC vs DateTimeOffset
UTCのアプローチのように、データを送受信するときに変換に取り組んでいることに気づく人がいるかもしれません。 次に、実績のある実用的なソリューションがあるのに、なぜそれがすべてなのでしょうか? これにはいくつかの理由があります。
- DateTimeOffsetを使用すると、SQL Serverの場所を忘れることができます。
- これにより、作業の一部をシステムにシフトできます。
- DateTimeOffsetがどこでも使用されている場合、データを表示または外部システムに発行する前にのみ変換を行うと、変換を最小限に抑えることができます。
これらの理由は、説明されたアプローチを使用するために重要であると思われました。 私は質問に答えて、コメントを書いてうれしいです。