やりたいこと
誕生日を格納するための Date 型の birthday 列を持つ User モデルがある。ある年齢からある年齢までの User を検索したい。
方法
実装例
次の User モデルで User.by_age として実装している。
class User < ApplicationRecord
# ある年齢 from からある年齢 to までの User を検索する。
scope by_age, (lambda do |from: 0, to: 999, today: Date.today|
# 今日のちょうど to 年前の日付の 1 日後から
date_from = today.ago((to + 1).years).since(1.day)
# 今日のちょうど from 年前までを検索する。
date_to = today.ago(from.years)
where(birthday: date_from..date_to)
end)
# 年齢を (簡易的に) 計算する。
# TODO: うるう日やうるう秒を考慮する。
def age(today = Date.today)
((today.to_time - birthday.to_time) / 1.year.seconds).floor
end
def formatted_birthday
date = birthday.strftime('%Y/%m/%d')
wday = %w[日 月 火 水 木 金 土][birthday.wday]
"#{date} (#{wday})"
end
end
実行例
# (あえて境界値がうるう日前後になるように)
# 今日の日付が 2020/02/29 (土) だと仮定する。
today = Date.parse('2020/02/29')
User.order(:birthday).each do |user|
puts("#{user.formatted_birthday} #{user.age(today)} 歳 #{user.name}")
end
# 1989/02/28 (火) 31 歳 ピカチュウ
# 1989/03/01 (水) 30 歳 カイリュー
# 2000/02/29 (火) 20 歳 ヤドラン
# 2000/03/01 (水) 19 歳 ピジョン
# 20 〜 30 歳の User を検索する。
User.by_age(from: 20, to: 30, today: today).pluck(:name)
#=> ["カイリュー", "ヤドラン"]