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
Error "Undefinded Method "Load_Defaults" " When Trying to Deploy App on Heroku
Recommended Two-Way Encryption Gems for Ruby
Clicking a Button with Ruby Mechanize
Is There a Gem That Normalizes and Format Us Phone Numbers in Ruby
Ruby Selenium Webdriver Unable to Find Mozilla Geckodriver
Ruby - Does Array a Contain All Elements of Array B
Configure Webrick to Use Automatically Generated Self-Signed Ssl/Https Certificate
How to Add "Somewhere" a 'Before(:Each)' Hook So That All Spec File Can Run It
Access Ruby Hash Using Dotted Path Key String
Rails Render of Partial and Layout in Controller
/Usr/Bin/Env Ruby_Noexec_Wrapper Fails with No File or Directory
Rails 5.0.0 When Installing "Nio4R":Failed to Build Gem Native Extension
How to Cleanly Verify If the User Input Is an Integer in Ruby
Rails: How to Check If a Column Has a Value