Diferencia entre DateTime y Time en Ruby


¿Cuál es la diferencia entre las clases DateTime y Time en Ruby y qué factores me harían elegir una u otra?

Author: Arturo Herrero, 2009-08-11

7 answers

Las versiones más recientes de Ruby (2.0+) no tienen realmente diferencias significativas entre las dos clases. Algunas bibliotecas usarán una u otra por razones históricas, pero el nuevo código no necesariamente tiene que preocuparse. Elegir uno para la consistencia es probablemente lo mejor, así que intente encajar con lo que esperan sus bibliotecas. Por ejemplo, ActiveRecord prefiere DateTime.

En versiones anteriores a Ruby 1.9 y en muchos sistemas, el tiempo se representa como un valor con signo de 32 bits que describe el número de segundos desde el 1 de enero de 1970 UTC, una envoltura delgada alrededor de un valor POSIX-standard time_t, y está limitada a:

Time.at(0x7FFFFFFF)
# => Mon Jan 18 22:14:07 -0500 2038
Time.at(-0x7FFFFFFF)
# => Fri Dec 13 15:45:53 -0500 1901

Las versiones más recientes de Ruby son capaces de manejar valores más grandes sin producir errores.

DateTime es un enfoque basado en calendario donde el año, mes, día, hora, minuto y segundo se almacenan individualmente. Esta es una construcción de Ruby on Rails que sirve como un contenedor alrededor de los campos DATETIME estándar SQL. Estos contienen fechas arbitrarias y pueden representar casi cualquier punto en el tiempo como el rango de expresión es típicamente muy grande.

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

Así que es tranquilizador que DateTime pueda manejar las publicaciones de blog de Aristóteles.

Al elegir uno, las diferencias son algo subjetivas ahora. Históricamente, DateTime ha proporcionado mejores opciones para manipularlo en forma de calendario, pero muchos de estos métodos también se han portado al Tiempo, al menos dentro del entorno Rails.

 166
Author: tadman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-30 21:47:06

[Editar julio de 2018]

Todo lo que sigue siendo válido en Ruby 2.5.1. De la documentación de referencia :

DateTime no considera segundos intercalares, no rastrea ninguna regla de hora de verano.

Lo que no se ha señalado en este hilo antes es una de las pocas ventajas de DateTime: es consciente de las reformas del calendario, mientras que Time no lo es:

[Ruby] La clase Time de Ruby implementa un calendario gregoriano proléptico y no concepto de reforma del calendario [...].

La documentación de referencia concluye con la recomendación de usar Time cuando se trata exclusivamente de fechas/horas cercanas, actuales o futuras y solo usar DateTime cuando, por ejemplo, el cumpleaños de Shakespeare necesita ser convertido con precisión: (énfasis añadido)

Entonces, ¿cuándo debe usar DateTime en Ruby y cuándo debe usar Time? Es casi seguro que querrá usar el tiempo, ya que su aplicación probablemente esté tratando con la corriente fechas y horas. Sin embargo, si necesita tratar con fechas y horas en un contexto histórico, querrá usar DateTime [.]. Si también tiene que lidiar con zonas horarias, entonces la mejor de las suertes - solo tenga en cuenta que probablemente estará lidiando con tiempos solares locales, ya que no fue hasta el siglo 19 que la introducción de los ferrocarriles necesitó la necesidad de Tiempo Estándar y, finalmente, zonas horarias.

[/Editar julio de 2018]

A partir de ruby 2.0, la mayoría de los la información en las otras respuestas está desactualizada.

En particular, Time está ahora prácticamente sin consolidar. Puede estar más o menos a 63 bits de 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

La única consecuencia de usar valores más grandes debe ser el rendimiento, que es mejor cuando se usan Integers (vs. Bignum s (valores fuera del rango Integer) o Rationals (cuando se rastrean nanosegundos)):

