In Rails 2, if you wanted to define a named date/time format, the conventional way was to make an initializer which would modify the DATE_FORMATS hash that ActiveSupport mixed into Date and Time:
# config/initializers/date_and_time_formats.rb
def military_hour_to_civil_hour(hour)
mod = (hour % 12)
mod + (12 * (mod > 0 ? 0 : 1))
end
Date::DATE_FORMATS[:std] = lambda {|d| "#{d.month}/#{d.day}/#{d.year}" }
Time::DATE_FORMATS[:std] = lambda {|t| "#{t.month}/#{t.day}/#{t.year} #{military_to_civil_hour(t.hour):%M%p" }
To use the formatted date/time, you'd then use #to_formatted_s (aliased to #to_s) defined on Date and Time:
date = Date.new(2011, 2, 3)
date.to_s(:std) #=> "2/3/2011"
time = Time.utc(2011, 2, 3, 4, 5)
time.to_s(:std) #=> "2/3/2011 4:05am"
Modifying a constant monkeypatched into a Rails core class has never felt right to me, though. Fortunately in Rails 3 (*), date/time formats are now customizable via i18n, so we can now just update the locale file like so.
# config/locales/en.rb
{
:en => {
:date => {
:formats => {
# Note that a two-element array is passed to this lambda unlike before.
# The first element is the object, the second is a hash of options.
:std => lambda {|d,_| "#{d.month}/#{d.day}/#{d.year}" }
}
},
:time => {
:formats => {
# Note that a two-element array is passed to this lambda unlike before.
# The first element is the object, the second is a hash of options.
:std => lambda {|t,_|
military_hour = t.hour
mod = (military_hour % 12)
civil_hour = mod + (12 * (mod > 0 ? 0 : 1))
"#{t.month}/#{t.day}/%Y #{civil_hour}:%M%p"
}
}
}
}
}
Using the formatted date/time is a bit different too. Instead of using #to_formatted_s, we can use I18n.translate (or I18n.t for short):
date = Date.new(2011, 2, 3)
I18n.t date, :format => :std #=> "2/3/2011"
time = Time.utc(2011, 2, 3, 4, 5)
I18n.t time, :format => :std #=> "2/3/2011 4:05am"
(*) This may actually be also available in Rails 2.2, I just haven't seen anything about it before...