maco's life

主にエンジニアリングと読書について書いていきます。

Railsでサーバー時刻を任意の時間にする

Railsとかに限った話じゃなくて、開発中のサーバー時刻を任意の時間に変更したいといったことはよくあると思う。

そうかの有名なアニメ、「時をかける少女」でいうタイムリープをしたいということである。

タイムリープする方法は

  • アプリケーションの内部時刻を変更する
  • アプリケーションが動いているサーバー自体の時刻を変更する

以上のような方法があげられるがサーバー自体の時刻を変更するなんて恐ろしすぎるし、かつめんどくさいのでアプリケーションの内部時刻を変更するアプローチで、Railsのサーバー時刻を変更できるようにしてみた

実装方法

時間を操作する際に、よく使うモジュールでTimecopというものがある。このTimecopのread meでrailsで使う場合の方法も書いてある

# in config/environments/test.rb
config.after_initialize do
  # Set Time.now to September 1, 2008 10:05:00 AM (at this instant), but allow it to move forward
  t = Time.local(2008, 9, 1, 10, 5, 0)
  Timecop.travel(t)
end

read meより転載

と書かれていた。

例ではtest.rbで使うようにかいてあったけど、これをdevelopment.rbに書いてあげれば、developmentで起動しているときのみサーバー時間をいじれるはず。そこで以下のように書いてみた

# in config/environments/development.rb
config.after_initialize do
  if File.exist?("tmp/localtime") then
    localtime = File.open("tmp/localtime").read
    if !localtime.blank? then
      t = Time.parse(localtime)
      Timecop.travel(t)
    end
  end
end

tmp/localtimeというファイルをつくってその中にyyyy-mm-dd hh:mm:dd形式で時間を書いて、その時間を読み取ってタイムリープするといったコードです。(あんまり設定ファイルにごちゃごちゃ書くのもあれなんで切り出したほうがよいかも)

このlocaltimeの設定はrakeタスクで行うようにしていて、

# in lib/tasks/server_time.rake
namespace :server_time do
  task :mock => :environment do |t|
    set_time = ENV['RAILS_TIME']
    File.open("tmp/localtime","w") do |file|
      file.puts(set_time)
    end
  end

  task :reset => :environment do
    File.unlink('tmp/localtime')
  end
end

こんな感じに実装しました使い方はシンプルで bundle exec rake server_time:mock RAILS_TIME='2016-01-01 00:00:00'で設定して、bundle exec rake server_time:resetでリセットできます。