Sort UTC timestamp - ruby

I want to pull the latest UTC time from an array which contains various UTC time.
I could compare two time stamps in UTC as below:
#!/usr/bin/ruby
require "time"
a=Time.parse("2013-05-03 16:25:35 UTC")
b=Time.parse("2013-09-07 06:51:24 UTC")
if b < a
puts "latest time is #{a}"
else
puts "latest time is #{b}"
end
Output:
latest time is 2013-09-07 06:51:24 UTC
This way it is OK to compare only two time stamps. But my array contains more than 2 UTC time stamps and I need to chose the latest one. Here is the list of Array elements:
2013-04-30 12:13:20 UTC
2013-09-07 06:51:24 UTC
2013-05-03 16:25:35 UTC
2013-08-01 07:28:59 UTC
2013-04-09 13:42:36 UTC
2013-09-04 11:40:20 UTC
2013-07-01 06:47:52 UTC
2013-05-03 16:21:54 UTC
I want to chose the latest time from array which would be 2013-09-07 06:51:24 UTC
QUESTION:
How to compare all the array elements against each other on basis of UTC time ?
thanks.

Use Enumerable#max:
a = [ ... ] # Array of Time instances
latest = a.max
By default, max uses <=> to compare things and Time#<=> exists so this is probably the most straight forward way.
Your timestamps are (almost) in ISO 8601 format and those compare sensibly so you could leave them as Strings and apply max to an array of Strings as well.

Exact method is Array#sort or direct one Enumerable#max
require 'time'
time_ar = [ '2013-04-30 12:13:20 UTC',
'2013-09-07 06:51:24 UTC',
'2013-05-03 16:25:35 UTC',
'2013-08-01 07:28:59 UTC',
'2013-04-09 13:42:36 UTC',
'2013-09-04 11:40:20 UTC',
'2013-07-01 06:47:52 UTC',
'2013-05-03 16:21:54 UTC'
]
time_ar.map(&Time.method(:parse)).sort.last
# => 2013-09-07 06:51:24 UTC
time_ar.map(&Time.method(:parse)).max
# => 2013-09-07 06:51:24 UTC

Related

How to convert UTC to EST/EDT in Ruby?

How do I convert UTC timestamp in the format '2009-02-02 00:00:00' to EST/EDT in Ruby? Note that I am not using Rails, instead it is a simple Ruby script.
1If the date range falls between EST (usually Jan-Mid March) it needs to to UTC-5hrs. For EDT it is UTC-4hrs.
So far I have the following function to convert UTC to EST/EDT.
def utc_to_eastern(utc)
eastern = Time.parse(utc) # 2009-02-02 00:00:00 -0500
offset_num = eastern.to_s.split(" -")[1][1].to_i # 5
eastern_without_offset = (eastern-offset_num*60*60).strftime("%F %T") # 2009-02-01 19:00:00
return eastern_without_offset
end
puts utc_to_eastern("2009-02-02 00:00:00") # 2009-02-01 19:00:00
puts utc_to_eastern("2009-04-02 00:00:00") # 2009-04-01 20:00:00
The above code does what I want, however there's two issues with my solution:
I do not want to reinvent the wheel, meaning I do not wish to write the time conversion functionality instead use existing methods provided by Ruby. Is there a more intuitive way to do this?
The parsing uses my local timezone to convert UTC to EST/EDT, however I would like to explicitly define the timezone conversion ("America/New_York"). Because this means someone running this on a machine on central time would not be using EST/EDT.
The best approach would be to use TZInfo.
require 'tzinfo'
require 'time'
def utc_to_eastern utc
tz = TZInfo::Timezone.get("America/New_York")
tz.to_local(Time.parse(utc)).strftime('%Y-%m-%d %H:%M:%S')
end
utc_to_eastern "2020-02-02 00:00:00 UTC" => "2020-02-01 19:00:00"
utc_to_eastern "2020-04-02 00:00:00 UTC" => "2020-04-01 20:00:00"

