如何将Unix时间戳(自纪元以来的秒数)转换为Ruby DateTime?


Answers:


354

DateTime.strptime自时代以来可以处理几秒钟。该数字必须转换为字符串:

require 'date'
DateTime.strptime("1318996912",'%s')

4
这无法处理小数秒
Dan Sandberg 2013年

45
它确实可以处理毫秒%Q
迷你约翰

3
跟进@TheMiniJohn的答案。看来Time有必要代替DateTime。因此,使用Time.strptime("1318996912345",'%Q').to_f,您将看到保留的毫秒数,而DateTime.strptime("1318996912345",'%Q').to_f没有保留它。
skensell'2

625

抱歉,短暂的突触失败时刻。这是真正的答案。

require 'date'

Time.at(seconds_since_epoch_integer).to_datetime

简短示例(考虑了当前系统时区):

$ 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)> 

进一步更新(适用于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)>

最近更新:一两个星期前,在使用HA服务时,我对该线程中的顶级解决方案进行了基准测试,并惊讶地发现它的Time.at(..)性能胜过DateTime.strptime(..)(更新:添加了更多基准测试)。

# ~ % 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>

1
谢谢...下面的答案更为简洁,我找到了Time.at,但试图找到与DateTime等价的东西。
Tronathan 2011年

27
这很有趣,但是Time.at()。to_datetime似乎比DateTime.strptime()更令人愉悦,原因仅在于可读性……至少对我而言
tybro0103 2012年

34
这与上面的解析器不同,Time.at假定当前时区,其中DateTime.strptime使用UTC。
Vitaly Babiy 2013年

5
Time.at表现出色并不奇怪DateTime.strptime。后者必须解析一个字符串,通常比直接输入数字要慢得多。

2
您的基准测试并不是完全测试,DateTime.strptime因为它每次迭代都会创建两个新的String,这非常昂贵。不仅仅是像@claw所说的那样解析字符串
WattsInABox 2015年

67

时区处理

我只是想澄清一下,即使已经对此发表了评论,所以未来的人们不会错过这个非常重要的区别。

DateTime.strptime("1318996912",'%s') # => Wed, 19 Oct 2011 04:01:52 +0000

以UTC显示返回值,并要求秒为字符串并输出UTC Time对象,而

Time.at(1318996912) # => 2011-10-19 00:01:52 -0400

在LOCAL时区显示返回值,通常需要FixNum参数,但是Time对象本身仍然处于UTC,即使显示不是。

因此,即使我为两个方法都传递了相同的整数,但由于类的#to_s方法是如何工作的,我看起来还是有两个不同的结果。但是,由于@Eero必须两次提醒我:

Time.at(1318996912) == DateTime.strptime("1318996912",'%s') # => true

两个返回值之间的相等比较仍然返回true。同样,这是因为值基本上是相同的(尽管类不同#==,但是该#to_s方法将为您处理),但是该方法将打印出截然不同的字符串。虽然,如果我们查看字符串,我们可以看到它们确实是同一时间,只是在不同时区打印。

方法参数说明

文档还说:“如果给出数字参数,则结果是本地时间。” 这是有道理的,但对我来说有点困惑,因为它们没有提供文档中非整数参数的任何示例。因此,对于一些非整数参数示例:

Time.at("1318996912")
TypeError: can't convert String into an exact number

您不能使用String参数,但可以在其中使用Time参数Time.at,它将在参数的时区中返回结果:

Time.at(Time.new(2007,11,1,15,25,0, "+09:00"))
=> 2007-11-01 15:25:00 +0900

基准测试

与@AdamEberlin讨论了他的答案后,我决定发布稍有更改的基准,以使所有内容尽可能相等。另外,我再也不需要构建它们,因此这是保存它们的理想场所。

Time.at(int).to_datetime〜快2.8倍

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>

****编辑为在各方面均不完全不正确****

****增加了基准****


3
似乎是合理的,我已经投票赞成(现在无法撤消),但是在进一步检查后,您关于UTC的主张是不正确的。生成的DateTime / Time对象将采用UTC vs本地,是的,但是在两种情况下,原始时间戳都被解释为采用UTC!因此,无论采用何种方法,时刻都是相等的。Time.at(1318996912) == DateTime.strptime("1318996912",'%s')在非UTC时区尝试,您将看到!
Eero

4
对不起,您所纠正的仍然是错误的!:-)运行Time.use_zone "Samoa" do Time.at(1318996912) == DateTime.strptime("1318996912",'%s') end以验证时间是否相等,没有本地时间戳,并且在两种情况下,Unix时间戳都解释为采用UTC。 在本地时区中Time.at 显示结果Time对象,在UTC中DateTime.strptime 显示结果DateTime对象,但是无论显示方式如何,它们都是相等的,因为它们是等效的时刻。
Eero 2014年

声明Time.at(1318996912) # => 2011-10-19 00:01:52 -0400显示本地时区中的返回值 似乎不准确......你能请核实?我相信您的陈述只有在您使用时才是正确的Time.zone.at(1318996912)
-BigRon

是的,这似乎是正确的。我的本地计算机设置为EST,时间显示在EST中。
WattsInABox

您能否提供一个示例,而不是@BigRon?哪个时区,红宝石版本等无法正常运行?
WattsInABox

10

一个将日期时间转换为Unix格式然后转换为字符串的命令

    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

最后strftime用于格式化日期

例:

    irb(main):034:0> DateTime.strptime("1410321600",'%s').strftime("%d %m %y")
    "10 09 14"

需要注意的一件事是,纪元格式没有时区,因此在中不需要在链接到to_i之前先链接utc Time.now.utc.to_i
Trevor McCasland

1

这告诉您从执行代码起算的未来秒数的日期。

time = Time.new + 1000000000 #date in 1 billion seconds

投入时间

根据当前时间,我正在回答它打印的问题047-05-14 05:16:16 +0000(未来10亿秒)

或者如果您想从特定时间算起十亿秒,则采用以下格式 Time.mktime(year, month,date,hours,minutes)

time = Time.mktime(1987,8,18,6,45) + 1000000000

puts(“我的年龄为10亿秒:” +时间)


0

如果你想只是一个日期,你可以Date.strptime(invoice.date.to_s, '%s')在那里invoice.date进来的形式Fixnum,然后转换成一个String


3
Time.at(1500923406).to_date.to_s=>"2017-07-24"
Chloe
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.