koudenpaのブログ

趣味のブログです。株式会社はてなでWebアプリケーションエンジニアをやっています。職業柄IT関連の記事が多いと思います。

UTCな時刻をDateTimeKind.UnspecifiedなDateTimeにしてしまうとどんどんずれる

.NET での時刻のシリアライズで失敗したという話。

背景

.NET の DateTime 構造体 (System) は、時刻とは別に、その値が現地時刻なのか、世界協定時刻(UTC)なのか、その辺不明(Unspecified)なのかを持っている。

その辺を明示しないでDateTime構造体を構築すると、その辺不明の値になる。

その辺不明のDateTime値は大体において現地時刻相当で扱われる。 特に意識しないで時刻を扱う場合は、今いる場所の時刻だろうから、妥当な扱いだと思う。

で、DateTime構造体には DateTime.ToUniversalTime メソッド (System) という、呼べばUTCを返してくれるいい感じのメソッドがある。

やらかしたこと

  1. UTC(ISOで尻がZ)な時刻文字列を DateTime にする
  2. なんも考えないでシリアライズして永続化する
  3. なんも考えないでデシリアライズして DateTime にする
    • DateTimeKind.UnspecifiedなDateTimeになった
  4. なんも考えないで ToUniversalTime() してUTCにした気になる
    • +09:00 な時刻からUTC変換した感じに -9 時間された
  5. 2 に戻る

教訓

時刻をシリアライズするときにはタイムゾーンに関する情報を持たせるようにしましょう。

やらかすとはこういうことだ

f:id:koudenpa:20160621015641p:plainf:id:koudenpa:20160621015649p:plainf:id:koudenpa:20160621015656p:plainf:id:koudenpa:20160621015704p:plain

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            label1.Text = DateTime.UtcNow.ToString("s");
        }

        private void button1_Click(object sender, EventArgs e)
        {
            label1.Text = DateTime.Parse(label1.Text).ToUniversalTime().ToString("s");
        }
    }
}