how to get previous day in UTC format using ruby

How to get previous day on UTC time using ruby?
Currently I'm using Time.now.utc.iso8601 to get UTC time format in ruby, I need previous day in same UTC format. Can someone help me with sample code to get previous day?
> Time.now.utc
=> 2015-03-22 19:00:46 UTC
> Time.now.utc - 86400
=> 2015-03-21 19:00:51 UTC
> (Time.now.utc - 86400).iso8601
=> "2015-03-21T19:00:59Z"

Lua UTC time inconsistencies

Lua 5.1 doc says:
If format starts with '!', then the date is formatted in Coordinated
Universal Time.
If format is %c, !'s behavior seems correct
local date_1 = os.date("!%c")
local date_2 = os.date("%c")
print("utc date: "..date_1)
print("not utc date: "..date_2)
If format is *t, !'s behavior seems swapped
local time_1 = os.time(os.date("!*t"))
local time_2 = os.time(os.date("*t"))
print("should be utc time, but is not: "..time_1) -- this should be UTC, and is not
print("should not be utc time, but is: "..time_2) -- this should not be UTC, but is
Dates are tested with: http://www.epochconverter.com/
Why is that?
The table returned by os.date("!*t") and os.date("*t") is correct. I'm printing only the hour field. Note that they are consistent with %c format:
local date_1 = os.date("!%c")
local date_2 = os.date("%c")
print("utc date: "..date_1)
print("not utc date: "..date_2)
print("utc date hour: " .. os.date("!*t").hour)
print("not utc date hour: " .. os.date("*t").hour)
Output on my machine (China Standard Time, UTC+08:00):
utc date: 02/06/15 02:02:29
not utc date: 02/06/15 10:02:29
utc date hour: 2
not utc date hour: 10
However, os.time takes the table, assuming it's the local time, and returns the epoch. So, the local time is converted to the real epoch, but the utc time is not.
print(os.time{year=1970, month=1, day=1, hour=8})
outputs 0 on my machine.

How do you convert UTC time to EST in ruby (not using Rails)?

