Unterschied zwischen DateTime und Time in Ruby

218

Was ist der Unterschied zwischen DateTimeund TimeKlassen in Ruby und welche Faktoren würden mich veranlassen, den einen oder anderen zu wählen?

Tom Lehman
quelle
Die Dokumentation enthält einen Abschnitt , in dem erläutert wird, wann welche verwendet werden sollen.
X-Yuri

Antworten:

177

Neuere Versionen von Ruby (2.0+) weisen keine signifikanten Unterschiede zwischen den beiden Klassen auf. Einige Bibliotheken verwenden die eine oder andere aus historischen Gründen, aber neuer Code muss nicht unbedingt betroffen sein. Aus Gründen der Konsistenz ist es wahrscheinlich am besten, eine auszuwählen. Versuchen Sie also, die Erwartungen Ihrer Bibliotheken zu erfüllen. ActiveRecord bevorzugt beispielsweise DateTime.

In Versionen vor Ruby 1.9 und auf vielen Systemen wird die Zeit als vorzeichenbehafteter 32-Bit-Wert dargestellt, der die Anzahl der Sekunden seit dem 1. Januar 1970 UTC beschreibt, ein dünner Wrapper um einen POSIX-Standardwert time_t, und ist begrenzt:

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

Neuere Versionen von Ruby können größere Werte verarbeiten, ohne Fehler zu verursachen.

DateTime ist ein kalenderbasierter Ansatz, bei dem Jahr, Monat, Tag, Stunde, Minute und Sekunde einzeln gespeichert werden. Dies ist ein Ruby on Rails-Konstrukt, das als Wrapper für SQL-Standard-DATETIME-Felder dient. Diese enthalten beliebige Daten und können nahezu jeden Zeitpunkt darstellen, da der Ausdrucksbereich typischerweise sehr groß ist.

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

Es ist also beruhigend, dass DateTime Blog-Posts von Aristoteles verarbeiten kann.

Bei der Auswahl sind die Unterschiede jetzt etwas subjektiv. In der Vergangenheit hat DateTime bessere Optionen für die kalenderweise Bearbeitung bereitgestellt, aber viele dieser Methoden wurden zumindest in der Rails-Umgebung auch auf Time portiert.

Tadman
quelle
6
Soll ich also immer DateTime verwenden?
Tom Lehman
4
Wenn Sie mit Datumsangaben arbeiten, würde ich DateTime verwenden. Die Zeit ist praktisch, um Dinge wie die aktuelle Tageszeit oder Punkte in naher Zukunft wie 10.minutes.from_now darzustellen. Die beiden haben viel gemeinsam, obwohl DateTime, wie bereits erwähnt, einen viel größeren Wertebereich darstellen kann.
Tadman
3
Ich glaube, das liegt daran, dass Ruby von 32-Bit-Fixnum zu Bignum beliebiger Länge wechselt, wenn ein Überlauf auftritt. Nummern außerhalb dieses Bereichs werden möglicherweise von externen Anwendungen nicht unterstützt. Ja, im Jahr 2038 sind wir im Grunde genommen durcheinander, bis wir uns alle auf ein richtiges 64-Bit-Zeitformat einigen können. Die Jury ist noch nicht besetzt.
Tadman
27
Diese Antwort ist älter als 1.9.2. Ignorieren Sie alles, was über die Posix-Einschränkungen von Time gesagt wird, und treffen Sie Ihre Auswahl basierend auf den APIs von Time und DateTime.
Ben Nagy
8
Diese Antwort ist veraltet, oder? Jetzt wird empfohlen, ActiveSupport :: WithTimeZone von Time oder Rail zu verwenden. Sie müssen DateTime nicht mehr verwenden. Es dient der Abwärtskompatibilität.
Donato
102

[Bearbeiten Juli 2018]

All das gilt weiterhin für Ruby 2.5.1. Aus der Referenzdokumentation :

DateTime berücksichtigt keine Schaltsekunden und verfolgt keine Sommerzeitregeln.

Was in diesem Thread noch nicht erwähnt wurde, ist einer der wenigen Vorteile von DateTime: Es ist sich der Kalenderreformen bewusst, während dies Timenicht der Fall ist:

[…] Rubys Zeitklasse implementiert einen proleptischen Gregorianischen Kalender und hat kein Konzept für eine Kalenderreform […].

