How do you convert a Unix timestamp (seconds since epoch) to Ruby DateTime?
6 Answers
Sorry, brief moment of synapse failure. Here's the real answer.
require 'date'
Time.at(seconds_since_epoch_integer).to_datetime
Brief example (this takes into account the current system timezone):
$ date +%s
1318996912
$ irb
ruby-1.9.2-p180 :001 > require 'date'
=> true
ruby-1.9.2-p180 :002 > Time.at(1318996912).to_datetime
=> #<DateTime: 2011-10-18T23:01:52-05:00 (13261609807/5400,-5/24,2299161)>
Further update (for UTC):
ruby-1.9.2-p180 :003 > Time.at(1318996912).utc.to_datetime
=> #<DateTime: 2011-10-19T04:01:52+00:00 (13261609807/5400,0/1,2299161)>
Recent Update: I benchmarked the top solutions in this thread while working on a HA service a week or two ago, and was surprised to find that Time.at(..)
outperforms DateTime.strptime(..)
(update: added more benchmarks).
# ~ % ruby -v
# => ruby 2.1.5p273 (2014-11-13 revision 48405) [x86_64-darwin13.0]
irb(main):038:0> Benchmark.measure do
irb(main):039:1* ["1318996912", "1318496912"].each do |s|
irb(main):040:2* DateTime.strptime(s, '%s')
irb(main):041:2> end
irb(main):042:1> end
=> #<Benchmark ... @real=2.9e-05 ... @total=0.0>
irb(main):044:0> Benchmark.measure do
irb(main):045:1> [1318996912, 1318496912].each do |i|
irb(main):046:2> DateTime.strptime(i.to_s, '%s')
irb(main):047:2> end
irb(main):048:1> end
=> #<Benchmark ... @real=2.0e-05 ... @total=0.0>
irb(main):050:0* Benchmark.measure do
irb(main):051:1* ["1318996912", "1318496912"].each do |s|
irb(main):052:2* Time.at(s.to_i).to_datetime
irb(main):053:2> end
irb(main):054:1> end
=> #<Benchmark ... @real=1.5e-05 ... @total=0.0>
irb(main):056:0* Benchmark.measure do
irb(main):057:1* [1318996912, 1318496912].each do |i|
irb(main):058:2* Time.at(i).to_datetime
irb(main):059:2> end
irb(main):060:1> end
=> #<Benchmark ... @real=2.0e-05 ... @total=0.0>

- 24,647
- 4
- 70
- 96

- 14,005
- 5
- 37
- 49
-
1Thank you... The following answer is a little more succinct, I found Time.at but was trying to find a DateTime equivalent. – Tronathan Oct 19 '11 at 18:57
-
29It's funny but Time.at().to_datetime seems more pleasant than DateTime.strptime() simply because of readability...At least to me anyway – tybro0103 Mar 14 '12 at 17:35
-
36This is not the same as the above anser, Time.at assumes current timezone, where DateTime.strptime uses UTC. – Vitaly Babiy Jan 15 '13 at 15:52
-
5It's not too surprising that `Time.at` outperforms `DateTime.strptime`. The latter has to parse a string, which is generally much slower than taking in a number directly. – Claw Jan 09 '15 at 19:03
-
2Your benchmark isn't exactly testing `DateTime.strptime` because it's creating two new Strings every iteration which is very expensive. It's not just the string parsing like @claw said – WattsInABox Jan 12 '15 at 16:02
-
1@Claw et al - Thanks, noted. See updated benchmarks. – Adam Eberlin Jan 21 '15 at 21:10
-
1@WattsInABox, I missed your comment somehow, but the mere fact that strptime requires a string makes it inherently more expensive. Cheers. – Adam Eberlin Mar 28 '16 at 14:00
-
@AdamEberlin I know I'm 3 years late in replying (lol) but my point was if you just create that '%s' outside of that loop, then you'll likely drastically speed up the DateTime.strptime call. You'll create 1 string instead of thousands... And also turn those ints into strings in the array itself... – WattsInABox Aug 30 '19 at 12:58
DateTime.strptime
can handle seconds since epoch. The number must be converted to a string:
require 'date'
DateTime.strptime("1318996912",'%s')

- 158,662
- 42
- 215
- 303

