`Date.strptime` unexpected behaviour - ruby

I try to parse string "01.09 2015" with "%d.%m %y", but it returns year 2020.
require 'date'
year = Time.now.year.to_s # => "2015"
ny = (Time.now.year+1).to_s # => "2016"
sem = "01.09"
Date.strptime(sem+" "+year, "%d.%m %y")
# => #<Date: 2020-09-01 ((2459094j,0s,0n),+0s,2299161j)>
I don't get what's happening here.

You need to replace %y (meaning year % 100 (00..99)) with %Y (4 digits year):
Date.strptime("01.09 2015", "%d.%m %Y")

Related

Matching a sliced string

I am trying to use the case statement:
week # => "03 – 09 MAR 2019"
first_day = week.slice(0..2) # => 03
last_day = week.slice(5..7) # => 09
month = week.slice(8..11) # => MAR
year = week.slice(12..17) # => 2019
puts month # >> MAR
case month
when 'JAN' then month_num = '01'
when 'FEB' then month_num = '02'
when 'MAR' then month_num = '03'
when 'APR' then month_num = '04'
when 'MAY' then month_num = '05'
when 'JUN' then month_num = '06'
when 'JUL' then month_num = '07'
when 'AGO' then month_num = '08'
when 'SEP' then month_num = '09'
when 'OCT' then month_num = '10'
when 'NOV' then month_num = '11'
when 'DEC' then month_num = '12'
else month_num = 'ERROR'
end
puts month_num # >> ERROR
However, the case statement always goes to the else branch.
Why is the var month_num equal to the string "ERROR" instead of "03"?
You are using puts to examine what you have, and therefore you are missing to observe whitespaces in your results. You actually have:
week.slice(0..2) # => "03 "
week.slice(5..7) # => "09 "
week.slice(8..11) # => "MAR "
week.slice(12..17) # => "2019"
To observe what you have, it is better to use p rather than puts.
You have the wrong range. Actually, there is no reason to use ranges here. It is much easier to use the second argument to specify the length:
week.slice(0, 2) # => "03"
week.slice(5, 2) # => "09"
week.slice(8, 3) # => "MAR"
week.slice(12, 4) # => "2019"
Your month is "MAR "
Try
month = week.slice(8..10)
And makes sense, from 8 to 10 inclusive are three characters. Same for the other parts.
Seems like you want to parse a string containing data in a specific format. Instead of relying on absolute indices, you could use a regular expression to match the date format, e.g:
PATTERN = /
(?<first_day>\d{2}) # 2-digit first day
\s* # optional whitespace
[–-] # delimiter(s)
\s*
(?<last_day>\d{2}) # 2-digit last day
\s*
(?<month>\w{3}) # 3-letter month name
\s*
(?<year>\d{4}) # 4-digit year
/ix
To extract the data:
str = '03 – 09 MAR 2019'
m = str.match(PATTERN)
#=> #<MatchData "03 – 09 MAR 2019" first_day:"03" last_day:"09" month:"MAR" year:"2019">
m[:first_day] #=> "03"
m[:last_day] #=> "09"
m[:month] #=> "MAR"
m[:year] #=> "2019"
The results could further be fed into Date.strptime:
require 'date'
from = m.values_at(:first_day, :month, :year).join(' ') #=> "03 MAR 2019"
to = m.values_at(:first_day, :month, :year).join(' ') #=> "09 MAR 2019"
Date.strptime(from, '%d %b %Y') #=> #<Date: 2019-03-03 ...>
Date.strptime(to, '%d %b %Y') #=> #<Date: 2019-03-09 ...>
Or _strptime if you're just interested in the raw values:
Date._strptime(from, '%d %b %Y')
#=> {:mday=>3, :mon=>3, :year=>2019}

Ruby date-time string to epoch time local

I have date-time represented in string format like below in the input data I'm getting. I need to convert this to epoch time local. How do I go about this ?
example date-time string
str = "Aug 23 2018 03:49:17:017 PM IST"
Maybe this can put you on track.
Please refer to http://ruby-doc.org/stdlib-2.5.0//libdoc/date/rdoc/DateTime.html.
This is a usage example.
require 'date'
str = "Aug 23 2018 03:49:17:017 PM IST"
d = DateTime.strptime(str, '%b %d %Y %I:%M:%S:%L %p %z')
# Maybe you need %e - Day of the month, blank-padded ( 1..31) instead of %d - Day of the month, zero-padded (01..31)
p d.hour # => 15
p d.min # => 49
p d.sec # => 17
p d.second_fraction # => (17/1000)
p d.day # => 23
p d.month # => 8
p d.year # => 2018
p d.zone # => "+05:30"
# this way you can switch timezone
d = d.new_offset('utc')
p d.zone # => "+00:00"
p d.hour # => 10
d = d.new_offset('brt')
p d.hour # => 7
p d.zone # => "-03:00"
# to print back as string, use strftime:
p d.strftime('%Y-%m-%d %I:%M:%S %z') # => "2018-08-23 07:19:17 -0300"

How to convert date in to string words like that input 11-12-2001 output should be eleven December two thousand one, in ruby

