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
- Military timezones such as "A", "B", "C".
>> 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