- 79,051
- 16
- 138
- 171
-
4
-
59
-
3To follow up on @TheMiniJohn's answer. It looks like `Time` is necessary instead of `DateTime`. So use `Time.strptime("1318996912345",'%Q').to_f` and you will see the milliseconds preserved, while `DateTime.strptime("1318996912345",'%Q').to_f` does not preserve it. – skensell Feb 22 '17 at 15:20
-
`Time.at 1318996912` is preferable in some cases because it infers the time for your local timezone, as WattsInABox pointed out. – JellicleCat Sep 10 '20 at 21:22
Time Zone Handling
I just want to clarify, even though this has been commented so future people don't miss this very important distinction.
DateTime.strptime("1318996912",'%s') # => Wed, 19 Oct 2011 04:01:52 +0000
displays a return value in UTC and requires the seconds to be a String and outputs a UTC Time object, whereas
Time.at(1318996912) # => 2011-10-19 00:01:52 -0400
displays a return value in the LOCAL time zone, normally requires a FixNum argument, but the Time object itself is still in UTC even though the display is not.
So even though I passed the same integer to both methods, I seemingly two different results because of how the class' #to_s
method works. However, as @Eero had to remind me twice of:
Time.at(1318996912) == DateTime.strptime("1318996912",'%s') # => true
An equality comparison between the two return values still returns true. Again, this is because the values are basically the same (although different classes, the #==
method takes care of this for you), but the #to_s
method prints drastically different strings. Although, if we look at the strings, we can see they are indeed the same time, just printed in different time zones.
Method Argument Clarification
The docs also say "If a numeric argument is given, the result is in local time." which makes sense, but was a little confusing to me because they don't give any examples of non-integer arguments in the docs. So, for some non-integer argument examples:
Time.at("1318996912")
TypeError: can't convert String into an exact number
you can't use a String argument, but you can use a Time argument into Time.at
and it will return the result in the time zone of the argument:
Time.at(Time.new(2007,11,1,15,25,0, "+09:00"))
=> 2007-11-01 15:25:00 +0900
Benchmarks
After a discussion with @AdamEberlin on his answer, I decided to publish slightly changed benchmarks to make everything as equal as possible. Also, I never want to have to build these again so this is as good a place as any to save them.
Time.at(int).to_datetime ~ 2.8x faster
09:10:58-watsw018:~$ ruby -v
ruby 2.3.7p456 (2018-03-28 revision 63024) [universal.x86_64-darwin18]
09:11:00-watsw018:~$ irb
irb(main):001:0> require 'benchmark'
=> true
irb(main):002:0> require 'date'
=> true
irb(main):003:0>
irb(main):004:0* format = '%s'
=> "%s"
irb(main):005:0> times = ['1318996912', '1318496913']
=> ["1318996912", "1318496913"]
irb(main):006:0> int_times = times.map(&:to_i)
=> [1318996912, 1318496913]
irb(main):007:0>
irb(main):008:0* datetime_from_strptime = DateTime.strptime(times.first, format)
=> #<DateTime: 2011-10-19T04:01:52+00:00 ((2455854j,14512s,0n),+0s,2299161j)>
irb(main):009:0> datetime_from_time = Time.at(int_times.first).to_datetime
=> #<DateTime: 2011-10-19T00:01:52-04:00 ((2455854j,14512s,0n),-14400s,2299161j)>
irb(main):010:0>
irb(main):011:0* datetime_from_strptime === datetime_from_time
=> true
irb(main):012:0>
irb(main):013:0* Benchmark.measure do
irb(main):014:1* 100_000.times {
irb(main):015:2* times.each do |i|
irb(main):016:3* DateTime.strptime(i, format)
irb(main):017:3> end
irb(main):018:2> }
irb(main):019:1> end
=> #<Benchmark::Tms:0x00007fbdc18f0d28 @label="", @real=0.8680500000045868, @cstime=0.0, @cutime=0.0, @stime=0.009999999999999998, @utime=0.86, @total=0.87>
irb(main):020:0>
irb(main):021:0* Benchmark.measure do
irb(main):022:1* 100_000.times {
irb(main):023:2* int_times.each do |i|
irb(main):024:3* Time.at(i).to_datetime
irb(main):025:3> end
irb(main):026:2> }
irb(main):027:1> end
=> #<Benchmark::Tms:0x00007fbdc3108be0 @label="", @real=0.33059399999910966, @cstime=0.0, @cutime=0.0, @stime=0.0, @utime=0.32000000000000006, @total=0.32000000000000006>
****edited to not be completely and totally incorrect in every way****
****added benchmarks****

