Time.parse produces strange dates - ruby

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.

Related

How to avoid from storing date objects in array in Ruby?

Consider below code:
dates = ["20th OCT 1232", "6th JUN 2019", "23th AUG 2017", "9th JAN 2015"]
def reformateDate(dates)
ans = []
dates.length.times do |i|
ans << (DateTime.parse(dates[i], '%d %b %Y').to_date)
end
ans
end
This function return array in below format:
[#<Date: 1232-10-20 ((2171339j,0s,0n),+0s,2299161j)>, #<Date: 2019-06-06 ((2458641j,0s,0n),+0s,2299161j)>, #<Date: 2017-08-23 ((2457989j,0s,0n),+0s,2299161j)>, #<Date: 2015-01-09 ((2457032j,0s,0n),+0s,2299161j)>]
But i want it to return array in this format:
["1232-10-20","2019-06-06","2017-08-23","2015-01-09"]
So how can i do this?
dates.map { |e| Date.parse(e).strftime('%Y-%m-%d') }
#=> ["1232-10-20", "2019-06-06", "2017-08-23", "2015-01-09"]
Change the template '%Y-%m-%d' according to your needs, see this for reference: Date#strftime.
Picking up the wise suggestion from Cary Swoveland.
Instead of Date.parse(e) you can use Date.strptime(e, '%dth %b %Y'), which works more or less the reverse of strftime. See Date#strptime. It follows a template ('%dth %b %Y') to interpret the original string as a date. Adding th to the template after %d (day), it converts properly the current format to a date object:
Date.strptime("20th OCT 1232", '%dth %b %Y') #=> #<Date: 1232-10-20 ((2171339j,0s,0n),+0s,2299161j)>
But, what if the date is '1st OCT 2018' or '23rd OCT 2018'? The template does not match, because it expects to find th and not st or rd.
To be ordinal suffix agnostic, comes in hand the method String#sub:
"20th OCT 1232".sub(/(?<=\d)\p{Alpha}+/, '') #=> "20 OCT 1232"
So, mixing all together, the best solution to be safe should be:
dates.map { |e| Date.strptime(e.sub(/(?<=\d)\p{Alpha}+/, ''), '%d %b %Y').strftime('%Y-%m-%d') }
Well, you're actually storing Date objects when you write:
ans << (DateTime.parse(dates[i], '%d %b %Y').to_date)
There's a couple of problems with this: First, the parenthesis don't do anything, so you can remove them. Second, what you're doing is parsing a string into a DateTime object, and then converting it into a Date object. Not really sure why you would do that, but I believe it's a mistake. If you want to convert this to a string by temporally using DateTime objects, consider using strftime, which will take the DateTime object and turn it into a string with a specific format. It would look like this:
ans << DateTime.parse(dates[i], '%d %b %Y').strftime('%Y-%m-%d')
I would do something like this:
require 'date'
def reformat_date(dates)
dates.map { |date| Date.parse(date).to_s }
end
dates = ["20th OCT 1232", "6th JUN 2019", "23th AUG 2017", "9th JAN 2015"]
reformat_date(dates)
#=> ["1232-10-20", "2019-06-06", "2017-08-23", "2015-01-09"]

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

Parsing a string to a date in 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"

Ruby DateTime.Parse to local time

I have a date string 20101129220021, so I will use
require 'date'
d = DateTime.parse('20101129220021')
This part works fine, and I get a date, which is in UTC.
My question is, how can I convert this into my local time? I tried many methods like extracting the time part using d.to_time and manipulate the result, but it didn't work. As far as I know, DateTime object is immutable. Can I please get some help?
irb(main):001:0> require "date"
=> true
irb(main):002:0> d = DateTime.parse('20101129220021')
=> #<DateTime: 2010-11-29T22:00:21+00:00 (70719276007/28800,0/1,2299161)>
irb(main):003:0> d.to_time
=> 2010-11-30 00:00:21 +0200
ruby 1.9.2p180 (2011-02-18)
You can add a rational fraction based on the timezone to get the local time.
require 'date'
# Make this whatever your zone is. Using UTC +0300 here.
ZONE = 3
d = DateTime.parse('20101129220021') + Rational(ZONE,24)
d.to_s # => "2010-11-30T01:00:21+00:00"

How do I create a Ruby date object from a string?

How do I create a Ruby date object from the following string?
DD-MM-YYYY
Date.parse('31-12-2010')
Alternatively Date#strptime(str, format).
Because in the USA they get the dates backwards, it's important not to simply use Date.parse() because you'll find 9/11/2001 can be 11 September 2001 in the USA and 9 November 2001 in the rest of the world. To be completely unambiguous use Date::strptime(your_date_string,"%d-%m-%Y") to correctly parse a date string of format dd-mm-yyyy.
Try this to be sure:
>irb
>> require 'date'
=> true
>> testdate = '11-09-2001'
=> "11-09-2001"
>> converted = Date::strptime(testdate, "%d-%m-%Y")
=> #<Date: 4918207/2,0,2299161>
>> converted.mday
=> 11
>> converted.month
=> 9
>> converted.year
=> 2001
For other strptime formats see http://pubs.opengroup.org/onlinepubs/009695399/functions/strptime.html
Also I always make sure I set my base timezone to :utc if my website is going to be handling any dates, and use Javascript on the client side to display local times.
You can use Time#parse.
Time.parse("20-08-2010")
# => Fri Aug 20 00:00:00 +0200 2010
However, because Ruby could parse the date as "MM-DD-YYYY", the best way is to go with DateTime#strptime where you can specify the input format.
If you have control over the format of the date in the string, then Date.parse works fine internationally with strings in YYYY-MM-DD (ISO 8601) format:
Date.parse('2019-11-20')
I find this approach simpler since it avoid having to specify the date format for the parser:
date1 = Time.local(2012, 1, 20, 12, 0, 0).to_date
Like this You can get time Object from a string like this:
t = Time.parse "9:00 PM"
=> 2013-12-24 21:00:00 +0530
t = Time.parse "12:00 AM"
=> 2013-12-24 00:00:00 +0530
But Ruby parsing this as a Date!
So you can use the column as a string.
add_column :table_name, :from, :string, :limit => 8, :default => "00:00 AM", :null => false
add_column :table_name, :to, :string, :limit => 8, :default => "00:00 AM", :null => false
And you can assign string object to the attribute,
r.from = "05:30 PM"
r.save
And parse the string for getting time object,
Time.zone.parse("02:00 PM")
Not necessary for this particular string format, but best string to time parsing utility I know is Chronic which is available as a gem and works for about 99.9% of usecases for human formatted dates/times.

Resources