Python で時間を扱うコードのテストを書いてハマったので対策を紹介します.
結論
結論としては,何も考えずにtime-machineを使うのがおすすめです.
検証
同一の時刻操作を,time-machine と freezegun それぞれで行う下記のようなテストコードを準備します.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
import datetime import logging timezone_offset = datetime.timezone(datetime.timedelta(hours=9)) def move_to(handle, hour): target_time = datetime.datetime.now(tz=timezone_offset).replace(hour=hour, minute=0, second=0) logging.info("Move to %s", target_time) handle.move_to(target_time) logging.info("Now (native) is %s", datetime.datetime.now()) logging.info("Now (aware ) is %s", datetime.datetime.now(timezone_offset)) def test_freezegun(freezer): logging.info("Current timezone is %s", datetime.datetime.now(datetime.timezone.utc).astimezone().tzinfo) move_to(freezer, 0) move_to(freezer, 1) def test_time_machine(time_machine): logging.info("Current timezone is %s", datetime.datetime.now(datetime.timezone.utc).astimezone().tzinfo) move_to(time_machine, 0) move_to(time_machine, 1) |
Pytest で実行した結果がこちら.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
tests/a.py::test_freezegun -------------------------------- live log call --------------------------------- 22:42:13 INFO [a.py:18 test_freezegun] Current timezone is tzlocal() 22:42:13 INFO [a.py:10 move_to] Move to 2024-08-19 00:00:00.984044+09:00 00:00:00 INFO [a.py:13 move_to] Now (native) is 2024-08-18 15:00:00.984044 00:00:00 INFO [a.py:14 move_to] Now (aware ) is 2024-08-19 00:00:00.984044+09:00 00:00:00 INFO [a.py:10 move_to] Move to 2024-08-19 01:00:00.984044+09:00 01:00:00 INFO [a.py:13 move_to] Now (native) is 2024-08-18 16:00:00.984044 01:00:00 INFO [a.py:14 move_to] Now (aware ) is 2024-08-19 01:00:00.984044+09:00 PASSED [ 50%] tests/a.py::test_time_machine -------------------------------- live log call --------------------------------- 22:42:14 INFO [a.py:25 test_time_machine] Current timezone is JST 22:42:14 INFO [a.py:10 move_to] Move to 2024-08-19 00:00:00.030326+09:00 00:00:00 INFO [a.py:13 move_to] Now (native) is 2024-08-19 00:00:00.030326 00:00:00 INFO [a.py:14 move_to] Now (aware ) is 2024-08-19 00:00:00.030600+09:00 00:00:00 INFO [a.py:10 move_to] Move to 2024-08-19 01:00:00.030847+09:00 01:00:00 INFO [a.py:13 move_to] Now (native) is 2024-08-19 01:00:00.030847 01:00:00 INFO [a.py:14 move_to] Now (aware ) is 2024-08-19 01:00:00.031089+09:00 PASSED [100%] |
「Now (native)」の結果が,違っていることがわかります. test_time_machine の方は期待値と一致しますが,test_freezegun の方は, JST タイムゾーンなのに UTC の値が取れてしまっています.この挙動を理解せずに freezegun を使うと,テスト対象のコードがおかしいのか,テストコードがおかしいのか混乱してしまうことになります.
注意点
time-machine の紹介ページでは,freezegun や python-libfaketime と比較した欠点として CPython でしか使えないことが挙げられています.それに該当するケースの場合はまた別の対応必要になります.
コメント