Ruby: Datetime to UTC conversion - ruby

I am trying to convert the below date and time combination to UTC
from_date: "2017-06-19",from_time: "14:00"
to_date: "2017-06-19", to_time: "23:00"
Timezone: EDT
I am using below piece of code for conversion
Date.parse(dt).to_datetime + Time.parse(t).utc.seconds_since_midnight.seconds
And it gives the wrong date value for the to_date & to_time combination.
Output:
Date.parse(from_date).to_datetime +
Time.parse(from_time).utc.seconds_since_midnight.seconds
#⇒ **Mon, 19 Jun 2017 18:00:00 +0000**
Date.parse(to_date).to_datetime +
Time.parse(to_time).utc.seconds_since_midnight.seconds
#⇒ **Mon, 19 Jun 2017 03:00:00 +0000**
Above conversion should give "Tue, 20 Jun 2017 03:00:00 +0000" instead.

Below line of codes worked for me:
parsed_date = Time.zone.parse(from_date).strftime('%Y-%m-%d')
parsed_time = Time.zone.parse(from_time).strftime('%T')
Time.parse(parsed_date + ' ' + parsed_time).utc.strftime('%F %T')

require 'time'
from = Time.parse "2017-06-19 14:00 US/Eastern"
=> 2017-06-19 14:00:00 -0400
from.utc
=> 2017-06-19 18:00:00 UTC
to = Time.parse "2017-06-19 23:00 US/Eastern"
=> 2017-06-19 23:00:00 -0400
to.utc
=> 2017-06-20 03:00:00 UTC
Though you can also specify the timezone offset without using the string, doing it this way handles Daylight Savings Time.

I think this is shorter:
from_date = "2017-06-19"
from_time = "14:00"
DateTime.strptime("#{from_date}T#{from_time}ZEDT", "%Y-%m-%dT%H:%MZ%z").utc
=> Mon, 19 Jun 2017 18:00:00 +000
to_date = "2017-06-19"
to_time = "23:00"
DateTime.strptime("#{to_date}T#{to_time}ZEDT", "%Y-%m-%dT%H:%MZ%z").utc
=> Tue, 20 Jun 2017 03:00:00 +0000

Related

Get min and max value from this array of hashes

I have an array that contains a hash in each row containing created_at and a value. How do I get the min and max from the array for the value fields?
The array is called - channels_counts_for_history_graph
and
channels_counts_for_history_graph.max[1]
Gives me the max date rather than the max value?
[[Sun, 30 Dec 2018 15:03:55 UTC +00:00, 4305],
[Sun, 30 Dec 2018 15:05:42 UTC +00:00, 4305],
[Mon, 31 Dec 2018 09:24:06 UTC +00:00, 4306],
[Sat, 05 Jan 2019 09:04:50 UTC +00:00, 4308],
[Tue, 01 Jan 2019 11:26:04 UTC +00:00, 4306],
[Wed, 02 Jan 2019 17:24:19 UTC +00:00, 4305]]
Any help appreciated.
Thanks
I suggest using Enumerable#minmax_by to get the min and the max value in just one method call:
array = [['Sun, 30 Dec 2018 15:03:55 UTC +00:00', 4305],['Sun, 30 Dec 2018 15:05:42 UTC +00:00', 4305],['Mon, 31 Dec 2018 09:24:06 UTC +00:00', 4306],['Sat, 05 Jan 2019 09:04:50 UTC +00:00', 4308],['Tue, 01 Jan 2019 11:26:04 UTC +00:00', 4306],['Wed, 02 Jan 2019 17:24:19 UTC +00:00', 4305]]
array.minmax_by(&:last)
#=> [["Sun, 30 Dec 2018 15:03:55 UTC +00:00", 4305], ["Sat, 05 Jan 2019 09:04:50 UTC +00:00", 4308]]
By default when you sort an array sorts by the first element first.
You can reverse the array for the purposes of the sort.
channel_counts_for_history_graph.map(&:reverse).max[0]
I may guess that this is what you were asking for:
[{ created_at: Date.new(2017, 1, 1) }, { created_at: Date.new(2019, 1, 1) }, { created_at: Date.new(2018, 1, 1) }]
.minmax_by { |value| value[:created_at] }

DateTime set TimeZone

