Answers:
较新版本的Ruby(2.0+)在这两个类之间并没有太大的区别。某些图书馆出于历史原因会使用其中一种,但是不一定需要关注新代码。为保持一致性,最好选择一个,因此请尝试与您的库期望的内容相吻合。例如,ActiveRecord首选DateTime。
在Ruby 1.9之前的版本中以及在许多系统上,时间表示为32位带符号的值,该值描述自1970年1月1日UTC以来的秒数,这是围绕POSIX标准time_t
值的精简包装,并且有界:
Time.at(0x7FFFFFFF)
# => Mon Jan 18 22:14:07 -0500 2038
Time.at(-0x7FFFFFFF)
# => Fri Dec 13 15:45:53 -0500 1901
较新版本的Ruby能够处理更大的值而不会产生错误。
DateTime是一种基于日历的方法,其中年,月,日,时,分和秒分别存储。这是Ruby on Rails构造,用作SQL标准DATETIME字段的包装。它们包含任意日期,并且可以表示几乎任何时间点,因为表达式的范围通常很大。
DateTime.new
# => Mon, 01 Jan -4712 00:00:00 +0000
因此,令人放心的是DateTime可以处理Aristotle的博客文章。
选择一个时,差异现在有些主观。从历史上看,DateTime提供了更好的选项以日历方式进行操作,但是至少在Rails环境中,许多这些方法也已移植到Time上。
[2018年7月编辑]
以下所有内容在Ruby 2.5.1中仍然适用。从参考文档中:
DateTime不考虑任何leap秒,不跟踪任何夏令时规则。
之前在此主题中未注意到的是以下几项优点之一DateTime
:它知道日历改革,而Time
没有:
[…] Ruby的Time类实现了多功的公历,没有日历改革的概念[…]。
参考文档的结论是建议Time
仅在处理近乎过去,当前或将来的日期/时间DateTime
时使用,并且仅在例如莎士比亚的生日需要准确转换时才使用:(强调)
那么什么时候应该在Ruby中使用DateTime以及何时应该使用Time?几乎可以肯定,您将要使用时间,因为您的应用程序可能正在处理当前日期和时间。但是,如果您需要在历史环境中处理日期和时间,则需要使用DateTime […]。如果您还必须处理时区,那么祝您好运-请记住,您可能会处理当地的太阳时间,因为直到19世纪,铁路的引入才需要标准时间最后是时区。
[/编辑2018年7月]
从ruby 2.0开始,其他答案中的大多数信息已过时。
特别Time
是现在几乎不受限制。与Epoch的距离可能大于或小于63位:
irb(main):001:0> RUBY_VERSION
=> "2.0.0"
irb(main):002:0> Time.at(2**62-1).utc # within Integer range
=> 146138514283-06-19 07:44:38 UTC
irb(main):003:0> Time.at(2**128).utc # outside of Integer range
=> 10783118943836478994022445751222-08-06 08:03:51 UTC
irb(main):004:0> Time.at(-2**128).utc # outside of Integer range
=> -10783118943836478994022445747283-05-28 15:55:44 UTC
使用较大值的唯一结果应该是性能,这在使用Integer
s(vs。s Bignum
(超出Integer
范围的值)或Rational
s(跟踪纳秒)时更好):
从Ruby 1.9.2开始,Time实现使用带符号的63位整数Bignum或Rational。整数是自纪元以来的纳秒数,可以表示1823-11-12至2116-02-20。当使用Bignum或Rational时(在1823之前,2116之后,在纳秒以下),时间的使用速度比使用整数时慢。(http://www.ruby-doc.org/core-2.1.0/Time.html)
换句话说,据我所知,与相比,DateTime
涵盖的潜在价值范围不再广泛Time
。
另外,DateTime
应该注意两个之前未提及的限制:
DateTime不考虑任何leap秒,不跟踪任何夏令时规则。(http://www.ruby-doc.org/stdlib-2.1.0/libdoc/date/rdoc/Date.html#class-Date-label-DateTime)
首先,DateTime
没有of秒的概念:
irb(main):001:0> RUBY_VERSION
=> "2.0.0"
irb(main):002:0> require "date"
=> true
irb(main):003:0> t = Time.new(2012,6,30,23,59,60,0)
=> 2012-06-30 23:59:60 +0000
irb(main):004:0> dt = t.to_datetime; dt.to_s
=> "2012-06-30T23:59:59+00:00"
irb(main):005:0> t == dt.to_time
=> false
irb(main):006:0> t.to_i
=> 1341100824
irb(main):007:0> dt.to_time.to_i
=> 1341100823
对于上面的示例Time
,操作系统需要支持leap秒,并且需要正确设置时区信息,例如通过TZ=right/UTC irb
(在许多Unix系统上)。
其次,DateTime
对时区的了解非常有限,尤其是没有夏令时的概念。它几乎可以将时区处理为简单的UTC + X偏移量:
irb(main):001:0> RUBY_VERSION
=> "2.0.0"
irb(main):002:0> require "date"
=> true
irb(main):003:0> t = Time.local(2012,7,1)
=> 2012-07-01 00:00:00 +0200
irb(main):004:0> t.zone
=> "CEST"
irb(main):005:0> t.dst?
=> true
irb(main):006:0> dt = t.to_datetime; dt.to_s
=> "2012-07-01T00:00:00+02:00"
irb(main):007:0> dt.zone
=> "+02:00"
irb(main):008:0> dt.dst?
NoMethodError: undefined method `dst?' for #<DateTime:0x007f34ea6c3cb8>
如果将时间输入为DST,然后转换为非DST时区,而又无法跟踪DateTime
自身以外的正确偏移,则可能会造成麻烦(许多操作系统实际上可能已经为您解决了这一问题)。
总的来说,我想说当今Time
对于大多数应用程序是更好的选择。
还要注意加法的重要区别:将数字添加到Time对象时,以秒为单位,但是将数字添加到DateTime中,则以天为单位。
Time
也没有of秒的概念,因此与没什么不同DateTime
。我不知道您在哪里运行示例,但是我尝试Time.new(2012,6,30,23,59,60,0)
了从2.0到2.7的不同Ruby版本,并且始终使用2012-07-01 00:00:00 +0000
。
Time
支持leap秒取决于您的操作系统和时区配置。例如:TZ=right/UTC ruby -e 'p Time.new(2012,6,30,23,59,60,0)'
=> 2012-06-30 23:59:60 +0000
而TZ=UTC ruby -e 'p Time.new(2012,6,30,23,59,60,0)'
=> 2012-07-01 00:00:00 +0000
。
我认为“有什么区别”的答案是Ruby标准库中对此问题的不幸的常见答案之一:这两个类/库是由不同的人在不同的时间以不同的方式创建的。与精心计划的Java之类的开发相比,这是Ruby演化的社区性质的不幸后果之一。开发人员需要新功能,但又不想使用现有的API,因此他们只是创建一个新类-对于最终用户,没有明显的理由使两者存在。
通常对于软件库来说都是这样:通常是某些代码或API成为历史记录而不是逻辑记录的原因。
诱惑是从DateTime开始,因为它看起来更通用。日期...时间,对不对?错误。时间也会使日期更好,并且实际上可以解析DateTime无法解析的时区。而且它的性能更好。
我最终无处不在使用时间。
为了安全起见,我倾向于允许将DateTime参数传递到我的Timey API中,然后进行转换。另外,如果我知道两者都具有我感兴趣的方法,那么我都可以接受,就像我为将时间转换为XML(用于XMLTV文件)而编写的这种方法一样
# Will take a date time as a string or as a Time or DateTime object and
# format it appropriately for xmtlv.
# For example, the 22nd of August, 2006 at 20 past midnight in the British Summertime
# timezone (i.e. GMT plus one hour for DST) gives: "20060822002000 +0100"
def self.format_date_time(date_time)
if (date_time.respond_to?(:rfc822)) then
return format_time(date_time)
else
time = Time.parse(date_time.to_s)
return format_time(time)
end
end
# Note must use a Time, not a String, nor a DateTime, nor Date.
# see format_date_time for the more general version
def self.format_time(time)
# The timezone feature of DateTime doesn't work with parsed times for some reason
# and the timezone of Time is verbose like "GMT Daylight Saving Time", so the only
# way I've discovered of getting the timezone in the form "+0100" is to use
# Time.rfc822 and look at the last five chars
return "#{time.strftime( '%Y%m%d%H%M%S' )} #{time.rfc822[-5..-1]}"
end
Time.new(2011, 11, 1, 10, 30)
产生的2011-11-01 10:30:00 +0700
同时DateTime.new(2011, 11, 1, 10, 30)
产生Tue, 01 Nov 2011 10:30:00 +0000
。
我发现,假设您使用的是ActiveSupport扩展,则使用 DateTime可以更轻松地完成诸如解析和计算不同时区中一天的开始/结束之类的事情。
在我的情况下,我需要根据我以字符串形式接收的用户本地时间(例如“ 2012-10-10 10:10 +0300”),计算用户时区(任意)中的一天结束时间
使用DateTime就像
irb(main):034:0> DateTime.parse('2012-10-10 10:10 +0300').end_of_day
=> Wed, 10 Oct 2012 23:59:59 +0300
# it preserved the timezone +0300
现在,让我们以与“时间”相同的方式进行尝试:
irb(main):035:0> Time.parse('2012-10-10 10:10 +0300').end_of_day
=> 2012-10-10 23:59:59 +0000
# the timezone got changed to the server's default UTC (+0000),
# which is not what we want to see here.
实际上,时间需要在解析之前知道时区(还请注意,是Time.zone.parse
,不是Time.parse
):
irb(main):044:0> Time.zone = 'EET'
=> "EET"
irb(main):045:0> Time.zone.parse('2012-10-10 10:10 +0300').end_of_day
=> Wed, 10 Oct 2012 23:59:59 EEST +03:00
因此,在这种情况下,使用DateTime绝对容易。
DateTime.parse('2014-03-30 01:00:00 +0100').end_of_day
产生Sun, 30 Mar 2014 23:59:59 +0100
,但Time.zone = 'CET'; Time.zone.parse('2014-03-30 01:00:00').end_of_day
产生Sun, 30 Mar 2014 23:59:59 CEST +02:00
(CET = + 01:00,CEST = + 02:00-注意偏移量已更改)。但是,为此,您需要有关用户时区的更多信息(不仅是偏移量,还包括是否使用节省时间)。
Time.zone.parse
是非常有用的不同区域解析时候-它迫使你去思考哪个区域,你应该使用。有时,Time.find_zone的效果甚至更好。
DateTime
确实解析了您给它的偏移量+0100。您没有提供Time
偏移量,但是提供了时区(“ CET”没有描述偏移量,它是时区的名称)。时区在一年中可以有不同的偏移量,但是偏移量是一个偏移量,并且始终相同。
考虑一下它们如何使用自定义实例来不同地处理时区:
irb(main):001:0> Time.new(2016,9,1)
=> 2016-09-01 00:00:00 -0400
irb(main):002:0> DateTime.new(2016,9,1)
=> Thu, 01 Sep 2016 00:00:00 +0000
irb(main):003:0> Time.new(2016,9,1).to_i
=> 1472702400
irb(main):004:0> DateTime.new(2016,9,1).to_i
=> 1472688000
创建时间范围等时,这可能会很棘手。
在某些情况下,行为似乎有很大不同:
Time.parse("Ends from 28 Jun 2018 12:00 BST").utc.to_s
“ 2018-06-28 09:00:00 UTC”
Date.parse("Ends from 28 Jun 2018 12:00 BST").to_time.utc.to_s
“ 2018-06-27 21:00:00 UTC”
DateTime.parse("Ends from 28 Jun 2018 12:00 BST").to_time.utc.to_s
“ 2018-06-28 11:00:00 UTC”
Date
(毫不奇怪)仅解析日期,并且当将a Date
转换Time
为时,它将始终使用本地时区的午夜作为时间。之间的差Time
和TimeDate
从该事实茎Time
不理解BST,这是令人惊讶的,因为时区通常由处理更正确地Time
(相对于夏令时,例如)。因此,在这种情况下,只能DateTime
正确解析整个字符串。
除了Niels Ganser的答案,您还可以考虑以下说法:
请注意,《 Ruby样式指南》非常清楚地说明了这一点:
没有日期时间
除非需要考虑历史日历改革,否则不要使用DateTime-如果这样做,请显式指定start参数以清楚地说明您的意图。
# bad - uses DateTime for current time DateTime.now # good - uses Time for current time Time.now # bad - uses DateTime for modern date DateTime.iso8601('2016-06-29') # good - uses Date for modern date Date.iso8601('2016-06-29') # good - uses DateTime with start argument for historical date DateTime.iso8601('1751-04-23', Date::ENGLAND)