Was ist der Unterschied zwischen DateTime
und Time
Klassen in Ruby und welche Faktoren würden mich veranlassen, den einen oder anderen zu wählen?
Was ist der Unterschied zwischen DateTime
und Time
Klassen in Ruby und welche Faktoren würden mich veranlassen, den einen oder anderen zu wählen?
Antworten:
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.
[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 Time
nicht 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 Time
ausschließlich zu verwenden, wenn es sich ausschließlich um vergangene, aktuelle oder zukünftige Daten / Zeiten handelt, und nur dann, DateTime
wenn 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 Time
ist 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 Integer
s verwendet wird (im Vergleich zu Bignum
s (Werte außerhalb des Integer
Bereichs) oder Rational
s (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ß, DateTime
deckt es nicht mehr einen größeren Bereich potenzieller Werte ab alsTime
.
Darüber hinaus DateTime
sollten 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 DateTime
hat 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 DateTime
hat 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 DateTime
selbst zu verfolgen (viele Betriebssysteme erledigen dies möglicherweise bereits für Sie).
Insgesamt würde ich sagen, dass heutzutage Time
die 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.
Time
hat 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
.
Time
unterstü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 +0000
während TZ=UTC ruby -e 'p Time.new(2012,6,30,23,59,60,0)'
=> 2012-07-01 00:00:00 +0000
.
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
Time.new(2011, 11, 1, 10, 30)
produziert 2011-11-01 10:30:00 +0700
während DateTime.new(2011, 11, 1, 10, 30)
produziert Tue, 01 Nov 2011 10:30:00 +0000
.
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.
DateTime.parse('2014-03-30 01:00:00 +0100').end_of_day
erzeugt Sun, 30 Mar 2014 23:59:59 +0100
, jedoch Time.zone = 'CET'; Time.zone.parse('2014-03-30 01:00:00').end_of_day
erzeugt 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).
Time.zone.parse
ist 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.
DateTime
hat den von Ihnen angegebenen Offset analysiert, der +0100 betrug. Sie haben keinen Time
Versatz 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.
Ü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.
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"
Date
(nicht überraschend) analysiert nur Daten, und wenn a Date
in konvertiert wird Time
, wird immer Mitternacht in der lokalen Zeitzone als Zeit verwendet. Der Unterschied zwischen Time
und TimeDate
ergibt sich aus der Tatsache, dass Time
BST 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 DateTime
die gesamte Zeichenfolge korrekt analysiert.
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)