Parsing a string to a date in Ruby - ruby

I know this sounds like a repeated question but I think this situation is different. I will delete this post if it truly is.
I have a string containing a date in the following format: Thu, Jun. 20
I would like to parse this into a Date variable and then increment it to the next day.
So far I have done
text = "Thu, Jun. 20"
date = Date.new
date = Date.strptime(text, '{%a, %m, %d}')
But this gives me the following error:
invalid date (ArgumentError)
I got this idea from: Ruby: convert string to date
All answers I have seen so far have been parsing strings that contain full information (the full month or day of the week). Is what I'm trying to do even possible ? If not any suggestions on a work around would be most appreciated.

You used wrong date format. After parse it, you can use plus or minus operator to change date.
Reference: http://www.ruby-doc.org/stdlib-1.9.3/libdoc/date/rdoc/DateTime.html#method-i-strftime
%a - The abbreviated name (``Sun'')
%b - The abbreviated month name (``Jan'')
%d - Day of the month, zero-padded (01..31)
Code:
1.9.3p392 :003 > require 'date'
=> true
1.9.3p392 :008 > date = Date.strptime(text, '%a, %b. %d')
=> #<Date: 2013-06-20 ((2456464j,0s,0n),+0s,2299161j)>
1.9.3p392 :009 > date + 1
=> #<Date: 2013-06-21 ((2456465j,0s,0n),+0s,2299161j)>
1.9.3p392 :010 > date - 1
=> #<Date: 2013-06-19 ((2456463j,0s,0n),+0s,2299161j)>

To answer your need I would like to parse this into a Date variable and then increment it to the next day. I tried below :
require 'date'
d = Date.parse("Thu, Jun. 20")
# => #<Date: 2013-06-20 ((2456464j,0s,0n),+0s,2299161j)>
d.to_s # => "2013-06-20"
d.next.to_s # => "2013-06-21"

Related

Date.parse fails when system date is 2017-02-01

I'm experiencing an strange issue with Date.parse method.
I tried several ruby versions and it happens in all of them. The tests below were run in version 2.1.10.
Yesterday all my tests were passing but today they started to fail. The cause is a Date.parse call that started to raise an exception.
If system date is 2017-01-31, it works fine:
2.1.10 :002 > system('date')
Ter 31 Jan 2017 11:24:08 BRST
=> true
2.1.10 :003 > Date.parse("29%2F10%2F2015")
=> #<Date: 2017-01-29 ((2457783j,0s,0n),+0s,2299161j)>
But if system date is today, it fails:
2.1.10 :002 > system('date')
Qua 1 Fev 2017 11:24:27 BRST
=> true
2.1.10 :003 > Date.parse("29%2F10%2F2015")
ArgumentError: invalid date
from (irb):3:in `parse'
from (irb):3
from /Users/fernando/.rvm/rubies/ruby-2.1.10/bin/irb:11:in `<main>'
I probably can get around this by using another method to parse this date but I'm interested in why it started to fail today.
Is 2017-02-01 a special date for ruby?
Date.parse is a method which tries to parse a date from the given string using a number of heuristics in order to support many different formats without specifying the actual format. Thus, unless the format is clear, it is always possible that Ruby come to different conclusions than you.
In order to get an idea how Ruby parses your string, you can use
Date._parse("29%2F10%2F2015")
# => {:mday=>29}
As you can see, Ruby is able to get the day of month as 29 from the passed string but doesn't get any additional information. In order to form a valid date, Ruby substitutes the missing parts from the current date. Now, since February 2017 only has 28 days, the resulting date is invalid here but would be valid in January.
Still, the result is not what you actually seem to want. Instead, try to first transform your date into a more easily parsed string and try again using the approach by Eric Duminil in another answer to this question:
require 'date'
require 'uri'
string = '29%2F10%2F2015'
Date.strptime(URI.unescape(string), '%d/%m/%Y')
# => #<Date: 2015-10-29 ((2457325j,0s,0n),+0s,2299161j)>
As you can see, with Date.strptime, you can specify the exact format of the parsed string and can thus be sure it either gets correctly parsed or errors out.
%2F is the URL Encoded value of the Forward Slash (/)
so you need to decode your url-encoded string first
> require 'open-uri'
#=> true
> string = "29%2F10%2F2015"
#=> "29%2F10%2F2015"
> date = URI::decode(string)
#=> "29/10/2015"
> Date.parse(date)
#=> #<Date: 2015-10-29 ((2457325j,0s,0n),+0s,2299161j)>
Is 2017-02-01 a special date for ruby?
no, it's not special ;)
> s = "2017-02-01"
> Date.parse(s)
#=> #<Date: 2017-02-01 ((2457786j,0s,0n),+0s,2299161j)>
You have three problems :
'%2F' shouldn't be here
'2017-02-01' could be "February 1" or "January 2".
Date.parse relies on system date to parse the string.
If you know which format you have, you really should use Date.strptime :
require 'date'
require 'uri'
def parse_url_date(url_date)
Date.strptime(URI.unescape(url_date), '%d/%m/%Y')
end
puts parse_url_date("29%2F10%2F2015")
#=> 2015-10-29
puts parse_url_date("01%2F02%2F2017")
#=> 2017-02-01
If you know your "dates" are URL-encoded then you have to URL-decode them first. Use URI.unescape() for this then pass the value it returns to Date.parse().
Date.parse(URI.unescape("29%2F10%2F2015"))

how to convert string to date object in ruby

