Easy approach to format Dates in Rails projects
I saw a lot of projects where the default date format was used. In fact, customizing the date format to provide a human-readable date will be very welcomed from your customers!
So what should we do to start making our application awesome? No worries, it’s a very easy process that we will discover together.
Example
To get started we will need an example and to keep it simple we will use an administrative application that displays profiles and allows to export records as CSV.
The schema file:
ActiveRecord::Schema.define(version: 2018_08_30_221053) do
create_table "profiles", force: :cascade do |t|
t.string "firstname"
t.string "lastname"
t.date "birth_date"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
And we have a class for exporting profiles in CSV format.
require "csv"
class GenerateReport
def call
tempfile = Tempfile.new
header = ["First Name", "Last Name", "Birth Date"]
CSV.open(tempfile, "wb") do |csv|
csv << header
Profile.all.each do |profile|
csv << build_row(profile)
end
end
tempfile.rewind
tempfile
end
private
def build_row(profile)
[
profile.firstname,
profile.lastname,
profile.birth_date,
]
end
end
The first improvement is to use strftime
method from Ruby DateTime
class, for more details and possible options check this link for documentation.
We want to display birth dates with the following format 12-Jan-2012. So we update our private method build_row
like so:
class GenerateReport
.......
private
def build_row(profile)
[
profile.firstname,
profile.lastname,
profile.birth_date.strftime("%e-%b-%Y"),
]
end
end
But wait, we should never use a hardcoded literal for formatting dates, we want to write easy to maintain applications 🙂
Our first option is to move the formatting to a constant inside the service class:
class GenerateReport
HUMAN_READABLE_DATE_FORMAT = "%e-%b-%Y"
.......
private
def build_row(profile)
[
profile.firstname,
profile.lastname,
profile.birth_date.strftime(HUMAN_READABLE_DATE_FORMAT),
]
end
end
This is an improvement but let’s step back and ask this question:
- if we need to use the same date format in other parts from our application, does it make sense to have a constant with this name:
GenerateReport::HUMAN_READABLE_DATE_FORMAT
?
So we need a better alternative. Usually, locales files are a good candidate for date formatting for two reasons:
- Easy to edit and provide different formats based on locales.
- Easy to format a date using I18n API.
Let’s see this in action:
First, we create a new date format inside config/locales/en.yml
with human_readable
as key.
# config/locales/en.yml
en:
date:
formats:
human_readable: "%e-%b-%Y"
Then we need just to pass the new key to the format
option for I18n.localize
.
class GenerateReport
.......
private
def build_row(profile)
[
profile.firstname,
profile.lastname,
I18n.localize(profile.birth_date, format: :human_readable),
]
end
end
and if we have an index view we can do the following:
<table>
<thead>
<tr>
<th>Firstname</th>
<th>Lastname</th>
<th>Birth date</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% @profiles.each do |profile| %>
<tr>
<td><%= profile.firstname %></td>
<td><%= profile.lastname %></td>
<td><%= l(profile.birth_date, format: :human_readable) %></td>
</tr>
<% end %>
</tbody>
The last example uses a Rails helper called l
that delegates to I18n.localize
(docs). It’s more common to see the alias used in Rails views but now you shouldn’t be surprised to see I18n.localize
too.
Conclusion
We saw together how to format dates in Rails projects by using I18n API. For more details about this API or other uses cases, I invite you to check this Rails guide.