Upgrade to Pro — share decks privately, control downloads, hide ads and more …

日付と正規化

 日付と正規化

Yusuke Kawatsu

July 17, 2024
Tweet

More Decks by Yusuke Kawatsu

Other Decks in Programming

Transcript

  1. 2
 © Link and Motivation 株式会社リンクアンドモチベーション エンジニアリングマネージャー 川津 雄介 Yusuke

    Kawatsu • 前職は某複写機メーカーでエンジニアしてた • OSレイヤーからWeb/モバイルまで何でもやる • 開発室全体を横断した生産性向上に注力! https://github.com/megmogmog1965 https://qiita.com/megmogmog1965 https://twitter.com/KawatsuYusuke
  2. 【例】Python 2 時代の文字列オブジェクト Python 2 str 型 unicode 型 •

    バイナリ配列なので、元の文字エンコーディングが何か分から ない • 内部的に UCS-2 / UCS-4 で統一されている • str 型と `+` 演算する際は、自動的に unicode 型に変換する
  3. 様々な Encoding の文字列が混在する Python 2 時代のプログラム空間 Shift-JIS UTF-8 UTF-8 (bom)

    Linux OS (Locale = ja_JP.eucjp) ”文字リテラル” UTF-8 ascii "+" 演算 str 型 (*default) str 型 (*default) str 型 (*default) str 型 (*default)
  4. Shift-JIS UTF-8 様々な Encoding の文字列が混在する str1 ”あああ” str2 ”あああ” Shift-JIS

    UTF-8 `str1 + str2` をすると死ぬ 文字列が何の Encoding か? は分からない
  5. 出入口で「正規化」をするのが鉄則 Python 2 時代のプログラム空間 Shift-JIS UTF-8 UTF-8 (bom) Linux OS

    (Locale = ja_JP.eucjp) ”文字リテラル UTF-8 Unicode型 Unicode 演算のみ Unicode型 Unicode型 Unicode型 Python 3 は 言語仕様でこれを強制 入る・出る 瞬間に 絶対にする!
  6. ISO 8601 日時の国際標準表記として ISO 8601 があります。 • 国によって異なる表記揺れを統一 ◦ US:

    Tuesday, April 3rd, 2024 ◦ 日本: 2024年 4月 3日 (火) • タイムゾーン情報を持つ協定世界時 2024-01-01T12:30:45+09:00 年 月 日 時 分 秒 TIMEZONE Offset
  7. ISO 8601 以下はすべて同じ時間を表しています。 2024-01-01T00:00:00+ZZ:ZZ 年 月 日 時 分 秒

    TIMEZONE Offset 2024-01-01T00:00:00+00:00 年 月 日 時 分 秒 TIMEZONE Offset 2024-01-01T09:00:00+09:00 年 月 日 時 分 秒 TIMEZONE Offset ロンドン (深夜) ロンドン (深夜) 日本 (朝)
  8. Timezone を意識していないと... プログラムのメモリ空間 日本時間 ロンドン時間 Linux OS (ワシントンD.C., US) Time.now()

    ※紆余曲折あり、 混ざっている... サーバー 時間 ずれてる やん☠ 09:00 (表記) 00:00 (表記) 20:00 (表記) UTC +9 UTC +0 UTC -4 一度失われた offset は絶対に分からない! 前日の...
  9. あるべき理想(正規化) プログラムのメモリ空間 日本時間 UTC 時間 Linux OS (US, ワシントンD.C.) Time.now()

    offset付き時刻型 サーバー 時間 同じ基準✨ 09:00+09:00 (ISO 8601) 00:00Z (ISO 8601) 20:00-04:00 (ISO 8601) Timeオブジェクト (offset 付き) Timeオブジェクト (offset 付き) Timeオブジェクト (offset 付き) Timeオブジェクト (offset 付き) API は ISO 8601 形式のみ 出入口で 必ず正規化 UTC +9 UTC +0 UTC -4
  10. JSON API { "date": "2024/01/01", "start": "10:00", "end": "12:00" }

    { "start": "2024-01-01T10:00:00+09:00", "end": "2024-01-01T12:00:00+09:00" } API のリクエスト・レスポンスの日時パラメータは ISO 8601 に。
  11. フロントコード ISO 8601 表記の日時文字列であれば、何も考えずとも良い。 // 日本時間の朝 09:00. jst = new

    Date("2024-01-01T09:00:00+09:00") // 協定世界時 00:00. utc = new Date("2024-01-01T00:00:00+00:00") // false. jst < utc jst > utc. // true. jst.getTime() === utc.getTime()
  12. フロントコード オフセット表記がない場合は、ブラウザのロケールが尊重される。 // Mon Jan 01 2024 09:00:00 GMT+0900 (Japan

    Standard Time) jst = new Date("2024/01/01 09:00:00") // ==> 9 jst.getHours() // ==> 0 jst.getUTCHours() // true. jst.getTime() === new Date("2024-01-01T09:00:00+09:00").getTime() // false. jst.getTime() === new Date("2024-01-01T09:00:00Z").getTime()
  13. バックエンド (Ruby / Rails) プログラムの出入口で即座に正規化をすること。 • 外界からの入力の直後に Time オブジェクト正規化する ◦

    e.g. APIリクエスト、標準入力、ファイル読み込み... • 外界への出力の直前に ISO 8601 表記文字列に正規化する ◦ e.g. API レスポンス、標準出力、ファイル書き出し • DB から取り出した日時は ActiveSupport::TimeWithZone オブ ジェクトになっている。Ruby 標準の Time オブジェクト互換 • 日時表現の文字列が出入口以外で存在しないこと! jst = Time.zone.parse('2024-01-01T09:00:00+09:00') utc = Time.zone.parse('2024-01-01T00:00:00+00:00') // true. jst == utc
  14. バックエンド (Python 3) fromisoformat() で ISO 8601 文字列から datetime を生成。

    import datetime aware_utc = datetime.datetime.fromisoformat('2024-01-01T00:00:00+00:00') aware_jst = datetime.datetime.fromisoformat('2024-01-01T09:00:00+09:00') # True. aware_utc == aware_jst # tzinfo なし. (mode=naive) naive_now = datetime.datetime.now() # tzinfo あり. (mode=aware) aware_now = datetime.datetime.now(datetime.timezone.utc) # TypeError: can't compare offset-naive and offset-aware datetimes aware_utc < naive_now # True. aware_utc < aware_now datetime オブジェクトには tzinfo のあり・なしで naive / aware の2モードがあり、異なるモード間での比較演算はできない。
  15. RDB (MySQL) 一般的には次のどれかが良い。 • offset 付き DATETIME 型 ◦ 2024-01-01

    09:00:00+09:00 • UTC+0 時刻に統一した DATETIME 型 ◦ 2024-01-01 00:00:00 • UTC+0 時刻に統一した TIMESTAMP 型 ◦ 2024-01-01 00:00:00.000000 Railsでは、ActiveSupport::TimeWithZone は config.time_zone で設定したタ イムゾーンの日時に自動的に変換されて DB へ保存されます。 Ruby プログラム中で日時を文字列としては扱わず、Time 型等のオブジェクト 正規化がされていれば DB 中では一つのタイムゾーン時刻で統一されます。