前提
現在時刻が、特定の期間に含まれるか判定する関数を定義する必要がありました。
実装の過程でアドバイスをいただいて改善されたので、前後でどのように変わったのかまとめます。
Railsで実装しています。
元々実装していたコード
def event_period?()
now = Time.now
# 1月1日から1月31日まで
event_start_day = Time.new(2023, 1, 1, 0, 0, 0, "+00:00")
event_end_day = Time.new(2023, 2, 1, 0, 0, 0, "+00:00")
now >= event_start_day && now < event_end_day
end
現在の日時が event_start_day
より後かつ event_end_day
より前である場合に true
が返され、そうでない場合にfalse
が返されます。
最終的なコード
def event_period?(now: Time.zone.now)
Range
.new(
Time.zone.parse('2023-01-01 00:00:00'),
Time.zone.parse('2023-02-01 00:00:00'),
true
)
.cover?(now)
end
タイムゾーンに関して
元のコードでは、Time#newを使用してイベントの開始日と終了日を定義していました。
最終的なコードでは、Time.zone.parse()メソッドを使用して、Railsアプリケーションのタイムゾーンに設定されているタイムゾーンを考慮した時間を取得しています。
範囲をRangeクラスを使って表現する
また、Rangeクラスを使用して期間を表現することで、期間が直感的に理解できるようになりました。
最初のコードでは、開始日と終了日を定義して、さらに別の行で日付が含まれているか判定する手間がありました。
※備考
Range#newの第三引数にtrue
を指定すると、終端を含まないようにできます。(2023-02-01 00:00:00
は含まれない)
Range.new (Ruby 3.2 リファレンスマニュアル)
現在時刻を扱うため、デフォルト引数を指定
この関数は、現在時刻が特定の期間に含まれるかを判定します。
引数を取っていますが、デフォルトの値はTime.zone.now
にしています。
理由としては、この関数が単体で使用される場合はそのまま呼び出して問題ないのですが、仮に複数の現在時刻を扱う関数と一緒に呼び出されたり、ループ処理の中で呼び出された際に現在時刻がずれないように、外側からも渡せるようにするためです。
ですから、引数の名前はtime
ではなくnow
になっています。
コードを見た方がわかりやすいと思うため、以下に例を書いておきました。
- 時間がずれるパターン
def current_time(now = Time.now)
now
end
result = []
100000.times { result << current_time }
p result.first # 2023-05-16 02:50:10.53720954 +0000
p result.last # 2023-05-16 02:50:10.621344667 +0000 微妙にずれる
関数の内部で現在時刻を定義するとこのようなことが起こります。
もちろん、レアなケースだとは思うので気にならなければ良いのですが、外部から現在時刻を渡せるようにすると、以下のように改善されます。
- 時間がずれないパターン
def current_time(now = Time.now)
now
end
now = Time.now
result = []
100000.times { result << current_time(now) }
p result.first # 2023-05-16 02:51:53.278507445 +0000
p result.last # 2023-05-16 02:51:53.278507445 +0000 一致
外側から渡しているので、一致していますね。
使い所に応じて変更できるので便利です。
詳しくは参考リンクの記事がわかりやすいので、是非一度読んでみてください。
以上です。