2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Happy ElementsAdvent Calendar 2020

Day 5

RailsでTimecop/TimeHelpersを使って時刻を変える方法について

Last updated at Posted at 2020-12-04

この記事は、Happy Elements Advent Calendar 2020の5日目です。
RailsでTimecop/TimeHelpersを使って時刻を変える方法についての記事です。

はじめに

ソーシャルゲームでは、イベントが yyyy年mm月dd日に始まるなど、特定の時刻になると発動することが定番です。

例: 架空のソーシャルゲームのイベントカレンダー
image.png

これらのようなイベントの動作を確認するために、サーバ側で時刻を変えることができると、動作テストがしやすくなります。

  • 例1 12月2日 15:00開始のガチャAの動作確認をするために、サーバの時間を12/2 16時にセットする
  • 例2 バグ報告があったので、1ヶ月前のイベントの時間にセットしてバグ調査をする など

サーバ側で時刻を変える方法について、以下の3つの方法に絞って調査しました。

  • Timecop
  • ActiveSupport::Testing::TimeHelpers
  • libfaketime

この記事では、それぞれがどのようなものか、をまとめました。

Timecop

https://github.com/travisjeffery/timecop
こちらはRailsのgemです。
Time/Date/DateTimeクラスのオーバーライドとして実装されているものでした
https://github.com/travisjeffery/timecop/blob/master/lib/timecop/time_extensions.rb

調査を進めると、TimeHelpersという物がRailsに組み込まれていることがわかりました。
TimeHelpersは、Timecopより機能としては少なく、時刻を固定させるのみです。
Timecopは、固定の他に、過去や未来に設定した後に時刻が進むなども可能です。

TimecopとTimeHelperでは、Timecopの方がリッチな機能である、と言えると思います。

  • 標準のTimeHelpersで十分である
  • gemを入れたくない

などの場合は、TimeHelpersの利用が選択肢に上がってくると思います。
2020年現在もメンテナンスはされているようです。

ActiveSupport::Testing::TimeHelpers

Rails4.1以降で標準のものです。
時刻を固定・解除するのみです。

libfaketime

こちらはRailsの話ではなく、OSにインストールして動くサービスです。
システムコールを改変するものです

  • メリット
    • プログラムを変えなくていい
  • デメリット
    • 導入が少し手間

システムコールとは?

libfaketimeでシステムコールの話が出てきたので、ここではシステムコールについて。

strace というコマンドを使って、コマンドを打つと大量にログが出てきます。これがシステムコールです(fstat, close, read, ...etc)。
libfaketimeは、このシステムコールを書き換えて時刻を変えるので、TimecopやTimeHelpersよりも深い部分で動いていると言えると思います。

strace date

execve("/bin/date", ["date"], 0x7ffc3bc97870 /* 28 vars */) = 0
brk(NULL)                               = 0x55b92bdc6000
openat(AT_FDCWD, "/usr/local/lib/faketime/libfaketime.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0$\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0644, st_size=66512, ...}) = 0
...
read(4, "@2020-6-16 20:30:00\n", 4096)  = 20
close(4)                                = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0), ...}) = 0
write(1, "Tue Jun 16 20:30:00 JST 2020\n", 29Tue Jun 16 20:30:00 JST 2020
) = 29
close(1)                                = 0
close(2)                                = 0
munmap(0x7f46f7935000, 8)               = 0
munmap(0x7f46f7936000, 32)              = 0
exit_group(0)                           = ?
+++ exited with 0 +++

余談

調査中に、もしTimecopとlibfaketimeを同時に使った場合どうなるのだろう、と思いました。
実験してみたところ、Timecopで設定した時刻がRailsでは得られました。

libfaketimeで改変された時間を、さらにTimecopが上書きするという順序になります。
イメージ図は以下の通りです。

image.png

まとめ

自分が運営中のタイトルでは、開発者の手元では、TimeHelpersを使ってRailsの時刻を変えながらイベントの動作確認などに利用しています。libfaketimeも使えるようになっており、必要であればそちらも利用しています。
こちらの記事では、サーバ側で時刻を変える方法について、Timecop, TimeHelpers, libfaketimeを調査しました。
調査してわかりましたが、どれが優れているというものではありませんでした。
それぞれ、やりたいことの要件に合わせて選択すれば良いと思いました。
もしRailsで時刻を変えたいけど、どうしようと悩んで調べている方がいらっしゃいましたら、
参考にしていただけると幸いです。

(注) 記事の中の図は自分で用意しました

参考

https://andycroll.com/ruby/replace-timecop-with-rails-time-helpers-in-rspec/
https://techracho.bpsinc.jp/penguin10/2018_12_25/67780
https://qiita.com/ktrkmk/items/b1361dd43d22dcf5627e
https://qiita.com/Targityen/items/67682d6c80cdcbe1186c

終わりに

Happy Elements株式会社 カカリアスタジオでは、
いっしょに【熱狂的に愛されるコンテンツ】をつくっていただけるメンバーを大募集中です!

もし弊社にご興味持っていただけましたら、是非一度
下記採用サイトをご覧ください。
https://recruit.happyelements.co.jp/

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?