DateTime
和Time
类在Ruby中有什么区别,我该如何选择使用哪个?
新版本的Ruby(2.0+)在这两个类之间没有显着的区别。出于历史原因,一些库将使用其中之一,但新代码不一定需要关注。为了保持一致性,最好选择一个类,并尽量与你的库相匹配。例如,ActiveRecord更喜欢DateTime。
在Ruby 1.9之前的版本和许多系统中,Time被表示为描述自1970年1月1日UTC以来秒数的32位有符号值,它是围绕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可以处理来自亚里士多德的博客文章,这让人感到放心。
在选择DateTime和Time之间,现在差异有些主观。历史上,DateTime提供了更好的选项来按日历方式操作它,但是在Rails环境中,其中许多方法也已经移植到了Time中。
[2018年7月编辑]
所有以下内容在Ruby 2.5.1中仍然有效。根据参考文档:
DateTime不考虑任何闰秒,也不跟踪任何夏令时规则。
此前这个主题中没有注意到的是DateTime
的少数优点之一:它知道历法改革,而Time
则不知道:
[…] Ruby的Time类实现一个连续的Gregorian日历,并没有历法改革的概念 […].
参考文献总结建议,在只处理近期、当前或未来日期/时间时使用Time
,只有在需要准确转换例如莎士比亚的生日时才使用DateTime
:(强调添加)
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
时性能更好(而不是Bignum
(超出 Integer
范围的值)或Rational
(跟踪纳秒时)):DateTime
不再覆盖比Time
更广泛的潜在值。
此外,还应该注意DateTime
的两个先前未提到的限制:DateTime
不考虑闰秒:。它也不跟踪任何夏令时规则。(http://www.ruby-doc.org/stdlib-2.1.0/libdoc/date/rdoc/Date.html#class-Date-label-DateTime)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
一起使用,操作系统需要支持闰秒,并且需要正确设置时区信息,例如通过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>
DateTime
之外的正确偏移量。Time
是更好的选择。Time.new(2012,6,30,23,59,60,0)
,并且始终得到 2012-07-01 00:00:00 +0000
。 - michauTime
是否支持闰秒都取决于您的操作系统和时区配置。例如: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
。 - Niels Ganser# 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
。 - Phương Nguyễn我发现在不同时区解析和计算一天的开始/结束时间使用DateTime会更容易,假设您正在使用ActiveSupport扩展。
在我的情况下,我需要根据用户的本地时间(字符串形式,例如 "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 - 注意偏移量已更改)。但要做到这一点,您需要更多有关用户时区的信息(不仅仅是偏移量,还包括是否使用夏令时)。 - Petr ''Bubák'' ŠedivýDateTime
已经解析了您提供的偏移量,即 +0100。您没有给 Time
提供偏移量,而是时区(“CET”不描述偏移量,它是时区的名称)。时区在一年中可能具有不同的偏移量,但偏移量始终是相同的。 - MeckiNo DateTime
Don’t use DateTime unless you need to account for historical calendar reform - and if you do, explicitly specify the start argument to clearly state your intentions.
# 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)
考虑它们如何以不同的方式处理时区问题:
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
Date
(毫不意外地)仅解析日期,并且当 Date
转换为 Time
时,始终使用本地时区的午夜作为时间。Time
和 TimeDate
之间的差异源于 Time
不理解英国夏令时,这令人惊讶,因为通常 Time
更正确地处理时区(例如考虑到夏令时)。因此,在这种情况下,只有 DateTime
正确解析整个字符串。 - michau