Ruby Incorrectly Parses 2 Digit Year

Ruby incorrectly parses 2 digit year

The strptime method is parsing the text "63" to the year 2063, not 1963 as you want.

This is because the method decides the century by using the POSIX standard.

The chronic gem has a similar issue because it decides the century, though differently.

A solution is to adjust the date:

d = Date.strptime("15/10/63","%d/%m/%y")
if d > Date.today
d = Date.new(d.year - 100, d.month, d.mday)
end

In the comments of this post, Stefan suggests a good one liner:

d = d.prev_year(100) if d > Date.today 

If you need speed, you can try optimizing like this:

d <= Date.today || d = d << 1200

How to change the way Ruby on Rails parses two digit year input

Bold solution, but it's the only one that covers all cases. Anywhere in the app, when an attribute of type date is set, it will send it through Date.parse() before it writes.

# initializers/active_record_extension_parse_date_writer.rb

module ActiveRecordExtensionParseDateWriter

private

def write_attribute(attr_name, value)
if self.class.column_types.fetch(attr_name.to_s).type == :date
d = (Date.parse(value.to_s)) rescue nil
super(attr_name, d)
else
super
end
end

end

# include the extension
ActiveRecord::Base.send(:include, ActiveRecordExtensionParseDateWriter)

Ruby Time.parse incorrectly returns timestamp for String

Internally time.rb uses, following,

def parse(date, now=self.now)
comp = !block_given?
d = Date._parse(date, comp)
year = d[:year]
year = yield(year) if year && !comp
make_time(date, year, d[:mon], d[:mday], d[:hour], d[:min], d[:sec], d[:sec_fraction], d[:zone], now)
end

It used to parse day, month later year by precision, Range of digits when exeed to 3, it consider it as year

Rails Date#strptime parsing dates incorrectly before year 200

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

Ruby Date.strptime doesn't enforce 4-digit year

There was a Ruby bug opened for this issue last year, but it was rejected. I guess the Ruby team feels that this is valid behavior.

https://bugs.ruby-lang.org/issues/8941

Bug in `Date#strtime`? It parses days which more than 30/31 in a month, confusing them with the years

You have two hidden questions, I think.

Why is 01 a valid match for %Y (which means "year including century")

Because why assume 4 digit years? Otherwise you wouldn't be able to specify 3-digit years (for example, year 882 was when Kiev became capital of Rus). Or maybe in this case you did mean year 1. Ruby has no idea.

Why is 1970 a match for %d?

Because that's how strptime(3) works (which it's supposed to be compatible with). Once format descriptor %d ("day, 1-31") is satisfied with 19, the string stops being processed.

The return value of the function is a pointer to the first character not processed in this function call. In case the input string contains more characters than required by the format string the return value points right after the last consumed input character.

(Date.today + 6).year how to get the years last 2 digits

How about this?

(Date.today + 6).year % 100
# => 15


Related Topics



Leave a reply



Submit