- 4,548
- 2
- 33
- 43
-
3Seemed plausible, and I upvoted already (cannot rescind now), but upon further checking your claim regarding UTC is untrue. The resulting DateTime/Time object will be in UTC vs local, yes, but the original timestamp is interpreted as being in UTC in both cases! So the moment in time is equal regardless of method. Try `Time.at(1318996912) == DateTime.strptime("1318996912",'%s')` in a non-UTC timezone and you will see! – Eero Apr 16 '14 at 08:33
-
5I'm sorry, but what you corrected is still wrong! :-) Run `Time.use_zone "Samoa" do Time.at(1318996912) == DateTime.strptime("1318996912",'%s') end` to verify that the times are equal, there is no LOCAL timestamp, and in both cases the Unix timestamp is interpreted as being in UTC. `Time.at` *presents* the resulting Time object in the local time zone, and `DateTime.strptime` *presents* the resulting DateTime object in UTC, but regardless of presentation they are equal, as they are the equivalent moment in time. – Eero May 22 '14 at 12:24
-
The statement *whereas `Time.at(1318996912) # => 2011-10-19 00:01:52 -0400` displays a return value in the LOCAL time zone* does not appear to be accurate... Can you please verify? I believe your statement would only be true if you used `Time.zone.at(1318996912)` – BigRon Aug 10 '17 at 17:36
-
Yeah, that appears to be accurate. My local machine is set to EST and the times display in EST. – WattsInABox Aug 11 '17 at 15:59
-
Can you provide an example where this is not the case @BigRon? What time zone, ruby version, etc does not behave this way? – WattsInABox Aug 14 '17 at 18:47
-
Ruby 2.3.0: `Time.zone.name #=> "Central Time (US & Canada)"` `Time.at(1318996912) #=> 2011-10-19 04:01:52 +0000` `Time.zone.at(1318996912) #=> Tue, 18 Oct 2011 23:01:52 CDT -05:00`. **Time.at** will always return time in UTC, no? – BigRon Aug 15 '17 at 12:41
-
... but the [ruby docs](https://ruby-doc.org/core-2.3.0/Time.html#method-c-at) appear to agree with you, "If a numeric argument is given, the result is in local time." Somehow my results are different... – BigRon Aug 15 '17 at 12:47
-
1Time is an mfer. I suppose I'm getting these results because of the way [Rails supers `Time.at`](https://github.com/rails/rails/blob/08e05d4a49c1ba1327e3e6821eba1f0c93361ab2/activesupport/lib/active_support/core_ext/time/calculations.rb#L42), and my Time.zone must not be set properly in Ruby – BigRon Aug 15 '17 at 13:00
-
yeah, try booting into an irb console outside of any project or gemset with rails installed to verify if you're super curious – WattsInABox Aug 15 '17 at 16:26
One command to convert date time to Unix format and then to string
DateTime.strptime(Time.now.utc.to_i.to_s,'%s').strftime("%d %m %y")
Time.now.utc.to_i #Converts time from Unix format
DateTime.strptime(Time.now.utc.to_i.to_s,'%s') #Converts date and time from unix format to DateTime
finally strftime is used to format date
Example:
irb(main):034:0> DateTime.strptime("1410321600",'%s').strftime("%d %m %y")
"10 09 14"

- 564
- 7
- 12
-
One thing to note is that epoch format does not have a timezone so chaining utc before chaining to_i is not necessary in `Time.now.utc.to_i`. – Trevor McCasland Jan 31 '20 at 20:20
This tells you the date of the number of seconds in future from the moment you execute the code.
time = Time.new + 1000000000 #date in 1 billion seconds
puts(time)
according to the current time I am answering the question it prints 047-05-14 05:16:16 +0000
(1 billion seconds in future)
or if you want to count billion seconds from a particular time, it's in format Time.mktime(year, month,date,hours,minutes)
time = Time.mktime(1987,8,18,6,45) + 1000000000
puts("I would be 1 Billion seconds old on: "+time)

- 9,926
- 10
- 42
- 71

- 69
- 1
- 1
If you wanted just a Date, you can do Date.strptime(invoice.date.to_s, '%s')
where invoice.date
comes in the form of anFixnum
and then converted to a String
.

- 9,489
- 5
- 46
- 56