Ruby on Rails 3: Why is the conversion from Date to seconds different from Time to seconds - ruby

In Ruby I am trying to convert a Date into a format that is usable by the HighCharts JavaScript charting library. Odd thing is when I convert the Date to seconds it converts differently than when I convert a Time to seconds and differently when I convert a DateTime to seconds. Due to this difference in conversion the dates displayed on the Graph can be as much as 1 date behind.
I am sure this has something to do with Rails and how it handles conversion from UTC to Local. If someone could explain to me the details I would greatly appreciate it.
In my examples below I use the same date '2011/05/02' but the seconds come out to be different.
Examples:
Date.new(2011, 5, 2).to_time.to_i * 1000
=> 1304265600000
=> 05/01/2011
Time.utc(2011, 5, 2).to_i * 1000
=> 1304294400000
=> 05/02/2011
Date.new(2011, 5, 2).to_datetime.to_i * 1000
=> 1304294400000
=> 05/02/2011

ruby-1.9.2-p180 :106 > Time.utc(2011, 5, 2)
=> 2011-05-02 00:00:00 UTC
ruby-1.9.2-p180 :107 > Date.new(2011, 5, 2).to_time
=> 2011-05-02 00:00:00 +0300
Date.to_time generates Time with timezone. That's your difference.
The quick fix/hack that popped instantly into my mind is:
Date.new(2011, 5, 2).to_time.utc.midnight
Edit:
http://apidock.com/rails/Date/to_time
Date.new(2011, 5, 2).to_time(:utc)

Related

Ruby Date parse method returning odd results around the year 0 and other pre-modern times

I am trying to use the Date.parse method in Ruby but it is returning odd results when I try to parse early historical dates. For example:
Date.parse("16 January 27 BC")
=> #<Date: -0026-01-16 >
This returns the correct year (although it is -26 instead of -27 but that is normal I think).
But when I try:
Date.parse("19 august 1 BC")
=> #<Date: 2000-08-19 >
Here it assumes the year 2000 as a start date. Same thing for later years:
Date.parse("19 August 14")
=> #<Date: 2014-08-19 >
yearninetynine = Date.parse("19 august AD 99")
=> #<Date: 1999-08-19 >
(I have also tried to replace AD with "CE" but that makes no difference)
The year 100 is where it starts to work properly again:
Date.parse("19 august AD 100")
=> #<Date: 0100-08-19 >
To summarize: up to the year 100 it doesn't parse properly and treats it as years occurring in the 1990s and 2000s, negative years are one less than you would intuitively expect, negative years close to 0 are also not parsed properly.
I tried a few other methods like start=Date::ITALY, but these seem to make no difference. Is there a way to make it parse these years properly?
According to the docs that is exactly what the 2nd argument is for.
Date.parse("19 august 99") #=> Thu, 19 Aug 1999
Date.parse("19 august 99", false) #=> Mon, 19 Aug 0099

How to compare two Time objects only down to the hour

I want to compare two Time objects only down to the hour, while ignoring the difference of minutes and seconds.
I'm currently using t0.strftime("%Y%m%d%H") == t1.strftime("%Y%m%d%H"), but I don't think this is a good solution.
Is there better way to do this?
You can use this trick in pure Ruby
t0.to_a[2..9] == t1.to_a[2..9]
Where Time#to_a
$> Time.now.to_a
# => [7, 44, 2, 8, 3, 2014, 6, 67, false, "GMT"]
# [ sec, min, hour, day, month, year, wday, yday, isdst, zone ]
So you can check that the times are equals or not up to the level you want and without missing important components of the object like the zone, etc.
If you have ActiveSupport (either through Rails, or just installed as a gem), it includes an extension to the Time class that adds a change method which will truncate times:
$> require "active_support/core_ext/time"
# => true
$> t = Time.now
# => 2014-03-07 21:30:01 -0500
$> t.change(hour: 0)
# => 2014-03-07 00:00:00 -0500
This won't modify the original time value either. So you can do this:
t0.change(minute: 0) == t1.change(minute: 0)
It'll zero out everything at a lower granularity (seconds, etc.).
require 'time'
t1 = Time.new ; sleep 30 ; t2 = Time.new
t1.hour == t2.hour
This should give you a boolean answer.

How do I calculate the offset, in hours, of a given timezone from UTC in ruby?

