2008-11-11 10 views
120

¿Cómo se convierte entre un objeto DateTime y un objeto Time en Ruby?Convierta a/desde Fecha y hora en Ruby

+1

No estoy seguro de si esta debería ser una pregunta separada, pero ¿cómo se convierte entre una fecha y una hora? –

+8

Las respuestas aceptadas y mejor calificadas ya no son las más precisas en las versiones modernas de Ruby. Vea las respuestas [por @theTinMan] (http://stackoverflow.com/a/8511699/405017) y [por @PatrickMcKenzie] (http://stackoverflow.com/a/280026/405017) a continuación. – Phrogz

Respuesta

44

Necesitará dos conversiones ligeramente diferentes.

convertir de Time a DateTime se puede modificar la clase de tiempo de la siguiente manera:

require 'date' 
class Time 
    def to_datetime 
    # Convert seconds + microseconds into a fractional number of seconds 
    seconds = sec + Rational(usec, 10**6) 

    # Convert a UTC offset measured in minutes to one measured in a 
    # fraction of a day. 
    offset = Rational(utc_offset, 60 * 60 * 24) 
    DateTime.new(year, month, day, hour, min, seconds, offset) 
    end 
end 

ajustes similares a la fecha le permitirá convertir DateTime a Time.

class Date 
    def to_gm_time 
    to_time(new_offset, :gm) 
    end 

    def to_local_time 
    to_time(new_offset(DateTime.now.offset-offset), :local) 
    end 

    private 
    def to_time(dest, method) 
    #Convert a fraction of a day to a number of microseconds 
    usec = (dest.sec_fraction * 60 * 60 * 24 * (10**6)).to_i 
    Time.send(method, dest.year, dest.month, dest.day, dest.hour, dest.min, 
       dest.sec, usec) 
    end 
end 

Tenga en cuenta que debe elegir entre la hora local y la hora GM/UTC.

Ambos fragmentos de código están tomados de O'Reilly's Ruby Cookbook. Su código de reutilización policy permite esto.

+5

Esto se dividirá en 1.9, donde DateTime # sec_fraction devuelve la cantidad de milisegundos en un segundo.Para 1.9 usted quiere usar: usec = dest.sec_fraction * 10 ** 6 – dkubb

163
require 'time' 
require 'date' 

t = Time.now 
d = DateTime.now 

dd = DateTime.parse(t.to_s) 
tt = Time.parse(d.to_s) 
+11

+1 Esta puede no ser la más eficiente en la ejecución, pero funciona, es concisa y es muy legible. –

+6

Desafortunadamente, esto solo funciona cuando se trata de horarios locales. Si comienza con un DateTime o Time con una zona horaria diferente, la función de análisis se convertirá en la zona horaria local. Básicamente, pierdes la zona horaria original. – Alkaline

+6

A partir de ruby ​​1.9.1, DateTime.parse conserva la zona horaria. (No tengo acceso a versiones anteriores.) Time.parse no conserva la zona horaria, porque representa el tiempo_tiempo estándar POSIX, que creo que es una diferencia entera de época. Cualquier conversión a Time debería tener el mismo comportamiento. – anshul

9

Por desgracia, los DateTime.to_time, Time.to_datetimeTime.parse y funciones no retienen la información de zona horaria. Todo se convierte a la zona horaria local durante la conversión. La aritmética de fechas aún funciona pero no podrá mostrar las fechas con sus zonas horarias originales. Esa información de contexto a menudo es importante. Por ejemplo, si quiero ver las transacciones realizadas durante el horario comercial en Nueva York, probablemente prefiera verlas en sus zonas horarias originales, no en la zona horaria local en Australia (12 horas antes que en Nueva York).

Los siguientes métodos de conversión mantienen esa información.

Para Ruby 1.8, mira Gordon Wilson's answer. Es del buen viejo confiable libro de cocina de Ruby.

Para Ruby 1.9, es un poco más fácil.

require 'date' 

# Create a date in some foreign time zone (middle of the Atlantic) 
d = DateTime.new(2010,01,01, 10,00,00, Rational(-2, 24)) 
puts d 

# Convert DateTime to Time, keeping the original timezone 
t = Time.new(d.year, d.month, d.day, d.hour, d.min, d.sec, d.zone) 
puts t 

# Convert Time to DateTime, keeping the original timezone 
d = DateTime.new(t.year, t.month, t.day, t.hour, t.min, t.sec, Rational(t.gmt_offset/3600, 24)) 
puts d 

Esto imprime la siguiente

2010-01-01T10:00:00-02:00 
2010-01-01 10:00:00 -0200 
2010-01-01T10:00:00-02:00 

La información DateTime original completa incluyendo zona horaria se mantiene.

+4

wow, esto apesta .. ¿Por qué es tan difícil lidiar con el tiempo? – allyourcode

+1

El tiempo es complicado ... –

+2

El tiempo es complicado, pero no hay excusa para no proporcionar una conversión incorporada entre diferentes clases de tiempo incorporadas. Puede lanzar una RangeException si intenta obtener un UNIX time_t para 4713 BC (aunque un valor negativo de BigNum sería mejor), pero al menos proporcione un método para ello. –

53

Como una actualización del estado del ecosistema de Ruby, Date, DateTime y Time ahora tenemos métodos para convertir entre las diversas clases. El uso de Ruby 1.9.2+:

pry 
[1] pry(main)> ts = 'Jan 1, 2000 12:01:01' 
=> "Jan 1, 2000 12:01:01" 
[2] pry(main)> require 'time' 
=> true 
[3] pry(main)> require 'date' 
=> true 
[4] pry(main)> ds = Date.parse(ts) 
=> #<Date: 2000-01-01 (4903089/2,0,2299161)> 
[5] pry(main)> ds.to_date 
=> #<Date: 2000-01-01 (4903089/2,0,2299161)> 
[6] pry(main)> ds.to_datetime 
=> #<DateTime: 2000-01-01T00:00:00+00:00 (4903089/2,0,2299161)> 
[7] pry(main)> ds.to_time 
=> 2000-01-01 00:00:00 -0700 
[8] pry(main)> ds.to_time.class 
=> Time 
[9] pry(main)> ds.to_datetime.class 
=> DateTime 
[10] pry(main)> ts = Time.parse(ts) 
=> 2000-01-01 12:01:01 -0700 
[11] pry(main)> ts.class 
=> Time 
[12] pry(main)> ts.to_date 
=> #<Date: 2000-01-01 (4903089/2,0,2299161)> 
[13] pry(main)> ts.to_date.class 
=> Date 
[14] pry(main)> ts.to_datetime 
=> #<DateTime: 2000-01-01T12:01:01-07:00 (211813513261/86400,-7/24,2299161)> 
[15] pry(main)> ts.to_datetime.class 
=> DateTime 
+1

DateTime.to_time devuelve un DateTime ... '1.9.3p327: 007> ts = '2000-01-01 12:01:01 -0700' =>" 2000-01-01 12:01:01 - 0700 " 1.9.3p327: 009> dt = ts.to_datetime => sáb, 01 ene 2000 12:01:01 -0700 1.9.3p327: 010> dt.to_time => sáb, 01 ene 2000 12:01 : 01 -0700 1.9.3p327: 011> dt.to_time.class => DateTime' –

+0

Vaya. Me acabo de dar cuenta de que se trata de un problema de Ruby on Rails y no de Ruby: http://stackoverflow.com/questions/11277454/weird-ruby-behavior-in-datetime-to-time-conversion. Incluso tenían un error archivado en contra de este método en la línea 2.x y lo marcaron como "no corregirá". Horrible decisión en mi humilde opinión. El comportamiento de Rails rompe por completo la interfaz subyacente de Ruby. –

1

La mejora en la solución de Gordon Wilson, aquí está mi intento:

def to_time 
    #Convert a fraction of a day to a number of microseconds 
    usec = (sec_fraction * 60 * 60 * 24 * (10**6)).to_i 
    t = Time.gm(year, month, day, hour, min, sec, usec) 
    t - offset.abs.div(SECONDS_IN_DAY) 
end 

obtendrá el mismo tiempo en UTC, perdiendo la zona horaria (por desgracia)

Además, si tiene ruby ​​1.9, simplemente pruebe el método to_time