Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use US-style dates in Rails using Ruby 1.9?

I'm in the U.S., and we usually format dates as "month/day/year". I'm trying to make sure that my Rails app, using Ruby 1.9, assumes this format everywhere, and works the way it did under Ruby 1.8.

I know that lots of people have this issue, so I'd like to create a definitive guide here.

Specifically:

  1. '04/01/2011' is April 1, 2011, not Jan 4, 2011.
  2. '4/1/2011' is also April 1, 2011 - the leading zeros should not be necessary.

How can I do this?

Here's what I have so far.

Controlling Date#to_s behavior

I have this line in application.rb:

    # Format our dates like "12/25/2011'
    Date::DATE_FORMATS[:default] = '%m/%d/%Y'

This ensures that if I do the following:

d = Date.new(2011,4,1)
d.to_s

... I get "04/01/2011", not "2011-04-01".

Controlling String#to_date behavior

ActiveSupport's String#to_date method currently looks like this (source):

 def to_date
    return nil if self.blank?
    ::Date.new(*::Date._parse(self, false).values_at(:year, :mon, :mday))
  end

(In case you don't follow that, the second line creates a new date, passing in year, month and day, in that order. The way it gets the year, month and day values is by using Date._parse, which parses a string and somehow decides what those values are, then returns a hash. .values_at pulls the values out of that hash in the order Date.new wants them.)

Since I know that I will normally pass in strings like "04/01/2011" or "4/1/2011", I can fix this by monkeypatching it like this:

class String

  # Keep a pointer to ActiveSupport's String#to_date
  alias_method :old_to_date, :to_date

  # Redefine it as follows
  def to_date
    return nil if self.blank?
    begin
      # Start by assuming the values are in this order, separated by /
      month, day, year = self.split('/').map(&:to_i)
      ::Date.new(year, month, day)
    rescue
      # If this fails - like for "April 4, 2011" - fall back to original behavior
      begin
      old_to_date
      rescue NoMethodError => e
        # Stupid, unhelpful error from the bowels of Ruby date-parsing code
        if e.message == "undefined method `<' for nil:NilClass"
          raise InvalidDateError.new("#{self} is not a valid date")
        else
          raise e
        end
      end
    end
  end
end

class InvalidDateError < StandardError; end;

This solution makes my tests pass, but is it crazy? Am I just missing a configuration option somewhere, or is there some other, easier solution?

Are there any other date-parsing cases I'm not covering?

like image 917
Nathan Long Avatar asked Sep 02 '11 16:09

Nathan Long


People also ask

How do I change the Date format in Ruby on Rails?

For that, use Date#strptime . You can use Date#strftime to convert the Date object into preferred format.

What is Strftime Ruby?

Ruby | Time strftime() function Time#strftime() is a Time class method which returns the time format according to the directives in the given format string. Syntax: Time.strftime() Parameter: Time values. Return: time format according to the directives in the given format string.


2 Answers

Gem: ruby-american_date

This gem was created since I asked this question. I'm now using it and have been pleased.

https://github.com/jeremyevans/ruby-american_date

like image 86
Nathan Long Avatar answered Oct 19 '22 09:10

Nathan Long


Date.strptime is probably what you're looking for in ruby 1.9.

You're probably stuck monkeypatching it onto string.to_date for now, but strptime is the best solution for parsing dates from strings in ruby 1.9.

Also, the formats are symmetric with strftime as far as I know.

like image 38
Mainguy Avatar answered Oct 19 '22 07:10

Mainguy