ubuntu 14.04
ruby 1.9.3-p484
rails 3.2.18
I have a date as a string: 06/20/2015 02:45 AM
d = DateTime.strptime('06/20/2015 02:45 AM', '%m/%d/%Y %I:%M %p').to_time
=> Sat, 20 Jun 2015 02:45:00 UTC
Current TimeZone may be different and placed in Time.zone.
I tried d.to_time.in_time_zone. It gives respectively for PDT and CDT TimeZone:
Fri, 19 Jun 2015 21:45:00 CDT -05:00
Fri, 19 Jun 2015 19:45:00 PDT -07:00
I need to get DateTime object that holds date Sat, 20 Jun 2015 02:45:00 PDT -07:00 for PDT zone or Sat, 20 Jun 2015 02:45:00 CDT -05:00 for CDT zone.
I think it would work:
zone = ActiveSupport::TimeZone.new("Central Time (US & Canada)")
d.to_time.in_time_zone.in_time_zone(zone)
or just
d.to_time.in_time_zone.in_time_zone("Central Time (US & Canada)")
Try this:
#config/application.rb
config.time_zone = 'Central Time (US & Canada)'
config.active_record.default_timezone = :local
Don't forget to restart your server.
I have found the solution. Method DateTime#offset rules:
d = DateTime.strptime('06/20/2015 02:45 AM', '%m/%d/%Y %I:%M %p')
d = d.change(offset: (Time.zone.now.utc_offset / 3600).to_s)

Ruby Convert Date

