2016-11-14 2 views
4

Почему Rails Date#strptime разобрать «13/08» как 15 августа или 14 августа до 200 года?Рельсы Дата # strptime синтаксический анализ недействителен до года 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 
+0

Что ваш Rails и Рубите версии? Какая ОС? Каков ваш часовой пояс? – dnsh

+0

Интересно, что это не происходит в обычном рубине. 'puts Date.strptime ('13/08/99 ','% d /% m /% Y ')' '' 0099-08-13 "' - Ruby: 2.3.1 - Rails: 4.2.7.1 - ОС: Ubuntu 16.04 - Часовой пояс: Брисбен/Австралия –

+1

Что такого странного в этом поведении? Что вы ожидаете? http://apidock.com/ruby/DateTime/strftime указывает, что директива% Y ожидает не менее 4 цифр. –

ответ

2

Подводя итог:

Если вы не используете TimeCop камень, дата # strptime, кажется, работает нормально за год < 200.

Если вы используете TimeCop, дата # strptime переписывается и использует Time # TO_DATE, который, кажется, возвращать неверные значения за год < 200.

Простые решения, либо:

  • не использует Timecop
  • использования Даты # strptime_without_mock_date если вы используете TimeCop
  • использования Date.new + Time # strptime

Harder решение: понять, что случилось с реализацией Time#to_date (см.объяснение Стефана.)

[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)> 
2

Использование github.com/daveallie/where_is:

Where.is(Date, :strptime) 
#=> ["/home/deployer/.rvm/gems/ruby-2.3.1/gems/timecop-0.7.0/lib‌​/timecop/time_extens‌​ions.rb", 46] 

, что приводит к timecop/time_extensions.rb#L46:

def strptime_with_mock_date(str, fmt) 
    Time.strptime(str, fmt).to_date 
end 

Это правильно, Timecop получается:

Date.strptime('13/08/99', '%d/%m/%Y') 
#=> #<Date: 0099-08-13 ((1757442j,0s,0n),+0s,2299161j)> 

в:

Time.strptime('13/08/99', '%d/%m/%Y').to_date 
#=> #<Date: 0099-08-15 ((1757444j,0s,0n),+0s,2299161j)> 

Так что это не Date, что ошибочен, это Time?

Это не совсем багги, просто разные. Time#to_date интерпретирует значения как предваряющие григорианские календарные даты:

Date.new(99, 8, 13, Date::GREGORIAN).new_start 
#=> #<Date: 0099-08-15 ((1757444j,0s,0n),+0s,2299161j)> 

в сравнении:

Date.new(99, 8, 13, Date::ITALY).new_start 
#=> #<Date: 0099-08-13 ((1757442j,0s,0n),+0s,2299161j)> 

Вот резюме от Wikipedia:

Julian range    | Proleptic Gregorian range | Gregorian ahead by: 
-------------------------|---------------------------|-------------------- 
03/03/4 - 01/03/100 | 01/03/4 - 28/02/100 | −2 days 
02/03/100 - 29/02/200 | 01/03/100 - 28/02/200 | −1 days 
01/03/200 - 28/02/300 | 01/03/200 - 28/02/300 | 0 days 
29/02/300 - 27/02/500 | 01/03/300 - 28/02/500 | 1 day 
28/02/500 - 26/02/600 | 01/03/500 - 28/02/600 | 2 days 
27/02/600 - 25/02/700 | 01/03/600 - 28/02/700 | 3 days 
26/02/700 - 24/02/900 | 01/03/700 - 28/02/900 | 4 days 
25/02/900 - 23/02/1000 | 01/03/900 - 28/02/1000 | 5 days 
24/02/1000 - 22/02/1100 | 01/03/1000 - 28/02/1100 | 6 days 
23/02/1100 - 21/02/1300 | 01/03/1100 - 28/02/1300 | 7 days 
22/02/1300 - 20/02/1400 | 01/03/1300 - 28/02/1400 | 8 days 
21/02/1400 - 19/02/1500 | 01/03/1400 - 28/02/1500 | 9 days 
20/02/1500 - 04/10/1582 | 01/03/1500 - 14/10/1582 | 10 days 
Смежные вопросы