开发者

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开发者_如何学Go 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?


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


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.


you can use rails-i18n gem or just copy the en-US.yml and set your default locale "en-US" in config/application.rb


For parsing US-style dates, you could use:

Date.strptime(date_string, '%m/%d/%Y')

In console:

> Date.strptime('04/01/2011', '%m/%d/%Y')
 => Fri, 01 Apr 2011 
> Date.strptime('4/1/2011', '%m/%d/%Y')
 => Fri, 01 Apr 2011 


Use REE? :D

Seriously though. If this is a small app you have complete control over or you are standardizing on that date format, monkey patching for a project is totally reasonable. You just need to make sure all your inputs come in with the correct format, be it via API or website.


Instead of using to_s for Date instances, get in the habit of using strftime. It takes a format string that gives you complete control over the date format.

Edit: strptime gives you full control over the parsing by specifying a format string as well. You can use the same format string in both methods.


Another option is Chronic - http://chronic.rubyforge.org/

You just need to set the endian preference to force only MM/DD/YYYY date format:

Chronic::DEFAULT_OPTIONS[ :endian_precedence ] = [ :middle ]

However the default for Chronic is the out-of-order US date format anyway!

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