How to convert a unix timestamp (seconds since epoch) to Ruby DateTime?

后端 未结 6 596
孤城傲影
孤城傲影 2020-11-30 16:21

How do you convert a Unix timestamp (seconds since epoch) to Ruby DateTime?

相关标签:
6条回答
  • 2020-11-30 17:01

    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>
    
    0 讨论(0)
  • 2020-11-30 17:02

    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.

    0 讨论(0)
  • 2020-11-30 17:05

    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****

    0 讨论(0)
  • 2020-11-30 17:11

    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)

    0 讨论(0)
  • 2020-11-30 17:12

    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"
    
    0 讨论(0)
  • 2020-11-30 17:14

    DateTime.strptime can handle seconds since epoch. The number must be converted to a string:

    require 'date'
    DateTime.strptime("1318996912",'%s')
    
    0 讨论(0)
提交回复
热议问题