7

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.

Jackson
  • 6,391
  • 6
  • 32
  • 43

4 Answers4

5

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
David Unric
  • 7,421
  • 1
  • 37
  • 65
  • 4
    Jesus. Another example of the horrible ruby docs--the Time class makes no mention of the fact that there is a Ruby Standard Library package called `time` that adds useful methods to the Time class. I would add that the op should not rely on the server's offset being 0, so use `t = Time.now.utc`. – 7stud Jul 30 '14 at 19:17
  • @7stud Yep. On the other side `ri` may help in such cases. Explicit conversion to UTC is a good idea. – David Unric Jul 30 '14 at 19:30
  • @7stud Just try yourself - `ri Time` would show docs for all Time class/modules/extensions. – David Unric Jul 30 '14 at 21:11
  • I get, `Nothing known about Time`. – 7stud Jul 31 '14 at 17:09
  • Ah, I didn't have ri installed on the ruby version I was using: `rvm docs generate-ri`. – 7stud Aug 02 '14 at 17:53
  • This answer is not correct. It will give right answer only during 4 months (when daylight saving is not on). See my answer for the detailed explanation and the right way to do conversion. – Dmitry Shevkoplyas Oct 31 '18 at 18:55
4

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:

enter image description here

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!

Dmitry Shevkoplyas
  • 6,163
  • 3
  • 27
  • 28
3

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
fbonetti
  • 6,652
  • 3
  • 34
  • 32
1

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

lucas
  • 1,050
  • 12
  • 21