Die Referenzdokumentation schließt mit der Empfehlung, diese Timeausschließlich zu verwenden, wenn es sich ausschließlich um vergangene, aktuelle oder zukünftige Daten / Zeiten handelt, und nur dann, DateTimewenn beispielsweise Shakespeares Geburtstag genau konvertiert werden muss: (Hervorhebung hinzugefügt)

Wann sollten Sie DateTime in Ruby verwenden und wann sollten Sie Time verwenden? Mit ziemlicher Sicherheit möchten Sie Time verwenden, da Ihre App wahrscheinlich aktuelle Daten und Zeiten verarbeitet. Wenn Sie sich jedoch mit Datums- und Uhrzeitangaben in einem historischen Kontext befassen müssen, sollten Sie DateTime […] verwenden. Wenn Sie sich auch mit Zeitzonen auseinandersetzen müssen, dann viel Glück - denken Sie daran, dass Sie sich wahrscheinlich mit lokalen Sonnenzeiten befassen werden, da die Einführung der Eisenbahnen erst im 19. Jahrhundert die Notwendigkeit der Standardzeit erforderte und schließlich Zeitzonen.

[/ Edit Juli 2018]

Ab Ruby 2.0 sind die meisten Informationen in den anderen Antworten veraltet.

Insbesondere Timeist jetzt praktisch ungebunden. Es kann mehr oder weniger als 63 Bit von Epoche entfernt sein:

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

Die einzige Konsequenz der Verwendung größerer Werte sollte die Leistung sein, die besser ist, wenn Integers verwendet wird (im Vergleich zu Bignums (Werte außerhalb des IntegerBereichs) oder Rationals (wenn Nanosekunden verfolgt werden)):

