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.
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:
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
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
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
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.