I am getting a date as a string like below:
"September 1998"
I tried like Date.parse("September 1998"), but it did not work.
How do I convert it into a ruby date object which returns string in above format?
Date.strptime('September 1998', '%B %Y'). However, this will represent September 1st 1998, because date objects represent, well, dates.
You could use the chronic gem:
require 'chronic'
t = Chronic.parse('September 1998', :guess => true) #returns a Time object
=> 1998-09-01 00:00:00 -0700
t.to_date #convert to Date object
=> <Date: 1998-09-16 ((2451073j,0s,0n),+0s,2299161j)>
Chronic was created by Tom Preston-Werner, who also co-created Github.
Just prepend the missing "1 ":
str ="September 1998"
p Date.parse("1 " + str) # => #<Date: 1998-09-01 ((2451058j,0s,0n),+0s,2299161j)>

ruby parse date using strptime and strftime

So I'm parsing this string from AIX's errpt - to convert it into epoch - and it doesn't seem to be respecting the Hour and Minute part of the string.
So the string is: 1108095913 (MMDDHHMMYY) .. but when I do my strptime to convert it to a date object, and then format it how I want, it completely zero'd out my hour and minute.
Am I missing something?
irb(main):039:0> Date.strptime("1108095913", "%m%d%H%M%y").strftime('%m/%d/%y %H:%M')
=> "11/08/13 00:00"
You should use Time.strptime instead of Date method, Date removes hours and minutes
1.9.3-p429 :005 > Time.strptime("1108095913", "%m%d%H%M%y").strftime('%m/%d/%y %H:%M')
=> "11/08/13 09:59"
Use DateTime instead of Date:
irb(main):002:0> require 'date'
=> true
irb(main):003:0> DateTime.strptime("1108095913", "%m%d%H%M%y").strftime('%m/%d/%y %H:%M')
=> "11/08/13 09:59"
The reason is DateTime handles date and time, both and Date only handles date.
Hope this helps!

How to get the current month with Sequel

I would like recover a list of entries for the current month with Sequel.
I tried:
Entry.where(:date >= Date.month).sum(:duration)
or
Entry.where(:date.like('%/06/2013')).sum(:duration)
and other ways, but none of them seemed to work.
If you want all entries the current month and the current year, it's probably easiest to use a range:
d = Date.today
Entry.where(:date=> Date.new(d.year, d.month)...(Date.new(d.year, d.month) >> 1)).sum(:duration)
If you want the current month in any year, Sequel has built in support for this:
Entry.where(Sequel.extract(:month, :date) => Date.today.month).sum(:duration)
You'll need to think in terms of how a database thinks, and how Sequel turns Ruby ranges into SQL:
require 'date'
today = Date.today # => #<Date: 2013-07-03 ((2456477j,0s,0n),+0s,2299161j)>
first_of_month = (today - today.day) + 1
first_of_month # => #<Date: 2013-07-01 ((2456475j,0s,0n),+0s,2299161j)>
next_month = today + 31 # => #<Date: 2013-08-03 ((2456508j,0s,0n),+0s,2299161j)>
last_of_month = next_month - next_month.day # => #<Date: 2013-07-31 ((2456505j,0s,0n),+0s,2299161j)>
last_of_month # => #<Date: 2013-07-31 ((2456505j,0s,0n),+0s,2299161j)>
Entry.where(:date => [first_of_month .. last_of_month]).sum(:duration)
I'd show you the SQL output, but I don't know the database type you're using, and, well, I'm lazy.
Often, you can play tricks inside the DB by truncating "now" to remove the day, then finding all timestamps whose truncated date matches it. That's a lot more specific to the DBM than using Sequel, which already knows how to deal with ranges when converting them to a "between"-type statement.

Time.parse produces strange dates

Wondering what happened with these inputs that gave me something strange instead of 2013-05-31 13:30:00 -0400
Time.parse("05-31 13:30") => 2013-06-06 16:30:00 -0400
Time.parse("5 31 13:30") => 2013-07-01 13:30:00 -0400
#SergioTulentsev's comment points to the problem. Date.parse can't know every possible combination of ways people might want to structure a date/datetime value. That's why Date supports strptime, which lets YOU define the pattern:
require 'date'
DateTime.strptime("05-31 13:30", '%m-%d %H:%M')
=> #<DateTime: 2013-05-31T13:30:00+00:00 ((2456444j,48600s,0n),+0s,2299161j)>
DateTime.strptime("5 31 13:30", '%m %d %H:%M')
=> #<DateTime: 2013-05-31T13:30:00+00:00 ((2456444j,48600s,0n),+0s,2299161j)>
The problem then becomes one of which format string to use for a given date string. In this test I changed the second datetime string so it's more obvious that the code is working correctly:
require 'date'
DATE_PATTERNS = {
/[0-2]\d-\d{2} \d{2}:\d{2}/ => '%m-%d %H:%M',
/[0-2]?\d \d{1,2} \d{2}:\d{2}/ => '%m %d %H:%M'
}
puts ["05-31 13:30", "5 31 13:31"].map { |str|
pattern = DATE_PATTERNS.keys.find { |k|
str[k]
}
puts pattern.source
DateTime.strptime(str[pattern], DATE_PATTERNS[pattern]).to_s
}
Which outputs:
[0-2]\d-\d{2} \d{2}:\d{2}
[0-2]?\d \d{1,2} \d{2}:\d{2}
2013-05-31T13:30:00+00:00
2013-05-31T13:31:00+00:00
In your time-string the year is missing.
parse tries to fix this but fails.
With the year given - like so
Time.parse("2013-05-31 13:30")
parse works.

Resources