Konvertieren Sie in Ruby nach / von DateTime und Time

131

Wie konvertiert man in Ruby zwischen einem DateTime- und einem Time-Objekt?

Schreibgeschützt
quelle
1
Ich bin mir nicht sicher, ob dies eine separate Frage sein sollte, aber wie konvertieren Sie zwischen einem Datum und einer Uhrzeit?
Andrew Grimm
8
Die akzeptierten und am besten bewerteten Antworten sind unter modernen Versionen von Ruby nicht mehr die genauesten. Siehe die Antworten von @theTinMan und @PatrickMcKenzie unten.
Phrogz

Antworten:

50

Sie benötigen zwei leicht unterschiedliche Konvertierungen.

Um von Time zu konvertieren , können DateTimeSie die Zeitklasse wie folgt ändern:

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

Ähnliche Anpassung Datum lassen Sie konvertieren DateTime zu 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

Beachten Sie, dass Sie zwischen Ortszeit und GM / UTC-Zeit wählen müssen.

Die beiden obigen Codefragmente stammen aus O'Reillys Ruby Cookbook . Ihre Richtlinie zur Wiederverwendung von Code erlaubt dies.

Gordon Wilson
quelle
5
Dies wird bei 1.9 unterbrochen, wobei DateTime # sec_fraction die Anzahl der Millisekunden in einer Sekunde zurückgibt. Für 1.9 möchten Sie verwenden: 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)
anshul
quelle
13
+1 Dies ist möglicherweise nicht die effizienteste Ausführung, aber es funktioniert, ist präzise und sehr gut lesbar.
Walt Jones
6
Leider funktioniert das nur im Umgang mit Ortszeiten wirklich. Wenn Sie mit einer DateTime oder Time mit einer anderen Zeitzone beginnen, wird die Analysefunktion in eine lokale Zeitzone konvertiert. Sie verlieren im Grunde die ursprüngliche Zeitzone.
Bernard
6
Ab Ruby 1.9.1 behält DateTime.parse die Zeitzone bei. (Ich habe keinen Zugriff auf frühere Versionen.) Time.parse behält die Zeitzone nicht bei, da es den POSIX-Standard time_t darstellt, der meiner Meinung nach einen ganzzahligen Unterschied zur Epoche darstellt. Jede Konvertierung in Zeit sollte dasselbe Verhalten haben.
Anshul
1
Du hast recht. DateTime.parse funktioniert in 1.9.1, jedoch nicht in Time.parse. In jedem Fall ist es weniger fehleranfällig (konsistent) und wahrscheinlich schneller, DateTime.new (...) und Time.new (..) zu verwenden. Beispielcode finden Sie in meiner Antwort.
Bernard
1
Hallo @anshul. Ich impliziere nicht, dass ich sage :-). Zeitzoneninformationen werden bei Verwendung von Time.parse () nicht beibehalten. Es ist einfach zu testen. Ersetzen Sie in Ihrem obigen Code einfach d = DateTime.now durch d = DateTime.new (2010,01,01, 10,00,00, Rational (-2, 24)). tt zeigt nun das Datum d an, das in Ihre lokale Zeitzone konvertiert wurde. Sie können weiterhin Datumsarithmetik durchführen und alle bis auf die ursprünglichen tz-Informationen gehen verloren. Diese Informationen sind ein Kontext für das Datum und oft wichtig. Siehe hier: stackoverflow.com/questions/279769/…
Bernard
63

Als Update zum Stand des Ruby - Ökosystems Date, DateTimeund Timehat nun Methoden zu konvertieren zwischen den verschiedenen Klassen. Verwenden von 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
der Blechmann
quelle
1
DateTime.to_time gibt eine DateTime zurück ... 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
Hoppla. Ich habe gerade festgestellt, dass dies ein Ruby on Rails-Problem ist, kein Ruby-Problem: stackoverflow.com/questions/11277454/… . Sie hatten sogar einen Fehler gegen diese Methode in der 2.x-Zeile und markierten sie "wird nicht behoben". Schreckliche Entscheidung IMHO. Das Rails-Verhalten unterbricht die zugrunde liegende Ruby-Schnittstelle vollständig.
Jesse Clark
12

Leider behalten die Funktionen DateTime.to_time, Time.to_datetimeund Time.parsedie Zeitzoneninformationen nicht bei. Während der Konvertierung wird alles in die lokale Zeitzone konvertiert. Datumsarithmetik funktioniert immer noch, aber Sie können die Datumsangaben nicht mit ihren ursprünglichen Zeitzonen anzeigen. Diese Kontextinformationen sind oft wichtig. Wenn ich beispielsweise möchte, dass Transaktionen während der Geschäftszeiten in New York ausgeführt werden, ziehe ich es wahrscheinlich vor, sie in ihren ursprünglichen Zeitzonen anzuzeigen, nicht in meiner lokalen Zeitzone in Australien (die 12 Stunden vor New York liegt).

Die folgenden Konvertierungsmethoden behalten diese tz-Informationen bei.

Schauen Sie sich für Ruby 1.8 die Antwort von Gordon Wilson an . Es ist aus dem guten alten zuverlässigen Ruby Cookbook.

Für Ruby 1.9 ist es etwas einfacher.

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

Dies gibt Folgendes aus

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

Die vollständigen ursprünglichen DateTime-Informationen einschließlich der Zeitzone werden beibehalten.

Bernard
quelle
2
Die Zeit ist kompliziert, aber es gibt keine Entschuldigung dafür, keine integrierte Konvertierung zwischen verschiedenen integrierten Zeitklassen bereitzustellen. Sie können eine RangeException auslösen, wenn Sie versuchen, ein UNIX time_t für 4713 BC abzurufen (obwohl ein negativer BigNum-Wert besser wäre), aber zumindest eine Methode dafür bereitstellen.
Mark Reed
1
Time#to_datetimescheint tz für mich zu bewahren:Time.local(0).to_datetime.zone #=> "-07:00"; Time.gm(0).to_datetime.zone #=> "+00:00"
Phrogz
@Phrogz UTC-Offset ist nicht dasselbe wie eine Zeitzone. Einer ist konstant, der andere kann sich zu verschiedenen Jahreszeiten für die Sommerzeit ändern. DateTime hat keine Zone, ignoriert die Sommerzeit. Die Zeit respektiert es, aber nur in der "lokalen" (Systemumgebung) TZ.
Andrew Vit
1

Hier ist mein Versuch, die Gordon Wilson-Lösung zu verbessern:

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

Sie erhalten die gleiche Zeit in UTC und verlieren die Zeitzone (leider)

Wenn Sie Ruby 1.9 haben, probieren Sie einfach die to_timeMethode aus

Mildred
quelle
0

Bei solchen Konvertierungen sollte das Verhalten von Zeitzonen bei der Konvertierung von einem Objekt zum anderen berücksichtigt werden. Ich habe in diesem Stackoverflow- Beitrag einige gute Notizen und Beispiele gefunden .

Kater
quelle