Dave Allie Dave Allie - 21 days ago 8
Ruby Question

Rails Date#strptime parsing dates incorrectly before year 200

Why does Rails'

Date#strptime
parse "13/08" as August 15 or August 14 before the year 200?

Date.strptime('13/08/99', '%d/%m/%Y') #=> Thu, 15 Aug 0099
Date.strptime('13/08/100', '%d/%m/%Y') #=> Fri, 14 Aug 0100
Date.strptime('13/08/199', '%d/%m/%Y') #=> Tue, 14 Aug 0199
Date.strptime('13/08/200', '%d/%m/%Y') #=> Wed, 13 Aug 0200

Answer

To sum up :

If you don't use timecop gem, Date#strptime seems to work fine for year < 200.

If you use timecop, Date#strptime is overwritten and uses Time#to_date, which seems to return wrong values for year < 200.

Easy solutions, either :

  • don't use timecop
  • use Date#strptime_without_mock_date if you do use timecop
  • use Date.new + Time#strptime

Harder solution : understand what's wrong with the implementation of Time#to_date (see Stefan's explanation.)

[0] pry(main)> Time.local(99,8,13).to_date
=> #<Date: 0099-08-15 ((1757444j,0s,0n),+0s,2299161j)>
[1] pry(main)> Date.strptime('13/08/99', '%d/%m/%Y')
=> #<Date: 0099-08-13 ((1757442j,0s,0n),+0s,2299161j)>
[2] pry(main)> require 'timecop'
=> true
[3] pry(main)> Date.strptime('13/08/99', '%d/%m/%Y')
=> #<Date: 0099-08-15 ((1757444j,0s,0n),+0s,2299161j)>
[4] pry(main)> Date.strptime_without_mock_date('13/08/99', '%d/%m/%Y')
=> #<Date: 0099-08-13 ((1757442j,0s,0n),+0s,2299161j)>
[5] pry(main)> time = Time.strptime('13/08/99', '%d/%m/%Y')
=> 0099-08-13 00:00:00 +0053
[6] pry(main)> Date.new(time.year,time.month,time.day)
=> #<Date: 0099-08-13 ((1757442j,0s,0n),+0s,2299161j)>