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.