Converting a string to Date in Ruby - ruby

I am trying to parse a string 26/03/2012, in dd/mm/yyyy format to Ruby's Date using Date.strptime, as follows:
#!/usr/bin/ruby
require 'date'
puts 'Ruby Version: ' + RUBY_VERSION
date_str = '26/03/2012'
date = Date.strptime(date_str, "%d/%m/%y")
puts 'Parsed Date: ' + date.to_s
The output is:
Ruby Version: 1.8.7
Parsed Date: 2020-03-26
The year part has become 2020, instead of 2012!

That should be %Y upper case, rather than %y:
date = Date.strptime(date_str, "%d/%m/%Y")
puts 'Parsed Date: ' + date.to_s
# Parsed Date: 2012-03-26
From the docs:
Date (Year, Month, Day):
%Y - Year with century (can be negative, 4 digits at least)
-0001, 0000, 1995, 2009, 14292, etc.
%C - year / 100 (round down. 20 in 2009)
%y - year % 100 (00..99)
Since %y expects two digits only, it takes the first two 20 and assumes that to be a 2 digit representation of 2020, since 2020 % 100 = 20.

If you change your strptime function to
date = Date.striptime(date_str, "%d/%m/%Y")
it will output correctly.

Related

Convert a Ruby string to proper Date and Time

I have a Ruby variable containing Timestamp , I need to get Date and Time from it , I am able to get Date (but wrong) time is always 00:00 (note: li is a variable getting from a file)
li = 'Date: Wed, 25 Dec 2019 23:16:52 -0600'
date = li.delete("Date: ")
=> "Wd,25c2019231652-0600"
puts date
Wd,25c2019231652-0600
Date.parse(date).to_s
=> "2020-09-25"
Time.parse(date).to_s
=> "2020-09-25 00:00:00 +0530"
DateTime.parse(date).to_s
=> "2020-09-25T00:00:00+00:00"
DateTime.parse(date).strftime('%Y-%d-%m %H:%M:%S')
=> "2020-25-09 00:00:00"
I need to get correct date and Time (either in UTC or -0600)
li = 'Date: Wed, 25 Dec 2019 23:16:52 -0600'
require 'date'
Date.strptime(li, 'Date: %a, %d %b %Y %H:%M:%S %z')
#=> #<Date: 2019-12-25 ((2458843j,0s,0n),+0s,2299161j)>
See Date::strptime. Date-time formatting directives are given in the doc DateTime#strftime.

Ruby incorrectly parses 2 digit year

Ruby correctly parses the first date but the second one is incorrect. Tested with ruby 1.9.3 and 2.1.2.
Any idea how to get it to work consistently? (We are getting in birth dates as 2 digit years)
Date.strptime("10/11/89","%d/%m/%y")
=> Fri, 10 Nov 1989
Date.strptime("15/10/63","%d/%m/%y")
=> Mon, 15 Oct 2063
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
When using %y in strptime, the code assumes that values under 68 are considered in the 21st century, as descirbed here:
The year within century (0-99). When a century is not otherwise specified (with a value for %C), values in the range 69-99 refer to years in the twentieth century (1969-1999); values in the range 00-68 refer to years in the twenty-first century (2000-2068).
In the chronic gem, incidentally, the cut-off year is 64:
Chronic.parse('15/10/64')
# => 1964-10-15 12:00:00 +0200
Chronic.parse('15/10/63')
# => 2063-10-15 12:00:00 +0300
Chronic gem has added an extended support parsing 2 digit years with ambiguous_year_future_bias option:
irb(main):029:0> Chronic.parse('15/10/99', ambiguous_year_future_bias: 10)
=> 2099-10-15 12:00:00 +0300
irb(main):030:0> Chronic.parse('15/10/99', ambiguous_year_future_bias: 50)
=> 1999-10-15 12:00:00 +0300
Add the Chronic gem to your Gemfile
gem 'chronic'
Then just parse it:
Chronic.parse("15/10/68")
=> 1968-10-15 12:00:00 -0700

Validating a Date - m/d/yyyy Doesn't Match mm/dd/yyyy?

I have a ruby script that checks a provided date, to make sure it is today's date. This is not working when the date provided doesn't have a 2 digit padding for the month. Is there anyway to get ruby to see that as equal? The example is that it says "Date Processed 3/13/2014 is not today's date 03/13/2014!" the difference is in the month - 3 vs 03. Below is the code. ev_val is provided from a csv and it is m/d/yyyy format. It is not provided with a 0 padding, though. Any thoughts?
Thanks!
tnow = Time.now
if ev_val != tnow.strftime("%m/%d/%Y")
log_linemsg = "Date Processed #{ev_val} is not today's date #{tnow.strftime("%m/%d/%Y")}! Processing date must be today's Date!!!\nSTOPPING SCRIPT!!!"
log_line = ["#{$cname}","#{log_linemsg}","","",]
puts log_linemsg
insert_logitems(connection, table_namelog, log_line)
exit
end
require "date"
date_val = Date.parse ev_val
today = Date.today
if today != date_val
log_linemsg = "Date Processed #{ev_val} is not today's date #{today}! Processing date must be today's Date!!!\nSTOPPING SCRIPT!!!"
end
Since you only care about the date portion, I would use Date instead of Time.
Take your input string and parse it into a Date object, then compare it to today's date.
?> date_val = Date.parse('3/13/2014')
=> Thu, 13 Mar 2014
>> date_val == Date.today
=> true
In your example Date.parse(ev_val) != Date.today should work for the comparison.

