如何在Rails中创建“即将到来的生日”模块?

我有一个表“users”,其列为“date_of_birth”(日期,月份,年份为DATE格式)。 在前端,我需要列出5个即将到来的生日。

花了很长时间试图弄清楚这个逻辑……还浏览了谷歌里所有可能没有运气的文章。

有任何建议如何在RoR中做到这一点?

谢谢!

有几个答案建议计算/存储一年中的日期并对其进行分类,但是当你接近年底并且需要在明年年初考虑有生日的人时,仅此一项并不会有多大好处。

我会找一个解决方案来计算每个人下一个生日的完整日期(年,月,日),然后对此进行排序。 您可以在Ruby代码中执行此操作,也可以使用数据库中的存储过程执行此操作。 后者会更快,但会使您的应用程序更难以在以后迁移到不同的数据库平台。

仅每天更新一次即将到来的生日列表是合理的,这意味着您可以使用某种forms的缓存。 因此,所需的查询/代码的速度不是问题,只要您缓存结果,这样的事情应该可以正常工作:

class User def next_birthday year = Date.today.year mmdd = date_of_birth.strftime('%m%d') year += 1 if mmdd < Date.today.strftime('%m%d') mmdd = '0301' if mmdd == '0229' && !Date.parse("#{year}0101").leap? return Date.parse("#{year}#{mmdd}") end end users = User.find(:all, :select => 'id, date_of_birth').sort_by(&:next_birthday).first(5) 

编辑 :修复了与闰年一起正常工作。

感谢我的rails 3应用程序中的这篇文章我使用:

  u = User.where("strftime('%m%d', date_of_birth) = ?", Date.today.strftime('%m%d')) 

更新:

要在Postgresql中使用它:

 u = User.where("extract(month from date_of_birth) = ? AND extract(day from date_of_birth) = ?", Date.today.strftime('%m'), Date.today.strftime('%d')) 

最好使用SQL来进行此查询:

 next_month = (Date.today + 1.month).month # or use any other integer users_having_birhtday_next_month = User.where("EXTRACT(MONTH FROM date_of_birth) = ?", next_month) 

注意:EXTRACT – PostgreSQL函数

我有一个before_save回调计算并存储在数据库中的一年中的生日那天。

然后你有一个简单的查询来拉回接下来的5个生日。 确保处理你年底的边界条件(我会检查你是否在RoR中没有获得5个结果,然后在1月1日运行一个新的查询以获得一些额外的生日来弥补到5)。

您可能希望缓存结果,这样如果查询位于公共页面上,则不会继续重新运行查询。

我也认为一年中的某一天都是可行的方式,但事实上,一年中的大部分时间都不同,这取决于它是否是闰年,这使得它变得棘手。

更好的是将月份和日期存储为字符串:d.strftime(’%m%d’)。 然后,您可以将其用作(可能)两个查询(假设新列为’monthday’)

第一,

 User.find(:all, :condition => [:monthday > Date.now.strftime('%m%d')], :select => "DISTINCT monthday", :limit => 5) 

如果未获得5个结果,请再次执行查询,但使用“0101”代替日期计算并降低限制。

这会为您提供monthday字符串列表,然后您必须将其转换回日期。

如果您需要用户,请删除:select行。

如果您使用的是Oracle,则可以在不创建新列的情况下执行此操作。 IMO创建一个包含您已有数据的列是一种气味。

SQL有点难看 – 我确信有更优雅的方式来做到这一点。 通常在这些情况下,我会向我的DBA朋友寻求建议。

 User.find(:all, :conditions => "TO_NUMBER(TO_CHAR(dob, 'MMDD')) >= TO_NUMBER(TO_CHAR(SYSDATE, 'MMDD'))", :order => "TO_NUMBER(TO_CHAR(dob, 'MMDD'))", :limit => 5) 

有些人认为重复的列更快,但是如果你有足够的用户数据来加快速度,那么你应该将重复列与没有它的表进行对比,该表具有TO_NUMBER(TO_CHAR(dob, 'MMDD'))的function索引。

以下是我今天的生日:

User.find_by_sql("select * from users where date_format(date_of_birth, '%m%d') = date_format(now(), '%m%d')"

我每天跑一次。 从大约100,000行开始只需不到一秒钟。 (它没有妥善处理2月29日出生的人。)

如果您的数据库是mysql,则以下内容可能更快:

 scope :birthday_next_week, (lambda { where('DayOfYear(date_of_birth) >= 7 And DayOfYear(date_of_birth) - DayOfYear(curdate()) Between 0 and 6) Or (MOD(YEAR(curDate()),4) = 0) And MOD(YEAR(curDate()),100) != 0 And (DayOfYear(date_of_birth) + 366 - DayOfYear(curdate())) % 366 < 7) Or (DayOfYear(date_of_birth) + 365 - DayOfYear(curdate())) % 365 < 7'). order('DATE_FORMAT( date_of_birth, "%m-%d" ) ASC') }) 

编辑:更改为使其在新年前夕/闰年前一周工作。

我发现这对我有用。 我没有要求过滤最近的五个结果,所以我使用月份与当前相同。

 def upcoming_birthdays @resident = current_business.residents.where("extract(month from date_of_birth) = ?", Date.today.strftime('%m')).paginate(:page => params[:page]) end