I need to calculate the offset, in hours, of a given timezone from UTC in Ruby. This line of code had been working for me, or so I thought:
offset_in_hours = (TZInfo::Timezone.get(self.timezone).current_period.offset.utc_offset).to_f / 3600.0
But, it turns out that was returning to me the Standard Offset, not the DST offset. So for example, assume
self.timezone = "America/New_York"
If I run the above line, offset_in_hours = -5, not -4 as it should, given that the date today is April 1, 2012.
Can anyone advise me how to calculate offset_in_hours from UTC given a valid string TimeZone in Ruby that accounts for both standard time and daylight savings?
Thanks!
Update
Here is some output from IRB. Note that New York is 4 hours behind UTC, not 5, because of daylight savings:
>> require 'tzinfo'
=> false
>> timezone = "America/New_York"
=> "America/New_York"
>> offset_in_hours = TZInfo::Timezone.get(timezone).current_period.utc_offset / (60*60)
=> -5
>>
This suggests that there is a bug in TZInfo or it is not dst-aware
Update 2
Per joelparkerhender's comments, the bug in the above code is that I was using utc_offset, not utc_total_offset.
Thus, per my original question, the correct line of code is:
offset_in_hours = (TZInfo::Timezone.get(self.timezone).current_period.offset.utc_total_offset).to_f / 3600.0
Yes, use TZInfo like this:
require 'tzinfo'
tz = TZInfo::Timezone.get('America/Los_Angeles')
To get the current period:
current = tz.current_period
To find out if daylight savings time is active:
current.dst?
#=> true
To get the base offset of the timezone from UTC in seconds:
current.utc_offset
#=> -28800 which is -8 hours; this does NOT include daylight savings
To get the daylight savings offset from standard time:
current.std_offset
#=> 3600 which is 1 hour; this is because right now we're in daylight savings
To get the total offset from UTC:
current.utc_total_offset
#=> -25200 which is -7 hours
The total offset from UTC is equal to utc_offset + std_offset.
This is the offset from the local time where daylight savings is in effect, in seconds.

How can I use Ruby to parse a time as though it is in a time zone I specify, with zone in the format America/Los_Angeles?

I want to be able to parse a Time from a string in Ruby (1.8.7), where the string does not contain any time zone information. I would like to treat the string as though it were in any of a number of time zones specified in this type of format: 'America/New_York'.
Time string example:
'2010-02-05 01:00:01'
I have spent quite a while trying to figure this one out.
I did find a similar question, but its answer does not apply in my case: How do I get Ruby to parse time as if it were in a different time zone?
The problem with the above solution is that my time zones cannot all be represented in the 3-letter format supported by Time.parse (http://www.ruby-doc.org/stdlib-1.8.7/libdoc/time/rdoc/classes/Time.html#M004931).
Is there a good way to accomplish what I'm trying to do here?
Edit: Made my answer actually appear as an answer.
require 'active_support/all'
time = ActiveSupport::TimeZone.new('UTC').parse('2010-02-05 01:00:01')
puts time
puts time.in_time_zone('EST')
Here's what I came up with using the tzinfo gem as suggested, though it seems rather complicated and unintuitive to me. As an end result I get the time parsed as though it were in the time zone I wanted, though represented by a Time object in UTC. I can also display it in the time zone I want using tzinfo's strftime:
jruby-1.6.1 :003 > time = '2010-05-01 01:00:00'
=> "2010-05-01 01:00:00"
jruby-1.6.1 :004 > tz = TZInfo::Timezone.get('America/New_York')
=> #<TZInfo::DataTimezone: America/New_York>
jruby-1.6.1 :005 > time += ' UTC'
=> "2010-05-01 01:00:00 UTC"
jruby-1.6.1 :006 > time = Time.parse(time)
=> Sat May 01 01:00:00 UTC 2010
jruby-1.6.1 :007 > time = tz.local_to_utc(time)
=> Sat May 01 05:00:00 UTC 2010
jruby-1.6.1 :010 > tz.strftime('%Y-%m-%d %H:%M:%S %Z', time)
=> "2010-05-01 01:00:00 EDT"
I believe this will suit my needs, but I wonder if I can get the Time to actually be in the timezone above (instead of just UTC).
You have two options the way I see it. On the one hand you could map the format you wish to use in an array (or any other structure you wish) to the 3-letter format used by Time.parse.
The other option is using the tzinfo gem as specified by my which seems to do the job quite nicely.
>> tz = TZInfo::Timezone.get("America/New_York")
=> #<TZInfo::DataTimezone: America/New_York>
>> tz.now
=> Thu Jul 07 16:29:13 UTC 2011
>> tz = TZInfo::Timezone.get("Europe/Rome")
=> #<TZInfo::DataTimezone: Europe/Rome>
>> tz.now
=> Thu Jul 07 22:30:03 UTC 2011

ROR + Ruby Date From XML API

By using XML API, I got date-time as "2008-02-05T12:50:00Z". Now I wanna convert this text format into different format like "2008-02-05 12:50:00". But I am getting proper way.
I have tried this one :: #a = "2008-02-05T12:50:00Z"
Steps
1. #a.to_date
=> Tue, 05 Feb 2008
2. #a.to_date.strftime('%Y')
=> "2008"
3. #a.to_date.strftime('%Y-%m-%d %H:%M:%S')
=> "2008-02-05 00:00:00
Suggest some thing ?
The to_date method converts your string to a date but dates don't have hours, minutes, or seconds. You want to use DateTime:
require 'date'
d = DateTime.parse('2008-02-05T12:50:00Z')
d.strftime('%Y-%m-%d %H:%M:%S')
# 2008-02-05 12:50:00
Use Ruby's DateTime:
DateTime.parse("2008-02-05T12:50:00Z") #=> #<DateTime: 2008-02-05T12:50:00+00:00 (353448293/144,0/1,2299161)>
From there you can output the value in any format you want using strftime. See Time#strftime for more info.

Resources