开发者

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

开发者 https://www.devze.com 2023-04-02 16:24 出处:网络
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

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

精彩评论

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