Converting a Time to a String and Back in Rails 4.0.0

I'm porting an old rails app to Rails 4 and got stumped tonight on time conversions.

This worked:

Loading development environment (Rails 2.3.18) > > Time.now.to_s => "08/14/2013 07:09PM"

Now it doesn't on Rails 4.0:

Loading development environment (Rails 4.0.0)

irb(main):002:0> Time.now.to_s => "08/15/2013 12:19AM"

1.9.3 (main):0 > Date.parse("08/20/2013").to_s ArgumentError: invalid date from (pry):22:in `parse' 1.9.3 (main):0 > Date.parse("20/08/2013").to_s => "2013-08-20" 1.9.3 (main):0 >

In the source for e.g. ruby-1.9.3-p448 see ./doc/NEWS-1.9.1:

o Time.parse and Date.parse interprets slashed numerical dates·      as "dd/mm/yyyy".

HTH!

Well, back to basics, my question is simple:

How do I tell Rails to use a custom date/time format without it blowing up?

I put this in an initializer:

Date::DATE_FORMATS.merge!(:default => '%m/%d/%Y') Time::DATE_FORMATS.merge!(:default => '%m/%d/%Y %I:%M%p')

This is how I do that in Rails 2.3/Ruby 1.8.7, btw:

ActiveSupport::CoreExtensions::date::Conversions::DATE_FORMATS.merge!(:default => '%m/%d/%Y') ActiveSupport::CoreExtensions::Time::Conversions::DATE_FORMATS.merge!(:default => '%m/%d/%Y %I:%M%p')

That causes Rails 4.0/Ruby 2.0 to eat its self when dealing with forms that have dates/times. I'm not calling Date or Time directly in this case. I'm just letting Rails populate forms and then parse the params on submission to update a model. It is outputting the correct format, but it can't seem to parse what it is outputting. This wasn't a problem with Rails 2.3/Ruby 1.8.7.

A simple way to manifest the problem is to do "Time.now.to_s.to_time" or "Date.today.to_s.to_date". This works on Rails 2.3/Ruby 1.8.7, but does not on Rails 4.0/Ruby 2.0 with the above initializers.

It is hard to believe that they removed such basic functionality? Tamara suggests this might be a Ruby bug(?).

Thanks!

Phil

> o Time.parse and Date.parse interprets slashed numerical dates· > as "dd/mm/yyyy". >

Well, back to basics, my question is simple:

How do I tell Rails to use a custom date/time format without it blowing up?

I put this in an initializer:

Date::DATE_FORMATS.merge!(:default => '%m/%d/%Y')

See my previous comment; that format IS NOT VALID in Ruby 1.9.x and above. Your issue has nothing to do with Rails.

A simple way to manifest the problem is to do "Time.now.to_s.to_time" or "Date.today.to_s.to_date". This works on Rails 2.3/Ruby 1.8.7, but does not on Rails 4.0/Ruby 2.0 with the above initializers.

It works fine with VALID formats. Change your initializer to '%d/%m/%Y' and try `Date.today.to_s.to_date` -- no problem.

It is hard to believe that they removed such basic functionality? Tamara

suggests this might be a Ruby bug(?).

No. It is documented behavior of Ruby.

The documentation, however, points to the libc functions, strftime(3) and strptime(3), which state quite plainly that these formats are reflexive. Hardly seems sporting, does it? Without including a format field, it's probably quite reasonable to assume the parse methods will interpret in that dd/mm/yyyy order, BUT, *with* a format field?? They should not ignore the format field in that case. *That* is the bug.

The american_date gem may help [1]

Colin

[1] https://github.com/jeremyevans/ruby-american_date

Don’t get me wrong, I appreciate your help!

It just seems that this was functionality that seemed to have been broken. It’s not a ‘valid’ vs. invalid argument (where I live, m/d/y is valid and d/m/y is not!) I should be able to define 1/13/2 as being Feb 1st, 2013 if I want to be crazy about it. String parsing isn’t hard, it just seems to be eluding me as how to hint Rails 4/Ruby 2 on how to do it the way I want when it worked in the past.

As Tamara suggests, it appears the format string is being dropped by the time it makes it to the parsing function(?). d/m/y is fine as a default, but saying to change my initializer to match the already default is like removing it all together and doesn’t solve my localization problem.

Phil

It's not a 'valid' vs. invalid argument

Of course it is. What you consider valid or not is irrelevant, it's defined in the language.

You happened to be lucky in picking a format (%m/%d/%Y) that worked in Ruby 1.8.7; the opposite day/month placement %d/%m/%Y doesn't:

1.8.7 :008 > Date.parse(Date.today.strftime('%m/%d/%Y')).to_s => "2013-08-22" 1.8.7 :009 > Date.parse(Date.today.strftime('%d/%m/%Y')).to_s ArgumentError: invalid date

As Tamara suggests, it appears the format string is being dropped by the time it makes it to the parsing function(?).

Date.parse doesn't take a format argument. The Rails String#to_date helper uses Date.parse. It "used to work" because you were lucky.

I can see an argument for replacing that with Date.strptime; why not try it and see if anything breaks? :slight_smile:

Add your use case to the tests and go for it.