How to convert UTC to EST/EDT in Ruby? - 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"

Related

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

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

How do I get Ruby to parse time as if it were in a different time zone?

I'm parsing something like this:
11/23/10 23:29:57
which has no time zone associated with it, but I know it's in the UTC time zone (while I'm not). How can I get Ruby to parse this as if it were in the UTC timezone?
You could just append the UTC timezone name to the string before parsing it:
require 'time'
s = "11/23/10 23:29:57"
Time.parse(s) # => Tue Nov 23 23:29:57 -0800 2010
s += " UTC"
Time.parse(s) # => Tue Nov 23 23:29:57 UTC 2010
credit from https://rubyinrails.com/2018/05/30/rails-parse-date-time-string-in-utc-zone/,
Time.find_zone("UTC").parse(datetime)
# => Wed, 30 May 2018 18:00:00 UTC +05:30
If your using rails you can use the ActiveSupport::TimeZone helpers
current_timezone = Time.zone
Time.zone = "UTC"
Time.zone.parse("Tue Nov 23 23:29:57 2010") # => Tue, 23 Nov 2010 23:29:57 UTC +00:00
Time.zone = current_timezone
It is designed to have the timezone set at the beginning of the request based on user timezone.
Everything does need to have Time.zone on it, so Time.parse would still parse as the servers timezone.
http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html
Note: the time format you have above was no longer working, so I changed to a format that is supported.
If you are using ActiveSupport [from Rails, e.g], you can do this:
ActiveSupport::TimeZone["GMT"].parse("..... date string")
Another pure Ruby (no Rails) solution if you don't want/need to load ActiveSupport.
require "time"
ENV['TZ'] = 'UTC'
Time.parse("2019/10/01 23:29:57")
#=> 2019-10-01 23:29:57 +0000
An aliter to #Pete Brumm's answer without Time.zone set/unset
Time.zone.parse("Tue Nov 23 23:29:57 2010") + Time.zone.utc_offset
Without rails dependencies: Time parses in local time but DateTime parses in UTC. Then you can transform it to a Time class if that's what you want:
require 'date'
DateTime.parse(string_to_parse).to_time
Rails adds utc method on datetime objects to return the utc time:
Time.parse('10:10').utc

convert String to DateTime

I need to parse following String into a DateTime Object:
30/Nov/2009:16:29:30 +0100
Is there an easy way to do this?
PS: I want to convert the string above as is. The colon after the year is not a typo. I also want to solve the problem with Ruby and not RoR.
Shouldn't this also work for Rails?
"30/Nov/2009 16:29:30 +0100".to_datetime
DateTime.strptime allows you to specify the format and convert a String to a DateTime.
I have had success with:
require 'time'
t = Time.parse(some_string)
This will convert the string in date to datetime, if using Rails:
"05/05/2012".to_time
Doc Reference: https://apidock.com/rails/String/to_time
I used Time.parse("02/07/1988"), like some of the other posters.
An interesting gotcha was that Time was loaded by default when I opened up IRB, but Time.parse was not defined. I had to require 'time' to get it to work.
That's with Ruby 2.2.
convert string to date:
# without timezone
DateTime.strptime('2012-12-09 00:01:36', '%Y-%m-%d %H:%M:%S')
=> Sun, 09 Dec 2012 00:01:36 +0000
# with specified timezone
DateTime.strptime('2012-12-09 00:01:36 +8', '%Y-%m-%d %H:%M:%S %z')
=> Sun, 09 Dec 2012 00:01:36 +0800
refer to:
https://ruby-doc.org/stdlib-3.1.1/libdoc/date/rdoc/Date.html
in Ruby 1.8, the ParseDate module will convert this and many other date/time formats. However, it does not deal gracefully with the colon between the year and the hour. Assuming that colon is a typo and is actually a space, then:
#!/usr/bin/ruby1.8
require 'parsedate'
s = "30/Nov/2009 16:29:30 +0100"
p Time.mktime(*ParseDate.parsedate(s)) # => Mon Nov 30 16:29:30 -0700 2009
You can parse a date time string with a given timezone as well:
zone = "Pacific Time (US & Canada)"
ActiveSupport::TimeZone[zone].parse("2020-05-24 18:45:00")
=> Sun, 24 May 2020 18:45:00 PDT -07:00

Resources