The EdgeCase Library Need Development? Check us out!

American Date Parsing

Posted 14 March 2012

Author Aaron Christy

Wave the flag!

I recently ran into an interesting issue while working on a Rails 3, Ruby 1.9.3 project.

My needs were simple: I had a single text field that allowed the user to enter a date in MM/DD/YYYY format. It was also bound to the jquery calendar plugin which was configured to prefill the textbox with the selected date in MM/DD/YYYY format. However, every time a new record was created, this date would be written as null.

When I forced the date to be YYYY/MM/DD format, it would save the date just fine. However, I didn't feel this date was as human readable. I also didn't want to continually wrestle with the jquery calendar altField, altFormat options just to work around the problem.

If you also are fighting this issue, you can skip to the end to fix it. It's fine, I won't be offended.

Problem Example

Given that I have a Person:

ActiveRecord::Schema.define(:version => 20120218015632) do
  create_table "people", :force => true do |t|
    t.string "name"
    t.date "birthdate"
    t.datetime "created_at"
    t.datetime "updated_at"
  end
end

And an empty ActiveRecord model:

class Person < ActiveRecord::Base
end

You find that you cannot even set the birthdate in MM/DD/YYYY format:

p = Person.create
p.birthdate = "04/29/2000"
=> "04/29/2000"

p.birthdate
=> nil                    #oh, noes!

p.birthdate = "2000-04-29"
=> "2000-04-29"

p.birthdate
=> "Sat, 29 Apr 2000"     #oh, my!

Ruby, Ruby, Ruby

The problem here is how Ruby 1.9 now handles Date parsing.

Mats himself directly addresses this change:

"dd/dd/dd" format itself is very culture dependent and ambiguous. It is yy/mm/dd in Japan (and other countries), mm/dd/yy in USA, dd/mm/yy in European countries, right? In some cases, you can tell them by accident, but we should not rely on luck in general cases. I believe that is the reason parsing this format is disabled in 1.9.

And just to solidify this problem:

irb: Date.parse('04/29/2000')
ArgumentError: invalid date
  from /Users/aaronchristy/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/1.9.1/date.rb:1022:in `new_by_frags'
  from /Users/aaronchristy/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/1.9.1/date.rb:1066:in `parse'
  from (irb):1
  from /Users/aaronchristy/.rvm/rubies/ruby-1.9.2-p290/bin/irb:16:in `'

irb: Date.parse('2000/04/29')
===> Date: 2000-04-29 (4903327/2,0,2299161)

Polish the Gem

The solution for this problem is to correctly translate MM/DD/YYYY into proper ISO format YYYY-MM-DD.

Luckily, this work has already been done for you and wrapped up in the american-date gem.

~ gem install american_date
Fetching: american_date-1.0.0.gem (100%)
Successfully installed american_date-1.0.0

irb: Date.parse('04/29/2000')
ArgumentError: invalid date
  from /Users/aaronchristy/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/1.9.1/date.rb:1022:in `new_by_frags'
  from /Users/aaronchristy/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/1.9.1/date.rb:1066:in `parse'
  from (irb):1
  from /Users/aaronchristy/.rvm/rubies/ruby-1.9.2-p290/bin/irb:16:in `'

irb: require 'american_date'
===> true

irb: Date.parse('04/29/2000')
===> #Date: 2000-04-29 (4903327/2,0,2299161)

TL;DR

  • Ruby 1.9.x no longer accepts dates in MM/DD/YYYY format
  • Include the american-date gem

Now get back to doing something useful.