Seit Ruby 1.9.2 verwendet die Zeitimplementierung eine vorzeichenbehaftete 63-Bit-Ganzzahl, Bignum oder Rational. Die Ganzzahl ist eine Anzahl von Nanosekunden seit der Epoche, die 1823-11-12 bis 2116-02-20 darstellen können. Wenn Bignum oder Rational verwendet wird (vor 1823, nach 2116, unter Nanosekunden), arbeitet die Zeit langsamer als bei Verwendung einer Ganzzahl. ( http://www.ruby-doc.org/core-2.1.0/Time.html )

Mit anderen Worten, soweit ich weiß, DateTimedeckt es nicht mehr einen größeren Bereich potenzieller Werte ab alsTime .

Darüber hinaus DateTimesollten wahrscheinlich zwei zuvor nicht erwähnte Einschränkungen von beachtet werden:

DateTime berücksichtigt keine Schaltsekunden und verfolgt keine Sommerzeitregeln. ( http://www.ruby-doc.org/stdlib-2.1.0/libdoc/date/rdoc/Date.html#class-Date-label-DateTime )

Erstens DateTimehat kein Konzept von Schaltsekunden:

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_time.to_i
=> 1341100823

Damit das obige Beispiel funktioniert Time, muss das Betriebssystem Schaltsekunden unterstützen und die Zeitzoneninformationen müssen korrekt eingestellt sein, z. B. durch TZ=right/UTC irb(auf vielen Unix-Systemen).

Zweitens DateTimehat das Verständnis der Zeitzonen sehr begrenzt und insbesondere kein Konzept der Sommerzeit . Zeitzonen werden so ziemlich als einfache UTC + X-Offsets behandelt:

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>

Dies kann zu Problemen führen, wenn Zeiten als Sommerzeit eingegeben und dann in eine Nicht-Sommerzeitzone konvertiert werden, ohne die korrekten Offsets außerhalb von sich DateTimeselbst zu verfolgen (viele Betriebssysteme erledigen dies möglicherweise bereits für Sie).

Insgesamt würde ich sagen, dass heutzutage Timedie bessere Wahl für die meisten Anwendungen ist.

Beachten Sie auch einen wichtigen Unterschied beim Hinzufügen: Wenn Sie einem Zeitobjekt eine Zahl hinzufügen, wird diese in Sekunden gezählt, wenn Sie einer DateTime eine Zahl hinzufügen, wird sie in Tagen gezählt.

Niels Ganser
quelle
Gilt das auch 2018 noch?
Qqwy
2
In Ruby 2.5.1 ist dies immer noch der Fall. ruby-doc.org/stdlib-2.5.1/libdoc/date/rdoc/DateTime.html : "DateTime berücksichtigt keine Schaltsekunden und verfolgt keine Sommerzeitregeln." Beachten Sie jedoch, dass DateTime Vorteile bietet, wenn Sie sich mit vorgeregorianischen Kalenderdaten / -zeiten befassen müssen. Dies wurde hier zuvor nicht erwähnt, ist aber in der Referenzdokumentation dokumentiert: "[…] Rubys Zeitklasse implementiert einen proleptischen Gregorianischen Kalender und hat kein Konzept für eine Kalenderreform […]." Ich werde meine Antwort bearbeiten, um weitere Details bereitzustellen.
Niels Ganser
Timehat auch kein Konzept von Schaltsekunden, also unterscheidet es sich nicht von DateTime. Ich weiß nicht, wo Sie Ihre Beispiele ausgeführt haben, aber ich habe es Time.new(2012,6,30,23,59,60,0)in verschiedenen Ruby-Versionen von 2.0 bis 2.7 versucht und immer bekommen 2012-07-01 00:00:00 +0000.
Michau
@michau Ob Schaltsekunden Timeunterstützt werden oder nicht, hängt von Ihrer Betriebssystem- und Zeitzonenkonfiguration ab. Zum Beispiel: TZ=right/UTC ruby -e 'p Time.new(2012,6,30,23,59,60,0)'=> 2012-06-30 23:59:60 +0000während TZ=UTC ruby -e 'p Time.new(2012,6,30,23,59,60,0)'=> 2012-07-01 00:00:00 +0000.
Niels Ganser
@NielsGanser Super, danke! Ich schlug eine Änderung vor, um diesen Punkt zu verdeutlichen.
Michau
44

Ich denke, die Antwort auf "Was ist der Unterschied?" Ist eine der unglücklichen allgemeinen Antworten auf diese Frage in den Ruby-Standardbibliotheken: Die beiden Klassen / Bibliotheken wurden von verschiedenen Personen zu unterschiedlichen Zeiten unterschiedlich erstellt. Dies ist eine der unglücklichen Konsequenzen des Community-Charakters von Rubys Evolution im Vergleich zur sorgfältig geplanten Entwicklung von etwas wie Java. Entwickler möchten neue Funktionen, möchten jedoch nicht auf vorhandene APIs zugreifen, sondern erstellen lediglich eine neue Klasse. Für den Endbenutzer gibt es keinen offensichtlichen Grund für die Existenz der beiden.

Dies gilt für Softwarebibliotheken im Allgemeinen: Oft ist der Grund, warum Code oder API eher historisch als logisch sind.

Die Versuchung besteht darin, mit DateTime zu beginnen, da es allgemeiner erscheint. Datum ... und Uhrzeit, richtig? Falsch. Die Zeit macht Daten auch besser und kann tatsächlich Zeitzonen analysieren, in denen DateTime dies nicht kann. Auch es funktioniert besser.

Ich habe die Zeit überall benutzt.

Aus Sicherheitsgründen erlaube ich jedoch, dass DateTime-Argumente an meine Timey-APIs übergeben und entweder konvertiert werden. Auch wenn ich weiß, dass beide die Methode haben, an der ich interessiert bin, akzeptiere ich beide, wie diese Methode, die ich zum Konvertieren von Zeiten in XML geschrieben habe (für XMLTV-Dateien).

# 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
Rhabarber
quelle
8
Außerdem behandeln Time.new und DateTime.new die Zeitzone unterschiedlich. Ich bin auf GMT + 7, also Time.new(2011, 11, 1, 10, 30)produziert 2011-11-01 10:30:00 +0700während DateTime.new(2011, 11, 1, 10, 30)produziert Tue, 01 Nov 2011 10:30:00 +0000.
Phương Nguyễn
21
Und wie wir alle wissen, führte die sorgfältig geplante Entwicklung von Java zu ausschließlich einfachen, logischen APIs.
pje
@ PhươngNguyễn: Würden Sie das bitte als Antwort hinzufügen, damit ich darüber abstimmen kann? Genau aus diesem Grund habe ich mich für Time anstelle von DateTime entschieden.
Sinnvoll
@Senseful Entschuldigung, ich habe Ihre Nachricht bis jetzt nicht erhalten. Die Leute geben meinem Kommentar bereits einige positive Stimmen, daher finde ich es hier cool.
Phương Nguyễn
@ PhươngNguyễn Hallo, irgendwelche Ideen, wie / warum dieser Unterschied in den Zeitzonen? Woher kommt der Offset?
Joel_Blum
10

Ich habe festgestellt, dass das Parsen und Berechnen des Beginns / Endes eines Tages in verschiedenen Zeitzonen mit DateTime einfacher ist, vorausgesetzt, Sie verwenden die ActiveSupport-Erweiterungen .

In meinem Fall musste ich das Ende des Tages in der Zeitzone eines Benutzers (willkürlich) basierend auf der Ortszeit des Benutzers berechnen, die ich als Zeichenfolge erhalten habe, z. B. "2012-10-10 10:10 +0300".

Mit DateTime ist es so einfach wie

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

Versuchen wir es jetzt genauso mit Time:

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.

Tatsächlich muss die Zeit die Zeitzone kennen, bevor sie analysiert wird (beachten Sie auch Time.zone.parse, dass dies nicht der Fall ist 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

In diesem Fall ist es also definitiv einfacher, mit DateTime zu arbeiten.

Yuriy Kharchenko
quelle
1
Gibt es Nachteile bei der Verwendung der Technik in der Produktion?
Alex Moore-Niemi
1
DateTime berücksichtigt nicht die Sommerzeit. Wenn Sie also Zeit sparen, funktioniert dies nicht richtig. DateTime.parse('2014-03-30 01:00:00 +0100').end_of_dayerzeugt Sun, 30 Mar 2014 23:59:59 +0100, jedoch Time.zone = 'CET'; Time.zone.parse('2014-03-30 01:00:00').end_of_dayerzeugt Sun, 30 Mar 2014 23:59:59 CEST +02:00(CET = + 01: 00, CEST = + 02: 00 - Mitteilung der geänderte Offset). Dazu benötigen Sie jedoch weitere Informationen zur Zeitzone des Benutzers (nicht nur Versatz, sondern auch Zeitersparnis).
Petr Bubák Šedivý
1
Es mag sein , das Offensichtliche, aber Time.zone.parseist sehr nützlich , wenn die Zeiten mit unterschiedlichen Zonen Parsing - es zwingt Sie , um die Zone denken Sie verwenden sollten. Manchmal funktioniert Time.find_zone sogar noch besser.
Prusswan
1
@ Petr''Bubák''Šedivý DateTimehat den von Ihnen angegebenen Offset analysiert, der +0100 betrug. Sie haben keinen TimeVersatz angegeben, aber eine Zeitzone ("CET" beschreibt keinen Versatz, es ist der Name einer Zeitzone). Zeitzonen können das ganze Jahr über unterschiedliche Offsets haben, aber ein Offset ist ein Offset und immer der gleiche.
Mecki
4

Überlegen Sie, wie sie Zeitzonen mit benutzerdefinierten Instanziierungen unterschiedlich behandeln:

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

Dies kann beim Erstellen von Zeitbereichen usw. schwierig sein.

gr8scott06
quelle
Scheint
1

In einigen Fällen scheint das Verhalten sehr unterschiedlich zu sein:

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"

Alexey Strizhak
quelle
Das ist eine interessante Beobachtung, aber es sollte erklärt werden, warum es passiert. Date(nicht überraschend) analysiert nur Daten, und wenn a Datein konvertiert wird Time, wird immer Mitternacht in der lokalen Zeitzone als Zeit verwendet. Der Unterschied zwischen Timeund TimeDateergibt sich aus der Tatsache, dass TimeBST nicht verstanden wird, was überraschend ist, da Zeitzonen normalerweise korrekter behandelt werden Time(z. B. in Bezug auf die Sommerzeit). In diesem Fall wird also nur DateTimedie gesamte Zeichenfolge korrekt analysiert.
Michau
1

Zusätzlich zur Antwort von Niels Ganser könnten Sie dieses Argument berücksichtigen:

Beachten Sie, dass im Ruby Style Guide ganz klar eine Position dazu angegeben ist:

Keine DateTime

Verwenden Sie DateTime nur, wenn Sie die historische Kalenderreform berücksichtigen müssen. Geben Sie in diesem Fall das Startargument explizit an, um Ihre Absichten klar anzugeben.

# bad - uses DateTime for current time
DateTime.now

# good - uses Time for current time
Time.now

# bad - uses DateTime for modern date
DateTime.iso8601('2016-06-29')

# good - uses Date for modern date
Date.iso8601('2016-06-29')

# good - uses DateTime with start argument for historical date
DateTime.iso8601('1751-04-23', Date::ENGLAND)
Paul van Leeuwen
quelle