Converting Unix epoch timestamps to Ruby objects

A handy trick to parse timestamps to Time objects using plain Ruby way

While working on API integrations with third party services, I get timestamp of the event in the request payload.

{ 
"timestamp": "1583122008",
...
}

Unix Time or Epoch Time

This timestamp is the number of seconds that have elapsed since the Unix epoch, that is the time 00:00:00 UTC on 1 January 1970, minus leap seconds. In Ruby, if we want to get unix time for current time, it is very easy.

>> Time.now.to_i
=> 1583122191

But how to take his epoch time and convert it back to a Ruby Time object?

Thanks to jrochkind for helping me improve the wording of this paragraph.

Time::at to the rescue

Ruby has a handy method Time::at which can covert Unix epoch time back to a Time object.

>> ts = Time.now.to_i
=> 1583122280
>> Time.at(ts)
=> 2020-03-02 09:41:20 +0530

It prints the time in local time zone, which is IST in my case. But Time::at also supports timezone argument as follows.

>> ts = Time.now.to_i
>> Time.at(ts, in: "+01:00")
=> 2020-03-02 05:13:24 +0100
>> Time.at(ts, in: "-02:00")
=> 2020-03-02 02:13:24 -0200

The in keyword arguments accepts timezone offset in the form of +HH:MM or -HH:MM.

The in argument was supported from Ruby 2.6 onwards. Before that Time.at only accepted one argument.
Time#at is provided by Ruby and not by Rails or Active Support.

Ruby's lack of documentation..

While looking at the solution to this parsing problem, I checked documentation of Time::at method on ruby-doc.org. I came across this:

If in argument is given, the result is in that timezone or UTC offset, or if a numeric argument is given, the result is in local time.

This led me down to Ruby's source code to understand what kind of arguments can be passed as in to Time::at. After checking the code, the in argument accepts following:

  • Timezone offset in the form of +HH:MM or -HH:MM.
>> ts = Time.now.to_i
>> Time.at(ts, in: "+01:00")
=> 2020-03-02 05:13:24 +0100
>> Time.at(ts, in: "-02:00")
=> 2020-03-02 02:13:24 -0200
  • UTC string
>> Time.at(ts, in: "UTC")
=> 2020-03-02 04:13:24 UTC
>> Time.at(ts, in: "E")
=> 2020-03-02 09:13:24 +0500
>> Time.at(ts, in: "Z")
=> 2020-03-02 04:13:24 UTC

If we pass anything else, we get an error message.

>> Time.at(ts, in: "IST")
Traceback (most recent call last):
        2: from (irb):12
        1: from (irb):12:in `at'
ArgumentError ("+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset)
This error message is from Ruby 2.7. In Ruby 2.6, the error message did not mention about military timezones. It only talked about UTC offset.

I opened a pull request to improve the documentation ย of Time::at and it got merged quickly. The documentation of Time::at will have all possible combinations for the in argument in the next release of Ruby ๐Ÿ‘๐Ÿป


If you want to perform this same trick in PostgreSQL, it can be done as described in this article - https://dev.to/prathamesh/converting-unix-epoch-time-to-timestamps-in-postgresql-1hah