I have a page that is returning this as a date new Date(1357106400000) (which I believe is Javascript).
How do I convert that using Ruby. I've tried:
Date.new(1357106400000)
DateTime.new(1357106400000)
and many others, but I can't get the correct date to display.
The date returned should be 12/09/2012
I believe you've got the time in milliseconds since the epoch, and the function you're looking for is Time#at. But you need to down-convert into seconds before calling. For example:
[holt#Michaela ~]$ irb
irb(main):001:0> Time.at(1357106400000)
=> Wed Dec 28 00:00:00 +0000 44974
irb(main):002:0> Time.at(1357106400000 / 1000)
=> Wed Jan 02 06:00:00 +0000 2013
irb(main):003:0>
Not the exact day you thought it was, but probably still correct. Hope that helps!
In Javascript:
> new Date(1357106400000)
Wed Jan 02 2013 06:00:00 GMT+0000 (GMT Standard Time)
In Ruby:
> require 'date'
> DateTime.strptime("1357106400000", "%Q")
=> #<DateTime: 2013-01-02T06:00:00+00:00 ((2456295j,21600s,0n),+0s,2299161j)>

Ruby: combine Date and Time objects into a DateTime

Simple question, but I can't find a good or definitive answer. What is the best and most efficient way to combine Ruby Date and Time objects (objects, not strings) into a single DateTime object?
I found this, but it's not as elegant you would hope:
d = Date.new(2012, 8, 29)
t = Time.now
dt = DateTime.new(d.year, d.month, d.day, t.hour, t.min, t.sec, t.zone)
By the way, the ruby Time object also stores a year, month, and day, so you would be throwing that away when you create the DateTime.
When using seconds_since_midnight, changes in daylight savings time can lead to unexpected results.
Time.zone = 'America/Chicago'
t = Time.zone.parse('07:00').seconds_since_midnight.seconds
d1 = Time.zone.parse('2016-11-06').to_date # Fall back
d2 = Time.zone.parse('2016-11-07').to_date # Normal day
d3 = Time.zone.parse('2017-03-12').to_date # Spring forward
d1 + t
#=> Sun, 06 Nov 2016 06:00:00 CST -06:00
d2 + t
#=> Mon, 07 Nov 2016 07:00:00 CST -06:00
d3 + t
#=> Sun, 12 Mar 2017 08:00:00 CDT -05:00
Here's an alternative, similar to #selva-raj's answer above, using string interpolation, strftime, and parse. %F is equal to %Y-%m-%d and %T is equal to %H:%M:%S.
Time.zone = 'America/Chicago'
t = Time.zone.parse('07:00')
d1 = Time.zone.parse('2016-11-06').to_date # Fall back
d2 = Time.zone.parse('2016-11-07').to_date # Normal day
d3 = Time.zone.parse('2017-03-12').to_date # Spring forward
Time.zone.parse("#{d1.strftime('%F')} #{t.strftime('%T')}")
#=> Sun, 06 Nov 2016 07:00:00 CST -06:00
Time.zone.parse("#{d2.strftime('%F')} #{t.strftime('%T')}")
#=> Sun, 07 Nov 2016 07:00:00 CST -06:00
Time.zone.parse("#{d3.strftime('%F')} #{t.strftime('%T')}")
#=> Sun, 12 Mar 2017 07:00:00 CDT -05:00
Simple:
Date.new(2015, 2, 10).to_datetime + Time.parse("16:30").seconds_since_midnight.seconds
# => Object: Tue, 10 Feb 2015 16:30:00 +0000
You gotta love Ruby!
If using Rails, try any of these:
d = Date.new(2014, 3, 1)
t = Time.parse("16:30")
dt = d + t.seconds_since_midnight.seconds
# => ActiveSupport::TimeWithZone
dt = (d + t.seconds_since_midnight.seconds).to_datetime
# => DateTime
dt = DateTime.new(d.year, d.month, d.day, t.hour, t.min, t.sec)
# => DateTime
If you are using Ruby on Rails, this works great.
I built a method to extend the DateTime class to combine a date and a time. It takes the zone from the date so that it does not end up an hour off with daylight savings time.
Also, for convenience, I like being able to pass in strings as well.
class DateTime
def self.combine(d, t)
# pass in a date and time or strings
d = Date.parse(d) if d.is_a? String
t = Time.zone.parse(t) if t.is_a? String
# + 12 hours to make sure we are in the right zone
# (eg. PST and PDT switch at 2am)
zone = (Time.zone.parse(d.strftime("%Y-%m-%d")) + 12.hours ).zone
new(d.year, d.month, d.day, t.hour, t.min, t.sec, zone)
end
end
So you can do:
DateTime.combine(3.weeks.ago, "9am")
or
DateTime.combine("2015-3-26", Time.current)
etc...
I found another way, I hope this is correct.
datetojoin=Time.parse(datetime).strftime("%Y-%m-%d")
timetojoin=Time.parse(time).strftime("%T")
joined_datetime = Time.parse(datetojoin +" "+ timetojoin).strftime("%F %T")
Any thoughts? Please share.

How to get Rails to interpret a time as being in a specific time zone?

In Ruby 1.8.7, how to set the time zone of a time?
In the following examples, my system time zone is PST (-8:00 hours from UTC)
Given a time (21 Feb 2011, 20:45), presume that the time is in EST:
#this interprets the time as system time zone, i.e. PST
Time.local(2011,02,21,20,45)
#=> Mon Feb 21 20:45:00 -0800 2011
#this **converts** the time into EST, which is wrong!
Time.local(2011,02,21,20,45).in_time_zone "Eastern Time (US & Canada)"
#=> Mon, 21 Feb 2011 23:45:00 EST -05:00
But, the output I want is:
Mon Feb 21 20:45:00 -0500 2011 (Note the -0500 (EST) as opposed to -0800 (PST) and the hour is same, i.e. 20, not 23)
UPDATE (see the better version of this below)
I managed to get this to work, but I don't like it:
DateTime.new(2011,02,21,20,45).change :offset => -(300.0 / 1440.0)
# => Mon, 21 Feb 2011 20:45:00 +0500
Where
300 = 5 hrs x 60 minutes
1440 = number of minutes in a day
or the "right" way:
DateTime.civil(2011,02,21,20,45,0,Rational(-5, 24))
Question: Now, is there a way to determine the accurate(i.e. catering for daylight saving time etc) UTC offset from Time.zone so that I can pass it to the change method?
Reference: DateTime::change method
UPDATE (better version)
Thanks to #ctcherry for all the help!
Determine the accurate time zone info from Time.zone:
DateTime.civil(2011,02,21,20,45,0,Rational((Time.zone.tzinfo.current_period.utc_offset / 3600), 24))
In ruby 1.8.7 it doesn't appear to be very easy to do what are asking for according to the documentation:
http://www.ruby-doc.org/core-1.8.7/classes/Time.html
However in 1.9 it looks a lot easier by passing the timezone offset to the localtime() method on a Time object:
http://www.ruby-doc.org/core/classes/Time.html#M000346
UPDATE
The offset for Time.zone is easy since its an object on its own: (This is in a Rails console)
ruby-1.8.7-p248 :001 > Time.zone
=> #<ActiveSupport::TimeZone:0x103150190 #current_period=nil, #name="Central Time (US & Canada)", #tzinfo=#<TZInfo::TimezoneProxy: America/Chicago>, #utc_offset=nil>
ruby-1.8.7-p248 :002 > Time.zone.utc_offset
=> -21600
ruby-1.8.7-p248 :003 > Time.zone.formatted_offset
=> "-06:00"
So I think this will (almost) accomplish what you want:
require 'time'
t = "21 Feb 2011, 20:45"
Time.parse(t) # => Mon Feb 21 20:45:00 -0700 2011
t += " -05:00" # this is the trick
Time.parse(t) # => Mon Feb 21 18:45:00 -0700 2011
It still returns the time based on your system time zone, but the actual time is the correct time that you are seeking.
By the way, this is tested on 1.8.7-p334.

Resources