Mighty Hacks

by Nikolay.TheOsom





Rails dates and magic created_on hack

To demonstrate the problem I'd like to start with a little rant. There is a very common and very simple task of formatting dates, which people, lets not point the fingers, tend to solve in all sorts of different ways. Say like that

<h1>User Profile</h1>

  <dd>Registered At</dd>
  <dt><%= @user.created_at.strftime('%d %b %Y %H:%M') %></dt>

Or, if the date might be missing, then like that

<dt><%= @user.created_at.strftime('%d %b %Y %H:%M') if @user.created_at %></dt>

Which then usually "optimized" into a helper method

module DatesHelper
  def time(time)
    time.strftime('%d %b %Y %H:%M') if time

Which then, often gets bettererized in something like that

def time(time)

And if you need to support i18n, things might take some rather disturbing turn...


Meanwhile, #rails is actually built to take care of those things for you automatically. All you had to do is to write it in your template like that and go home happily

<dt><%= @user.created_at %></dt>

Because as long as you set your locale correctly in the rails config, it will export your times and dates to strings automatically according to that locale.

This approach is better on many levels. 1) It's a rails convention 2) It keeps the templates clean 3) It never crashes if the date's missing and you forgot to take care of that 4) You can keep the formatting consistent through your app.

And if you feel particularly evil and want your own formatting anyways, you can always configure it like so in any initializer

Date::DATE_FORMATS[:default] = "%d/%m/%Y"
Time::DATE_FORMATS[:default] = "%d/%m/%Y %H:%M"

Rails is pretty awesome like that, isn't it?

The Hack

That was the preamble, now to the hack. There is still one valid case when you want to mangle with the times, when you have the automatic created_at timestamp on a record and want to present it as a plain date. Say time doesn't really matter. For example

<dd>Registration Date</dt>
<dt><%= @user.created_at.to_date %></dt>

If you happened to do that a lot, you might find it a bit annoying. Firstly, it still can crash if the date is missing for some reason, and secondly you're adding extra noise to your code with explicit dates conversion. If that's the case you might find the following hack useful.

class ActiveRecord::Base
  def created_on
    created_at && created_at.to_date

  def created_on=(value)
    self.created_at = value

  def updated_on
    updated_at && updated_at.to_date

  def updated_on=(value)
    self.updated_at = value


  def timestamp_attributes_for_create

  def timestamp_attributes_for_update

Just put it somewhere in your config/initializers folder and you'll get the created_on and updated_on methods for free! Now you can say in your code what you really meant

<dt><%= @user.created_on %></dt>

The benefits are obvious, cleaner code, no crashes, and it's built on top of rails conventions, so, no weird stuff, all crystal clear!