I am capturing the current time like so:
Time.now
My server runs on UTC. How can I convert the time to EST without using any Rails libraries? I am guessing some sort of offset but not sure how it works per say.
In plain Ruby you may use Time.zone_offset method:
require 'time'
t = Time.now # 2014-07-30 18:30:00 UTC
t + Time.zone_offset('EST') # 2014-07-30 13:30:00 UTC
The fbonetti's answer leads to the proper UTC to Eastern time conversion while accepted David Unric's answer would give wrong time for 8 months in 2017 (while DST is in effect).
Let's look at the following example:
First we'll need to figure out when DST starts/ends in 2017:
As we can see on March 12th, 2017 deep in the night (2:00am) they change time by adding +1 hour, so they "jump" from 1:59:59am up to 3:00:00am instantaneously! Which means there can not be 2:30am on March 12th, 2017.
Let's choose two UTC timestamps - one before and one after that switch, then we will try to convert those two timestamps from UTC back to Eastern.
First timestamp will be safely far enough from the switch moment:
require 'time'
t1 = Time.parse("2017-03-11 15:00:00 +0000")
=> 2017-03-11 15:00:00 +0000
t1_epoch_s = t1.to_i
=> 1489244400
Second timestamp is just +24 hours from the first one:
t2 = Time.parse("2017-03-12 15:00:00 +0000")
=> 2017-03-12 15:00:00 +0000
t2_epoch_s = t2.to_i
=> 1489330800
Now let us convert t1_epoch_s and t2_epoch_s to Eastern:
method-1: by adding Time.zone_offset('EST')
wrong, gives bad result: 10am for both days :(
and offset portion is shown as "+0000" which is also misleading and would refer to completely wrong point in time for people reading our output : ((
Time.at(t1_epoch_s) + Time.zone_offset('EST')
=> 2017-03-11 10:00:00 +0000
Time.at(t2_epoch_s) + Time.zone_offset('EST')
=> 2017-03-12 10:00:00 +0000
method-2: by changing timezone
Good!! Correctly yields 10am and 11am on next day!-)
ENV['TZ'] = 'America/New_York'
Time.at(t1_epoch_s)
=> 2017-03-11 10:00:00 -0500
Time.at(t2_epoch_s)
=> 2017-03-12 11:00:00 -0400
# resetting timezone back
ENV['TZ'] = nil
Basically manually adding Time.zone_offset('EST') is like adding constant and it will give right result for about 4 months (of 12 total) during the year, but then other time you'd have to manually add Time.zone_offset('EDT'), which is another constant. It pretty much same as "a broken clock is right twice a day": )) nasty!
And just for laughter let's see the "slow mo" how proper method handles the actual +1 hour magic jump in time:
ENV['TZ'] = "America/New_York"
Time.at(1489301999 + 0)
=> 2017-03-12 01:59:59 -0500
Time.at(1489301999 + 1)
=> 2017-03-12 03:00:00 -0400
ENV['TZ'] = nil
magic-magic!
In plain ruby, the timezone is determined by the 'TZ' environment variable. You could do something like this:
ENV['TZ'] = 'America/New_York' # set the TZ to Eastern Daylight Time
time = Time.now
time.zone
# => "EDT"
# do stuff
ENV['TZ'] = nil # reset the TZ back to UTC
If you don't mind using a gem,
require 'tzinfo'
tz = TZInfo::Timezone.get('US/Eastern')
Time.now.getlocal(tz.current_period.offset.utc_total_offset)
Credit: https://stackoverflow.com/a/42702906/2441263

Parse time with strptime using Time.zone

I am trying to parse a datetime with Time class in Ruby 2.0. I can't figure out how to parse date and get it in a specified timezone. I have used Time.zone.parse to parse a date where I first call Time.zone and set it to a specified timezone. In the below example, I set the zone but it does not effect strptime, I have tried doing Time.zone.parse(date) but I can't get it parse a date like the one below.
Time.zone = "Central Time (US & Canada)"
#=> "Central Time (US & Canada)"
irb(main):086:0> Time.strptime("08/26/2013 03:30 PM","%m/%d/%Y %I:%M %p")
#=> 2013-08-26 15:30:00 -0400
Time.zone isn’t a part of Ruby, it’s a part of ActiveSupport (which is included with Rails). As such, strptime does not know about Time.zone at all. You can, however, convert a normal Ruby Time into an ActiveSupport::TimeWithZone using in_time_zone, which uses Time.zone’s value by default:
require 'active_support/core_ext/time'
Time.zone = 'Central Time (US & Canada)'
time = Time.strptime('08/26/2013 03:30 PM', '%m/%d/%Y %I:%M %p')
#=> 2013-08-26 15:30:00 -0400
time.in_time_zone
#=> Mon, 26 Aug 2013 14:30:00 CDT -05:00
If you are only looking at Ruby2.0, you may find the time lib useful:
require 'time'
time.zone # return your current time zone
a = Time.strptime("08/26/2013 03:30 PM","%m/%d/%Y %I:%M %p")
# => 2013-08-26 15:30:00 +1000
a.utc # Convert to UTC
a.local # Convert back to local
# Or you can add/subtract the offset for the specific time zone you want:
a - 10*3600 which gives UTC time too
strptime gets its parameters from the time string. As such, the time string must contain time zone information.
If you are parsing time strings in a specific time zone, but the time strings that you receive do not have it embedded - then you can add time zone information before passing the time string to srtptime, and asking strptime to parse the time zone offset using %z or name using %Z.
In a nutshell, if you have a time string 08/26/2013 03:30 PM and you want it parsed in the UTC time zone, you would have:
str = '08/26/2013 03:30 PM'
Time.strptime("#{str} UTC}", "%m/%d/%Y %I:%M %p %Z")

Resources