寻找使用 Ruby 检测生日范围的有效方法

Looking for an efficient way to detect birthday ranges using Ruby

目前正在开发一款应用,需要向运营商显示生日在过去 7 天或未来 21 天内的员工列表。

我们有一个包含出生日期的员工数据库,按日期范围选择非常简单。但是,我们必须显示 future/past 生日从今天算起有多少天。

代码目前看起来像:

today = Date.today
dob = staff.dob

days = Date.new(today.year, dob.month, dob.day) - today

if days < 0
    "#{ days.abs } days ago"
else
    "in #{ days } days"
end

这几乎在所有情况下都适用,除了 十二月底或一月初。因为我们使用 today.year 作为年份比较器,如果未来 21 天溢出到新的一年,我会得到错误的 "days ago" 读数。例如

今天的日期是 20th December 2015,工作人员的生日是 2nd January,我得到的不是 "in 13 days",而是 "352 days ago"

我知道我可以将代码包装在另一个 if/then/else 子句中,该子句检查当前日期是否在 12 月 10 日之后,以适应这种边缘情况,但日期范围有可能会改变动态地,而且,代码开始看起来凌乱和不优雅 - 不像我希望 ruby 代码看起来的样子。

有没有人对如何处理这个问题有更好的建议?

(注意:这是在一个基于 Sinatra 的项目中,所以我没有所有的 ActiveSupport 或基于 Rails 的魔法,尽管我不反对使用 gem 如果它会给我我需要的结果。)

获取今年、明年和去年的最小天数

today = Date.today
dob = staff.dob

this_dob = Date.new(today.year, dob.month, dob.day)
next_dob = this_dob.next_year
prev_dob = this_dob.prev_year
days = [this_dob - today, next_dob - today, prev_dob - today].min_by{|i| i.to_i.abs}

if days < 0
  "#{ days.abs } days ago"
else
  "in #{ days } days"
end
require 'date'

class BirthDates
  attr_reader :year, :month, :day
  def initialize(year, month, day)
    @year  = year
    @month = month
    @day   = day
  end
end

def count_days(dob)
  today = Date.today
  this_year = today.year
  days = [this_year-1, this_year, this_year+1].map { |year|
    (Date.new(year, dob.month, dob.day) - today) }.min_by(&:abs)
  if days < 0
    "#{ -days } days ago"
  elsif days > 0
    "in #{ days } days"
  else
   "Happy Birthday!"
  end
end

我们来试试吧。 (今天是 15 年 10 月 29 日)

dob = BirthDates.new(1908, 12, 28)
count_days(dob) #=> "in 60 days"

dob = BirthDates.new(1999, 10, 27)
count_days(dob) #=> "2 days ago"

dob = BirthDates.new(2014, 10, 29)
count_days(dob) #=> "Happy Birthday"

如果今天是 16 年 1 月 5 日:

dob = BirthDates.new(1908, 12, 28)
count_days(dob) #=> "8 days ago"

dob = BirthDates.new(1999, 3, 27)
count_days(dob) #=> "in 82 days"