Natural Language Date & Time Parsing for ActiveRecord
Update: If you’re on Rails 2.1 or later, be sure to read the update to this post.
Chronic is a nice natural language parser for Ruby, but my first stab at adding it to a Rails application immediately felt wrong. I was adding special case code in a controller to re-parse the field if the initial parse failed. Of course, that needed to be duplicated for every field I wanted to support it.
A much better (second) idea was, why not add this directly to ActiveRecord? Every model gets it for free, and it turns out the code is hardly any more complicated.
require 'active_record' require 'chronic' module ChronicParser def self.extended(object) class << object alias_method_chain :string_to_date, :chronic alias_method_chain :string_to_time, :chronic end end def string_to_date_with_chronic(string) value = string_to_date_without_chronic(string) if value.nil? now = TzTime.now rescue Time.now value = Chronic.parse(string, :now => now).to_date rescue nil end value end def string_to_time_with_chronic(string) value = string_to_time_without_chronic(string) if value.nil? now = TzTime.now rescue Time.now value = Chronic.parse(string, :now => now) end value end end ActiveRecord::ConnectionAdapters::Column.extend(ChronicParser) |
Put this where it will be executed during Rails initialization and you’re done.
Note also that while Chronic doesn’t claim to support time zones, this code will do the right thing by supplying a local perspective of the user’s current time, using TzTime. Someone in Japan using your site (which is hosted in California) in the early morning will get the expected result for “tomorrow.”
If a zone hasn’t been set for TzTime, it’ll fall back on the server’s current time. If you aren’t using TzTime, modify appropriately.