What's up with Ruby Time including dates? - ruby

I love ruby's magic, but every so often you run across something that seems like a no-brainer.
Here's my problem:
I'm storing a value as Time in my DB using MySQL's Time Column type.
This store just the hh:mm:ss values.
Later when doing comparison's Ruby will grab this value and append 2001-01-01 hh:mm:ss UTC to it. (Record1 below was taken from my DB and is an ActiveRecord object obtained from a collection that happens to have a time attribute stored using MYSQL's Time function)
So it appears that my only option for useful comparions's is to use strftime? Is that right?
(ruby 1.9.2p290 (2011-07-09 revision 32553) [x86_64-darwin10.8.0])
ruby-1.9.2-p290 :009 > record1.time
=> 2000-01-01 08:30:00 UTC
ruby-1.9.2-p290 :001 > time1 = "08:30:00"
=> "08:30:00"
ruby-1.9.2-p290 :002 > #time_obj1 = Time.parse time1
=> 2012-01-27 08:30:00 -0700
ruby-1.9.2-p290 :003 > #time_obj1.to_s
=> "2012-01-27 08:30:00 -0700"
ruby-1.9.2-p290 :004 >

So it appears that my only option for useful comparions's is to use
strftime? Is that right?
That, or you could do something like
timestring = "#{#time_obj1.hour}:#{#time_obj1.min}:#{#time_obj1.sec}"
But strftime is quite a few less characters, and the more standard way to achieve what you're after.

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

ruby DateTime parsing from 'mm/dd/yyyy' format

I am using ruby 1.9.3 and want to get Date or Time object from 'mm/dd/yyyy' date format string
Time.zone.parse("12/22/2011")
this is giving me *** ArgumentError Exception: argument out of range
require 'date'
my_date = Date.strptime("12/22/2011", "%m/%d/%Y")
As above, use the strptime method, but note the differences below
Date.strptime("12/22/2011", "%m/%d/%Y") => Thu, 22 Dec 2011
DateTime.strptime("12/22/2011", "%m/%d/%Y") => Thu, 22 Dec 2011 00:00:00 +0000
Time.strptime("12/22/2011", "%m/%d/%Y") => 2011-12-22 00:00:00 +0000
(the +0000 is the timezone info, and I'm now in GMT - hence +0000. Last week, before the clocks went back, I was in BST +0100. My application.rb contains the line config.time_zone = 'London')
Try Time.strptime("12/22/2011", "%m/%d/%Y")
Would it be an option for you to use Time.strptime("01/28/2012", "%m/%d/%Y") in place of Time.parse? That way you have better control over how Ruby is going to parse the date.
If not there are gems: (e.g. ruby-american_date) to make the Ruby 1.9 Time.parse behave like Ruby 1.8.7, but only use it if it's absolutely necessary.
1.9.3-p0 :002 > Time.parse '01/28/2012'
ArgumentError: argument out of range
1.9.3-p0 :003 > require 'american_date'
1.9.3-p0 :004 > Time.parse '01/28/2012'
=> 2012-01-28 00:00:00 +0000

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"

Ruby Date Subtraction (e.g. 90 days Ago)

I've been a bit spoiled by the joda-time API of:
DateTime now = new DateTime();
DateTime ninetyDaysAgo = now.minusDays(90);
I'm trying to do a similar thing in Ruby, but I'm
now = Time.now
ninetyDaysAgo = now - (90*24)
However, the math is off here (I'm really working with dates at midnight).
Is there friendly API for date subtraction?
require 'date'
now = Date.today
ninety_days_ago = (now - 90)
Running this thru the IRB console I get:
>>require 'date'
now = Date.today
ninety_days_ago = (now - 90)
require 'date'
=> false
now = Date.today
=> #<Date: 2011-03-02 (4911245/2,0,2299161)>
ninety_days_ago = (now - 90)
=> #<Date: 2010-12-02 (4911065/2,0,2299161)>
If you need the time you could just say now = DateTime.now
For those using Rails, check out the following:
DateTime.now - 10.days
=> Sat, 04 May 2013 12:12:07 +0300
20.days.ago - 10.days
=> Sun, 14 Apr 2013 09:12:13 UTC +00:00
If you're using Rails or don't mind including ActiveSupport, you can use the Numeric#days DSL like this:
ruby-1.9.2-p136 :002 > Date.today
=> Wed, 02 Mar 2011
ruby-1.9.2-p136 :003 > Date.today - 90.days
=> Thu, 02 Dec 2010
Since you are working with dates instead of times, you should also either start with Date instances, or convert your DateTime intances with #to_date. When adding/subtracting numbers from date instances, the numbers are implicitly days.
ruby-1.9.2-p136 :016 > DateTime.now.to_date
=> #<Date: 2011-03-02 (4911245/2,0,2299161)>
ruby-1.9.2-p136 :017 > DateTime.now.to_date - 90
=> #<Date: 2010-12-02 (4911065/2,0,2299161)>
Ruby supports date arithmetic in the Date and DateTime classes, which are part of Ruby's standard library. Both those classes expose #+ and #- methods, which add and subtract days from a date or a time.
$ irb
> require 'date'
=> true
> (DateTime.new(2015,4,1) - 90).to_s # Apr 1, 2015 - 90 days
=> "2015-01-01T00:00:00+00:00"
> (DateTime.new(2015,4,1) - 1).to_s # Apr 1, 2015 - 1 day
=> "2015-03-31T00:00:00+00:00"
Use the #<< and #>> methods to operate on months instead of days. Arithmetic on months is a little different than arithmetic on days. Using Date instead of DateTime makes the effect more obvious.
> (Date.new(2015, 5, 31) << 3).to_s # May 31 - 3 months; 92 days diff
=> "2015-02-28"
Following your joda-time example, you might write something like this in Ruby.
now = DateTime.now
ninety_days_ago = now - 90
or maybe just
ninety_days_ago = DateTime.now - 90
use the number of seconds:
Time.now - 90*24*60*60
This is a super old post, but if you wanted to keep with a Time object, like was originally asked, rather than switching to a Date object you might want to consider using Ruby Facets.
Ruby Facets is a standardized library of extensions for core Ruby classes.
http://rubyworks.github.io/facets/
By requiring Facets you can then do the following with Time objects.
Time.now.less(90, :days)
Simple solution using Rails Active Support:
days90_ago = 90.days.ago.to_date.to_s
OUTPUT:
puts 90_days_ago
=> "2019-10-09" # considering cur_date: 2020-01-07

Ruby can only show local time and Greenwich Mean Time (UTC) but not East Coast Time or Central Time?

$ irb
ruby-1.9.2-p0 > RUBY_DESCRIPTION
=> "ruby 1.9.2p0 (2010-08-18 revision 29036) [x86_64-darwin10.4.0]"
ruby-1.9.2-p0 > Time.now
=> 2010-09-07 18:18:38 -0700
ruby-1.9.2-p0 > Time.now.gmtime
=> 2010-09-08 01:18:43 UTC
ruby-1.9.2-p0 > Time.now.strftime('%H:%M %Z')
=> "18:22 PDT"
ruby-1.9.2-p0 > Time.now.gmtime.strftime('%H:%M %Z')
=> "01:22 UTC"
But I don't see any method to show the time in East Coast or Central / Mountain time, or to change the zone to something other than GMT? There seems to have no way to tell strftime() to use a particular time zone when displaying the time.
Check out the tzinfo gem.

Resources