Converti in / da DateTime e Time in Ruby


Risposte:


50

Avrai bisogno di due conversioni leggermente diverse.

Per convertire da Time a DateTimete puoi modificare la classe Time come segue:

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

Modifiche simili a Data ti permetteranno di convertire DateTime in 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

Si noti che è necessario scegliere tra l'ora locale e l'ora GM / UTC.

Entrambi i frammenti di codice sopra riportati sono tratti dal ricettario Ruby di O'Reilly . La loro politica di riutilizzo del codice lo consente.


5
Questo si interromperà su 1.9 dove DateTime # sec_fraction restituisce il numero di millisecondi in un secondo. Per 1.9 si desidera utilizzare: usec = dest.sec_fraction * 10 ** 6
dkubb

185
require 'time'
require 'date'

t = Time.now
d = DateTime.now

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

13
+1 Potrebbe non essere il più efficiente nell'esecuzione, ma funziona, è conciso ed è molto leggibile.
Walt Jones,

6
Purtroppo questo funziona davvero solo quando si ha a che fare con l'ora locale. Se si inizia con un DateTime o Time con un fuso orario diverso, la funzione di analisi verrà convertita in fuso orario locale. In pratica perdi il fuso orario originale.
Bernard,

6
A partire dal rubino 1.9.1, DateTime.parse conserva il fuso orario. (Non ho accesso alle versioni precedenti.) Time.parse non preserva il fuso orario, perché rappresenta lo standard POSIX time_t, che credo sia una differenza intera rispetto all'epoca. Qualsiasi conversione in Time dovrebbe avere lo stesso comportamento.
anshul

1
Hai ragione. DateTime.parse funziona in 1.9.1 ma non Time.parse. In ogni caso, è meno soggetto a errori (coerente) e probabilmente più veloce da utilizzare DateTime.new (...) e Time.new (..). Vedi la mia risposta per il codice di esempio.
Bernard,

1
Ciao @anshul. Non sto insinuando che sto affermando :-). Le informazioni sul fuso orario non vengono conservate quando si utilizza Time.parse (). È facile da testare. Nel codice sopra riportato, è sufficiente sostituire d = DateTime.now con d = DateTime.new (2010.01,01, 10,00,00, Rational (-2, 24)). tt ora mostrerà la data d convertita nel tuo fuso orario locale. Puoi ancora eseguire l'aritmetica della data e tutte le informazioni tz originali sono andate perse. Questa informazione è un contesto per la data ed è spesso importante. Vedi qui: stackoverflow.com/questions/279769/…
Bernard

63

Come aggiornamento allo stato dell'ecosistema Ruby Date, DateTimee Timeora hanno metodi per la conversione tra le varie classi. Utilizzando 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 restituisce 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 => Sat, 01 Jan 2000 12:01:01 -0700 1.9.3p327 :010 > dt.to_time => Sat, 01 Jan 2000 12:01:01 -0700 1.9.3p327 :011 > dt.to_time.class => DateTime
Jesse Clark,

Ops. Ho appena realizzato che si tratta di un problema di Ruby on Rails e non di Ruby: stackoverflow.com/questions/11277454/… . Hanno persino presentato un bug su questo metodo nella riga 2.x e lo hanno contrassegnato come "non risolto". Decisione orribile IMHO. Il comportamento di Rails rompe totalmente l'interfaccia di Ruby sottostante.
Jesse Clark,

12

Sfortunatamente, il DateTime.to_time, Time.to_datetimeeTime.parse funzioni non mantengono le informazioni sul fuso orario. Tutto viene convertito in fuso orario locale durante la conversione. L'aritmetica della data funziona ancora, ma non sarà possibile visualizzare le date con i loro fusi orari originali. Le informazioni sul contesto sono spesso importanti. Ad esempio, se voglio vedere le transazioni eseguite durante l'orario di lavoro a New York, probabilmente preferisco vederle visualizzate nei loro fusi orari originali, non nel mio fuso orario locale in Australia (che è 12 ore avanti rispetto a New York).

I seguenti metodi di conversione mantengono le informazioni tz.

Per Ruby 1.8, guarda la risposta di Gordon Wilson . Viene dal buon vecchio affidabile Ruby Cookbook.

Per Ruby 1.9, è leggermente più semplice.

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

Questo stampa quanto segue

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

Vengono conservate tutte le informazioni originali su DateTime incluso il fuso orario.


2
Il tempo è complicato, ma non ci sono scuse per non fornire la conversione integrata tra diverse classi temporali integrate. Puoi lanciare un RangeException se provi a ottenere un time_t UNIX per 4713 aC (anche se un valore negativo di BigNum sarebbe più bello), ma almeno forniscene un metodo.
Mark Reed,

1
Time#to_datetimesembra preservare tz per me:Time.local(0).to_datetime.zone #=> "-07:00"; Time.gm(0).to_datetime.zone #=> "+00:00"
Phrogz

L'offset UTC di @Phrogz non è la stessa cosa di un fuso orario. Uno è costante, l'altro può cambiare in diversi periodi dell'anno per l'ora legale. DateTime non ha una zona, ignora l'ora legale. Il tempo lo rispetta, ma solo nella TZ "locale" (ambiente di sistema).
Andrew Vit,

1

Migliorando la soluzione Gordon Wilson, ecco il mio tentativo:

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

Avrai lo stesso tempo in UTC, perdendo il fuso orario (purtroppo)

Inoltre, se hai ruby ​​1.9, prova il to_timemetodo


0

Mentre si effettuano tali conversioni, si dovrebbe prendere in considerazione il comportamento dei fusi orari durante la conversione da un oggetto all'altro. Ho trovato alcune buone note ed esempi in questo post di StackOverflow .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.