Desde Ruby 1.9.2, la implementación de Time utiliza un entero de 63 bits con signo, Bignum o Racional. El entero es un número de nanosegundos desde la Época que puede representar 1823-11-12 a 2116-02-20. Cuando se usa Bignum o Racional (antes de 1823, después de 2116, bajo nanosegundos), el tiempo funciona más lento que cuando se usa entero. ( http://www.ruby-doc.org/core-2.1.0/Time.html )

En otras palabras, por lo que entiendo, DateTime ya no cubre una gama más amplia de valores potenciales que Time.

Además, dos restricciones no mencionadas anteriormente de DateTime probablemente debería tenerse en cuenta:

DateTime no considera ningún salto de segundo, no rastrea ninguna regla de horario de verano. (http://www.ruby-doc.org/stdlib-2.1.0/libdoc/date/rdoc/Date.html#class-Date-label-DateTime)

Primero, DateTime no tiene concepto de segundos intercalares:

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

Segundo, DateTime tiene una comprensión muy limitada de las zonas horarias y, en particular, no tiene un concepto de horario de verano. Prácticamente maneja el tiempo zonas como simples desplazamientos 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>

Esto puede causar problemas cuando los tiempos se ingresan como DST y luego se convierten en una zona horaria no DST sin realizar un seguimiento de las compensaciones correctas fuera de DateTime en sí (muchos sistemas operativos ya pueden encargarse de esto por usted).

En general, diría que hoy en día Time es la mejor opción para la mayoría de las aplicaciones.

También tenga en cuenta una diferencia importante en la suma: cuando se agrega un número a un objeto de tiempo, es contado en segundos, pero cuando se agrega un número a una fecha y hora, se cuenta en días.

 81
Author: Niels Ganser,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-25 15:21:03

Anticuado! Véase más adelante...

La diferencia de rendimiento no se puede enfatizar lo suficiente... El tiempo es C, y DateTime es 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)

Actualización (2/2012):

Como ya se mencionó en el comentario, 1.9.3 ha mejorado enormemente DateTime el rendimiento:

       user     system      total        real
DateTime:  0.330000   0.000000   0.330000 (  0.333869)
Time:      0.300000   0.000000   0.300000 (  0.306444)
 66
Author: Mladen Jablanović,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-27 19:13:54

Creo que la respuesta a "cuál es la diferencia" es una de las desafortunadas respuestas comunes a esta pregunta en las bibliotecas estándar de Ruby: las dos clases/bibliotecas fueron creadas de manera diferente por diferentes personas en diferentes momentos. Es una de las consecuencias desafortunadas de la naturaleza comunitaria de la evolución de Ruby en comparación con el desarrollo cuidadosamente planeado de algo como Java. Los desarrolladores quieren una nueva funcionalidad, pero no quieren pisar las API existentes, por lo que simplemente crean una nueva clase, hasta el final usuario no hay ninguna razón obvia para que los dos existan.

Esto es cierto para las bibliotecas de software en general: a menudo la razón por la que algún código o API es la forma en que es resulta ser histórica en lugar de lógica.

La tentación es comenzar con DateTime porque parece más genérico. Fecha... y Tiempo, ¿verdad? Equivocada. El tiempo también hace las fechas mejor, y de hecho puede analizar zonas horarias donde DateTime no puede.

He terminado usando el tiempo en todas partes.

To be seguro, aunque yo tiendo a permitir DateTime argumentos que se pasan en mi Timey Api, y convertir. También si sé que ambos tienen el método que me interesa, acepto cualquiera, como este método que escribí para convertir tiempos a XML (para archivos 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
 43
Author: Rhubarb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-12 10:33:12

Encontré que cosas como analizar y calcular el principio/final de un día en diferentes zonas horarias son más fáciles de hacer con DateTime, asumiendo que está utilizando las extensiones de ActiveSupport.

En mi caso necesitaba calcular el final del día en la zona horaria de un usuario (arbitrario) basado en la hora local del usuario que recibí como una cadena, p.ej. "2012-10-10 10:10 +0300"

Con DateTime es tan simple como

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

Ahora vamos a intentarlo de la misma manera con Hora:

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.

En realidad, el tiempo necesita conocer la zona horaria antes de analizar (también tenga en cuenta que es Time.zone.parse, no 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

Por lo tanto, en este caso es definitivamente más fácil ir con DateTime.

 9
Author: Yuriy Kharchenko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-02 16:14:24

Considere cómo manejan las zonas horarias de manera diferente con instanciaciones personalizadas:

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

Esto puede ser complicado al crear rangos de tiempo, etc.

 4
Author: gr8scott06,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-01 21:40:36

Parece que en algunos casos el comportamiento es muy diferente:

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"

 0
Author: Alexey Mifrill Strizhak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-23 12:00:35