Разница между датой и временем в рубине - PullRequest
210 голосов
/ 11 августа 2009

В чем разница между DateTime и Time классами в Ruby и какие факторы могут заставить меня выбрать один или другой?

Ответы [ 7 ]

172 голосов
/ 11 августа 2009

Более новые версии 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, которая служит оболочкой для стандартных полей SQL DATETIME. Они содержат произвольные даты и могут представлять практически любой момент времени, поскольку диапазон выражений обычно очень велик.

DateTime.new
# => Mon, 01 Jan -4712 00:00:00 +0000

Так что отрадно, что DateTime может обрабатывать сообщения в блоге от Аристотеля.

При выборе одного различия несколько субъективны. Исторически DateTime предоставлял лучшие варианты для манипулирования им в календарном режиме, но многие из этих методов также были перенесены на Time, по крайней мере, в среде Rails.

92 голосов
/ 12 января 2014

[Изменить июль 2018]

Все вышеперечисленное все еще верно в Ruby 2.5.1. Из справочной документации :

DateTime не учитывает никаких високосных секунд, не отслеживает никаких правил летнего времени.

То, что не было отмечено в этой теме ранее, является одним из немногих преимуществ DateTime: он осведомлен о календарных реформах, тогда как Time - это не:

[…] Класс Time в Ruby реализует логический григорианский календарь и не имеет понятия о реформе календаря […].

Справочная документация заканчивается рекомендацией использовать Time при работе исключительно с датами / временем ближайшего, текущего или будущего и использовать DateTime только тогда, когда, например, день рождения Шекспира должен быть точно преобразован: (выделение добавленное) * * тысяча двадцать-одна

Итак, когда вы должны использовать DateTime в Ruby и когда вы должны использовать Time? Почти наверняка вам захочется использовать Time, поскольку ваше приложение, вероятно, имеет дело с текущими датами и временем. Однако, если вам нужно иметь дело с датами и временем в историческом контексте, вы захотите использовать DateTime […]. Если вам также приходится иметь дело с часовыми поясами, тогда удачи - просто имейте в виду, что вы, вероятно, будете иметь дело с местным солнечным временем, поскольку только в 19 веке введение железных дорог потребовало стандартного времени. и в конечном итоге часовые пояса.

[/ Edit Июль 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 с (против Bignum с (значения вне диапазона Integer) или Rational с (когда отслеживаются наносекунды) )):

Начиная с Ruby 1.9.2, реализация Time использует 63-разрядное целое число со знаком, Bignum или Rational. Целое число - это число наносекунд, начиная с эпохи, которое может представлять 1823-11-12 до 2116-02-20. Когда используется Bignum или Rational (до 1823, после 2116, в течение наносекунды), время работает медленнее, чем при использовании целого числа. (http://www.ruby -doc.org / ядро-2.1.0 / Time.html )

Другими словами, насколько я понимаю, DateTime больше не охватывает более широкий диапазон потенциальных значений, чем Time.

Кроме того, следует отметить два ранее не упомянутых ограничения DateTime:

DateTime не учитывает никаких скачков, не отслеживает правила летнего времени. (http://www.ruby -doc.org / STDLIB-2.1.0 / libdoc / дата / RDoc / Date.html # класс-Date-метка-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_i
=> 1341100823

Во-вторых, 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, оно считается в днях.

66 голосов
/ 14 августа 2009

Устаревшие! Смотри ниже ...

Разница в производительности не может быть подчеркнута достаточно ... Время равно C, а DateTime равно Ruby:

>> Benchmark.bm do |bm|
?>   bm.report('DateTime:') do
?>     n1 = DateTime.now
>>     n2 = DateTime.now
>>     1_000_000.times{ n1 < n2 }
>>   end
>>   bm.report('Time:    ') do
?>     n1 = Time.now
>>     n2 = Time.now
>>     1_000_000.times{ n1 < n2 }
>>   end
>> end
      user     system      total        real
DateTime:  4.980000   0.020000   5.000000 (  5.063963)
Time:      0.330000   0.000000   0.330000 (  0.335913)

Обновление (2/2012):

Как уже упоминалось в комментарии, 1.9.3 значительно улучшила производительность DateTime:

       user     system      total        real
DateTime:  0.330000   0.000000   0.330000 (  0.333869)
Time:      0.300000   0.000000   0.300000 (  0.306444)
43 голосов
/ 12 августа 2009

Я думаю, что ответ на вопрос «в чем разница» является одним из неудачных общих ответов на этот вопрос в стандартных библиотеках 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
10 голосов
/ 02 ноября 2012

Я обнаружил, что такие вещи, как разбор и вычисление начала / конца дня в разных часовых поясах, проще сделать с 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.

4 голосов
/ 02 сентября 2016

Рассмотрим, как они по-разному обрабатывают часовые пояса с помощью пользовательских экземпляров:

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

Это может быть сложно при создании временных диапазонов и т. Д.

0 голосов
/ 23 июня 2018

Кажется, что в некоторых случаях поведение очень отличается:

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"

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...