How to convert date in to string words in ruby
Input date
2-12-2002
Output
Two December two thousand two
require 'humanize'
day, month, year = '2-12-2002'.split('-')
month_numeric_range = (1..12).to_a.map(&:to_s)
month_words = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']
month_numeric_to_words = Hash[month_numeric_range.zip(month_words)]
day.to_i.humanize.capitalize + ' ' + months_numeric_to_words[month] + ' ' + year.to_i.humanize.capitalize
# => "Two December Two thousand and two"
This is a partial option using Class: Date:
require 'date'
date_string = "2-12-2002"
date = Date.strptime(date_string, "%d-%m-%Y")
p date.day # => 2
p date.month # => 12
p date.year # => 2002
p date.strftime("%d %B %Y") # => "02 December 2002"
If you want also to spell numbers, check this out:
Using Ruby convert numbers to words?
Number to English Word Conversion Rails

Invalid date ArgumentError and incorrect parse on a valid date?

dates = ["11/12/08 10:47", "11/12/08 13:23", "11/12/08 13:30",
"11/25/08 19:21", "2/2/09 11:29", "11/12/08 15:00"]
This throws an invalid argument error:
dates.each do |date|
d = Date.parse(date)
d.mon
end
#=> ArgumentError: invalid date
But take the first date in dates and this is the output:
d = Date.parse('11/12/08 10:47')
puts d.mon
#=> #<Date: 2011-12-08 ((2455904j,0s,0n),+0s,2299161j)>
#=> 12 but this should be 11
In the first example why am I getting an invalid ArgumentError?
In example 2, why is the Date object created with the mon and day swapped?
Given your input, Date.parse is parsing your dates assuming they are in the format YY/MM/DD, so when it try to parse 11/25/08 it fails because 25 is not a valid month:
d = Date.parse('11/12/08 10:47')
d.year
# => 2011
d.month
# => 12
d.day
# => 8
Date.parse('11/25/08 19:21')
# ArgumentError: invalid date
Given that your dates are all in the same format, you should use the Date.strptime method instead:
d = Date.strptime('11/12/08 10:47', '%m/%d/%y')
d.year
# => 2008
d.month
# => 11
d.day
# => 12
Date.strptime('11/25/08 19:21', '%m/%d/%y')
# => #<Date: 2008-11-25 ((2454796j,0s,0n),+0s,2299161j)>
Edit Instead of the format string %m/%d/%y the shortcut %D can be used:
Date.strptime('11/25/08 19:21', '%D')
# => #<Date: 2008-11-25 ((2454796j,0s,0n),+0s,2299161j)>
Ruby's Date.parse is expecting either a YYYY-MM-DD (see also ISO8601 for more information) or a DD-MM-YYYY as well but not DD-MM-YY (i.e. 2 digits only for year). The last is treated instead as YY-MM-DD.

Is there an add_days in ruby datetime?

In C#, There is a method AddDays([number of days]) in DateTime class.
Is there any kind of method like this in ruby?
The Date class provides a + operator that does just that.
>> d = Date.today
=> #<Date: 4910149/2,0,2299161>
>> d.to_s
=> "2009-08-31"
>> (d+3).to_s
=> "2009-09-03"
>>
In Rails there are very useful methods of Fixnum class for this (here n is Fixnum. For example: 1,2,3.... ):
Date.today + n.seconds # you can use 1.second
Date.today + n.minutes # you can use 1.minute
Date.today + n.hours # you can use 1.hour
Date.today + n.days # you can use 1.day
Date.today + n.weeks # you can use 1.week
Date.today + n.months # you can use 1.month
Date.today + n.years # you can use 1.year
These are convenient for Time class too.
PS: require Active Support Core Extensions to use these in Ruby
require 'active_support/core_ext'
I think next_day is more readable than + version.
require 'date'
DateTime.new(2016,5,17)
# => #<DateTime: 2016-05-17T00:00:00+00:00 ((2457526j,0s,0n),+0s,2299161j)>
DateTime.new(2016,5,17).next_day(10)
# => #<DateTime: 2016-05-27T00:00:00+00:00 ((2457536j,0s,0n),+0s,2299161j)>
Date.new(2016,5,17)
# => #<Date: 2016-05-17 ((2457526j,0s,0n),+0s,2299161j)>
Date.new(2016,5,17).next_day(10)
# => #<Date: 2016-05-27 ((2457536j,0s,0n),+0s,2299161j)>
http://ruby-doc.org/stdlib-2.3.1/libdoc/date/rdoc/Date.html#method-i-next_day.
From the Date class:
+(n)
Return a new Date object that is n days later than the current one.
n may be a negative value, in which case the new Date is earlier than the current one; however, #-() might be more intuitive.
If n is not a Numeric, a TypeError will be thrown. In particular, two Dates cannot be added to each other.
Date.new(2001,9,01).next_day(30) # 30 - numbers of day
# => #<Date: 2001-10-01 ...
You can also use the advance (https://apidock.com/rails/DateTime/advance) method. I think it's more legible.
date = Date.today
# => Fri, 25 Oct 2019
date.advance(days: 10)
# => Mon, 04 Nov 2019
time = DateTime.now
# => Fri, 25 Oct 2019 14:32:53 +0200
time.advance(months:1, weeks: 2, days: 2, hours: 6, minutes: 6, seconds: 34)
# => Wed, 11 Dec 2019 20:39:27 +0200

Resources