Rails 4 parse a date in a different language

I have a text_field :birthday_line in my user form, that I need to parse into the user's birthday attribute.
So I'm doing something like this in my User class.
attr_accessor :birthday_line
before_save :set_birthday
def set_birthday
self.birthday = Date.strptime(birthday_line, I18n.translate("date.formats.default")
end
But the problem is that for some reason it gives me an error saying Invalid date when I try to pass in a string 27 января 1987 г. wich should be parsed to 1987-01-27.
The format and month names in my config/locales/ru.yml
ru:
date:
formats:
default: "%d %B %Y г."
month_names: [~, января, февраля, марта, апреля, мая, июня, июля, августа, сентября, октября, ноября, декабря]
seem to be correct.
Date.parse also doesn't help, it just parses the day number (27) and puts the month and year to todays date (so it'll be September 27 2013 instead of January 27 1987).
I had the same problem and what I can suggest:
string_with_cyrillic_date = '27 Января 1987'
1)create array of arrays like this
months = [["января", "Jan"], ["февраля", "Feb"], ["марта", "Mar"], ["апреля", "Apr"], ["мая", "May"], ["июня", "Jun"], ["июля", "Jul"], ["августа", "Aug"], ["сентября", "Sep"], ["октября", "Oct"], ["ноября", "Nov"], ["декабря", "Dec"]]
2) Now you can iterate this and find your cyrillic month:
months.each do |cyrillic_month, latin_month|
if string_with_cyrillic_date.match cyrillic_month
DateTime.parse string_with_cyrillic_date.gsub!(/#{cyrillic_month}/, latin_month)
end
end
And now you will receive the date that you expect
27 Jan 1987

Rails 3.2.8 - How do I get the week number from Rails?

I would like to know how to get the current week number from Rails and how do I manipulate it:
Translate the week number into date.
Make an interval based on week number.
Thanks.
Use strftime:
%U - Week number of the year. The week starts with Sunday. (00..53)
%W - Week number of the year. The week starts with Monday. (00..53)
Time.now.strftime("%U").to_i # 43
# Or...
Date.today.strftime("%U").to_i # 43
If you want to add 43 weeks (or days,years,minutes, etc...) to a date, you can use 43.weeks, provided by ActiveSupport:
irb(main):001:0> 43.weeks
=> 301 days
irb(main):002:0> Date.today + 43.weeks
=> Thu, 22 Aug 2013
irb(main):003:0> Date.today + 10.days
=> Sun, 04 Nov 2012
irb(main):004:0> Date.today + 1.years # or 1.year
=> Fri, 25 Oct 2013
irb(main):005:0> Date.today + 5.months
=> Mon, 25 Mar 2013
You are going to want to stay away from strftime("%U") and "%W".
Instead, use Date.cweek.
The problem is, if you ever want to take a week number and convert it to a date, strftime won't give you a value that you can pass back to Date.commercial.
Date.commercial expects a range of values that are 1 based.
Date.strftime("%U|%W") returns a value that is 0 based. You would think you could just +1 it and it would be fine. The problem will hit you at the end of a year when there are 53 weeks. (Like what just happened...)
For example, let's look at the end of Dec 2015 and the results from your two options for getting a week number:
Date.parse("2015-12-31").strftime("%W") = 52
Date.parse("2015-12-31").cweek = 53
Now, let's look at converting that week number to a date...
Date.commercial(2015, 52, 1) = Mon, 21 Dec 2015
Date.commercial(2015, 53, 1) = Mon, 28 Dec 2015
If you blindly just +1 the value you pass to Date.commercial, you'll end up with an invalid date in other situations:
For example, December 2014:
Date.commercial(2014, 53, 1) = ArgumentError: invalid date
If you ever have to convert that week number back to a date, the only surefire way is to use Date.cweek.
date.commercial([cwyear=-4712[, cweek=1[, cwday=1[, start=Date::ITALY]]]]) → date
Creates a date object denoting the given week date.
The week and the day of week should be a negative
or a positive number (as a relative week/day from the end of year/week when negative).
They should not be zero.
For the interval
require 'date'
def week_dates( week_num )
year = Time.now.year
week_start = Date.commercial( year, week_num, 1 )
week_end = Date.commercial( year, week_num, 7 )
week_start.strftime( "%m/%d/%y" ) + ' - ' + week_end.strftime("%m/%d/%y" )
end
puts week_dates(22)
EG: Input (Week Number): 22
Output: 06/12/08 - 06/19/08
credit: Siep Korteling http://www.ruby-forum.com/topic/125140
Date#cweek seems to get the ISO-8601 week number (a Monday-based week) like %V in strftime (mentioned by #Robban in a comment).
For example, the Monday and the Sunday of the week I'm writing this:
[ Date.new(2015, 7, 13), Date.new(2015, 7, 19) ].map { |date|
date.strftime("U: %U - W: %W - V: %V - cweek: #{date.cweek}")
}
# => ["U: 28 - W: 28 - V: 29 - cweek: 29", "U: 29 - W: 28 - V: 29 - cweek: 29"]

Resources