Ответы:
Более новые версии Ruby (2.0+) на самом деле не имеют существенных различий между двумя классами. Некоторые библиотеки будут использовать одну или другую по историческим причинам, но новый код не обязательно должен быть связан с этим. Вероятно, лучше выбрать один для согласованности, поэтому постарайтесь сочетать с тем, что ожидают ваши библиотеки. Например, ActiveRecord предпочитает DateTime.
В версиях, предшествующих Ruby 1.9, и во многих системах время представляется в виде 32-разрядного знакового значения, описывающего количество секунд с 1 января 1970 года по Гринвичу, тонкой обертки вокруг стандартного time_t
значения POSIX и ограниченного:
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, которая служит оболочкой для полей DATETIME стандарта SQL. Они содержат произвольные даты и могут представлять практически любой момент времени, поскольку диапазон выражений обычно очень велик.
DateTime.new
# => Mon, 01 Jan -4712 00:00:00 +0000
Так что обнадеживает, что DateTime может обрабатывать сообщения в блоге от Аристотеля.
При выборе одного различия несколько субъективны. Исторически DateTime предоставлял лучшие варианты для манипулирования им по календарю, но многие из этих методов были перенесены и на Time, по крайней мере, в среде Rails.
[Изменить июль 2018 года]
Все перечисленное ниже относится и к Ruby 2.5.1. Из справочной документации :
DateTime не учитывает никаких дополнительных секунд, не отслеживает правила летнего времени.
То, что не было отмечено в этой теме ранее, является одним из немногих преимуществ DateTime
: он осведомлен о календарных реформах, а Time
не:
[…] Класс Time в Ruby реализует пролептический григорианский календарь и не имеет понятия о реформе календаря […].
Справочная документация заканчивается рекомендацией использовать, Time
когда речь идет исключительно о ближайших, текущих или будущих датах / временах, и использовать только DateTime
тогда, когда, например, день рождения Шекспира необходимо точно преобразовать: (выделение добавлено)
Итак, когда вы должны использовать DateTime в Ruby и когда вы должны использовать Time? Почти наверняка вам захочется использовать Time, поскольку ваше приложение, вероятно, имеет дело с текущими датами и временем. Однако, если вам нужно иметь дело с датами и временем в историческом контексте, вы захотите использовать DateTime […]. Если вам также приходится иметь дело с часовыми поясами, тогда удачи - просто имейте в виду, что вы, вероятно, будете иметь дело с местным солнечным временем, так как только в 19 веке введение железных дорог потребовало стандартного времени и в конечном итоге часовые пояса.
[/ Изменить июль 2018 года]
Начиная с ruby 2.0, большая часть информации в других ответах устарела.
В частности, Time
сейчас практически не связан. Это может быть больше или меньше 63 бит от Epoch:
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 (по сравнению Bignum
с s (значения вне 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 не учитывает никаких скачков, не отслеживает правила летнего времени. ( http://www.ruby-doc.org/stdlib-2.1.0/libdoc/date/rdoc/Date.html#class-Date-label-DateTime )
Во-первых, 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>
Это может вызвать проблемы, когда времена вводятся как DST, а затем преобразуются в часовой пояс не-DST, не отслеживая правильные смещения вне DateTime
себя (многие операционные системы могут фактически позаботиться об этом за вас).
В целом, я бы сказал, что в настоящее время Time
это лучший выбор для большинства приложений.
Также обратите внимание на важное различие при добавлении: когда вы добавляете число к объекту Time, оно считается в секундах, а когда вы добавляете число в DateTime, оно считается в днях.
Time
также не имеет понятия о високосных секундах, поэтому он не отличается от DateTime
. Я не знаю, где вы запускали свои примеры, но я пробовал Time.new(2012,6,30,23,59,60,0)
в разных версиях Ruby, от 2.0 до 2.7, и всегда получал 2012-07-01 00:00:00 +0000
.
Time
Поддерживает ли високосные секунды или нет, зависит от вашей ОС и конфигурации часового пояса. Например: 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: два класса / библиотеки были созданы по-разному разными людьми в разное время. Это одно из печальных последствий общественной природы эволюции Ruby по сравнению с тщательно спланированной разработкой чего-то вроде Java. Разработчики хотят новую функциональность, но не хотят наступать на существующие 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
.
Я обнаружил, что такие вещи, как разбор и вычисление начала / конца дня в разных часовых поясах, проще сделать с 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 должен знать часовой пояс перед синтаксическим анализом (также обратите внимание, что это 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
анализирует всю строку правильно.
В дополнение к ответу Нильса Гансера вы могли бы рассмотреть этот аргумент:
Обратите внимание, что в Руководстве по стилю 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)