$30 off During Our Annual Pro Sale. View Details »

Djangoでタイムゾーンとうまく付き合う

mizzsugar
November 13, 2019

 Djangoでタイムゾーンとうまく付き合う

mizzsugar

November 13, 2019
Tweet

More Decks by mizzsugar

Other Decks in Programming

Transcript

  1. Djangoでタイムゾーンと
    うまく付き合う
    2019/11/13 Stapy
    @mizzsugar0425

    View Slide

  2. お前、誰よ
    ● みずきと申します。
    ● やっていること↓
    ○ 昼:データ分析のデータ基盤のデータマネジメント (GCP: 特にBigQuery)
    ○ 夜:PythonでWebアプリ開発(Pyramid, PostgreSQL, Nuxt.js, TypeScript)
    ○ つい最近まで仕事で Djagoいじってたので趣味で Django触ったりもします
    ○ Twitter -> @mizzsugar0425
    ● コーヒーと自転車が好き
    ● 今週末はDjangogirlsのコーチやります! <- 今日の話のきっかけ

    View Slide

  3. DjangogirlsTutorialの復習をしていたときのこと

    View Slide

  4. しれっと使ってるdjango.utils.timezone.now
    from django.conf import settings
    from django.db import models
    from django.utils import timezone
    class Post(models.Model):
    author = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE)
    title = models.CharField(max_length=200)
    text = models.TextField()
    created_date = models.DateTimeField(default=timezone.now)
    published_date = models.DateTimeField(blank=True, null=True)
    def publish(self):
    self.published_date = timezone.now()
    self.save()
    https://tutorial.djangogirls.org/ja/django_models/

    View Slide

  5. なぜPython標準ライブラリの
    datetime.datetime.now()ではないの?

    View Slide

  6. 秘密はここにあった!
    USE_TZ = True
    settings.py

    View Slide

  7. USE_TZ=Trueだとタイムゾーンサポートが有効
    今19時
    だよー
    今10時
    だよー

    View Slide

  8. タイムゾーンサポートがあるなら
    settings.pyで TIMEZONE = ‘Asia/Tokyo’と設定している場合・・・
    ● テンプレート上で日本時間を表示してくれて便利!
    ● settings.pyのTIMEZONEを変更したらその場所の現地時間を表示
    ● 複数のタイムゾーンを使う場合、どのタイムゾーンの時間を表示するか設定す
    ればどのタイムゾーンの時間を表示するかそれぞれ振り分けてくれる※
    ※公式ドキュメントにタイムゾーンを選択して表示するための実装例があります ↓
    https://docs.djangoproject.com/ja/2.2/topics/i18n/timezones/#selecting-the-current-time-zone

    View Slide

  9. 内部ではどうなっているの?
    >>> from django.contrib.auth.models import User
    >>> from blog.models import Post
    >>>
    >>> author = User.objects.create(username='dummy', email='[email protected]')
    >>> post = Post.objects.create(
    author=author,
    title='dummy',
    text='dummy'
    )
    >>> post.publish() # 日本時間で2019/10/10 19:00:00に実行
    >>> post.published_date
    datetime.datetime(2019, 10, 10, 10, 0, 0, tzinfo=)
    19時じゃない!
    UTCって!?

    View Slide

  10. 前提知識:nativeとawareという概念
    ざっくりPythonでは
    日時を扱うdatetimeモジュールのdatetimeオブジェクトを
    ● タイムゾーンなしのdatetimeオブジェクトはnative
    ex: datetime.datetime(2019, 10, 10, 19, 0, 0)
    ● タイムゾーンありのdatetimeオブジェクトはaware
    ex: datetime.datetime(2019, 10, 10, 19, 0, 0, tzinfo=)
    と分けている
    もっと詳しい説明は公式ドキュメントで ↓
    https://docs.python.org/ja/3/library/datetime.html#aware-and-naive-objects

    View Slide

  11. UTCとは
    ● 協定世界時(Coordinated Universal Time)のこと
    ● 今の世界で標準時として使っている時間のこと
    ● 日本の場合だと、世界の標準時間より9時間早い
    https://wa3.i-3-i.info/word11831.html

    View Slide

  12. 内部的な話①
    ● テンプレート上やフォームではタイムゾーンが現地のawareなdatetimeオブジェ
    クト
    ex:2019年10月10日 19時0分0秒, datetime.datetime(2019, 10, 10, 19, 0, 0,
    tzinfo=)
    ● モデルのインスタンスのpublished_dateアトリビュートを取り出すとタイムゾーン
    がUTCのawareなdatetimeオブジェクト
    ex: datetime.datetime(2019, 10, 10, 10, 0, 0, tzinfo=)
    ● データベース上ではタイムゾーンなしの時間(値としてはUTCでの時間)
    ex:2019-10-10 10:00:00 ※PostgreSQLだと2019-10-10 10:00:00+00

    View Slide

  13. 内部的な話②
    ● Djangoでは、タイムゾーンサポートを有効にした場合、datetimeオブジェクトは
    タイムゾーンがsettings.pyのTIME_ZONEで指定した箇所のawereなdatetime
    オブジェクトとして処理される
    ● 内部的な処理にはタイムゾーンがUTCのawareなdatetimeオブジェクトを使い、
    エンドユーザー入出力を行うレイヤー(テンプレートやフォームなど)では現地時
    間でやり取りする仕組み
    ● 内部処理にタイムゾーンがUTCのdatetimeオブジェクトを使うと、複数のタイム
    ゾーンに柔軟かつ安全に変換できるのが良いという思想

    View Slide

  14. 1つのタイムゾーンでしか使わなくても
    ● 夏時間 (DST) 変換まわりのバグを防げる
    ● native -> awareに変換した際に意図しない値に変換してしまうバグを防げる
    ● Djangoがawareなdatetimeオブジェクトを想定している処理でnativeなオブジェ
    クトを使うことによるバグを防げる
    ● django-admin startprojectコマンドで作られるプロジェクトでタイムゾーンサ
    ポートありになっていることからもタイムゾーンサポートありの方がいいと主張し
    ていることがうかがえる

    View Slide

  15. datetime.datetime.now()の話に戻ると・・・
    >>> from django.utils import timezone
    >>> import datetime
    >>>
    >>> from django.utils import timezone
    >>>
    >>> # nativeなdatetimeオブジェクト
    >>> datetime.datetime.now()
    datetime.datetime(2019, 11, 12, 18, 0, 39, 452139)
    >>>
    >>> # タイムゾーンがUTCであるawareなdatetimeオブジェクト
    >>> timezone.now()
    datetime.datetime(2019, 11, 12, 9, 0, 46, 708708, tzinfo=)

    View Slide

  16. USE_TZ=Trueでnativeなオブジェクトを使うと
    RuntimeWarning: DateTimeField received a naive datetime * to while time zone support is
    active.
    Djangoから「タイムゾーンサポートが有効な時にnativeなdatetimeオブジェク
    トを使わないでください」と注意されます。

    View Slide

  17. つまりdatetime.datetime.now()は使わない訳は
    タイムゾーンサポートが有効な設定をしているから
    使っても動きはするけれども native -> awareの変換周りで苦労するやも

    View Slide

  18. まとめ
    ● Pythonのdatetimeオブジェクトにはnativeとawareという概念があり、前者
    がタイムゾーンなしで後者がタイムゾーンありの日時(ざっくり)
    ● Djangoではタイムゾーンサポートを有効にした方が安全
    ● タイムゾーンサポートを有効にした場合、Djangoでは内部的な処理ではタ
    イムゾーンがUTCの日時を使い、画面やフォームで現地時間に柔軟に変
    換できるようにしている

    View Slide

  19. もっと詳しくはブログで!
    https://mizzsugar.hatenablog.com/entry/2019/11/07/094230
    「テンプレートでは日本時間が表示さ
    れるのがわかった!では APIでは?」
    などなど具体的な話も書きました。

    View Slide

  20. ありがとうございました!

    View Slide