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

日付と正規化

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on.

 日付と正規化

Avatar for Yusuke Kawatsu

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 中では一つのタイムゾーン時刻で統一されます。