If you have an application that stores people and their birth dates, it can be suprisingly difficult to find out whose birthdays are this month or next month.
There are two common approaches used for this:
- Use a SQL query that compares the month (and possibly day) only out of the date of birth field.
- Add additional fields (birth day and birth month) into your data to help you achieve this goal.
This post will go with option 2, as it is very difficult to find a SQL query solution that works on all databases (eg. MySQL uses Month(xx) whereas SQLite requires a string format function).
So we have a model "User" and it has a field "date_of_birth". We want to add two new fields to this model for the day and month of the date of birth. The following migration generator will achieve this:
rails generate migration add_birth_date_fields_to_users birth_day:integer birth_month:integer
You can then update your database.
The next step is to make sure you populate these fields for all of your existing records. You could have done this in the migration, but I like to do this in a separate rake task.
Create a rake file in your lib folder called "update_birth_date_fields.rake" This rake task will iterate through all users that have a date of birth, and will update the fields. This can take a while if you have a lot of records, it is why I like to separate it into a rake task.
desc "Updating birth dates from date of birth" task :update_birth_date_fields => :environment do puts "Updating birth dates from date of birth" User.where('date_of_birth is not null').each do |u| u.birth_month = u.date_of_birth.month u.birth_day = u.date_of_birth.day u.save end puts "Finished updating birth dates from date of birth" end
You can run this now with
Now we need to make sure that all new date of births that are added or edited will also populate these fields. We do this with a before_save callback. In your user.rb model file, add this code:
before_save :update_birth_date_fields private def update_birth_date_fields if date_of_birth_changed? self.birth_month = date_of_birth ? date_of_birth.month : nil self.birth_day = date_of_birth ? date_of_birth.day : nil end end
Once this is all done, it is easy to access all users with birthdays this month:
This works out to be a very efficient query compared to other alternatives mentioned earlier.