Quelle est la différence entre DateTime
y Time
en Ruby et quels facteurs me feraient choisir l'un ou l'autre ?
Dois-je donc toujours utiliser DateTime ?
Quelle est la différence entre DateTime
y Time
en Ruby et quels facteurs me feraient choisir l'un ou l'autre ?
Les versions plus récentes de Ruby (2.0+) ne présentent pas vraiment de différences significatives entre les deux classes. Certaines bibliothèques utiliseront l'une ou l'autre pour des raisons historiques, mais le nouveau code ne doit pas nécessairement s'en préoccuper. Il est probablement préférable d'en choisir une pour des raisons de cohérence, alors essayez de vous adapter aux attentes de vos bibliothèques. Par exemple, ActiveRecord préfère DateTime.
Dans les versions antérieures à Ruby 1.9 et sur de nombreux systèmes, le temps est représenté sous la forme d'une valeur signée de 32 bits décrivant le nombre de secondes écoulées depuis le 1er janvier 1970 UTC, une enveloppe fine autour d'un standard POSIX time_t
et est borné :
Time.at(0x7FFFFFFF)
# => Mon Jan 18 22:14:07 -0500 2038
Time.at(-0x7FFFFFFF)
# => Fri Dec 13 15:45:53 -0500 1901
Les versions plus récentes de Ruby sont capables de gérer des valeurs plus importantes sans produire d'erreurs.
DateTime est une approche basée sur le calendrier où l'année, le mois, le jour, l'heure, la minute et la seconde sont stockés individuellement. Il s'agit d'une construction Ruby on Rails qui sert d'enveloppe aux champs DATETIME standard de SQL. Ceux-ci contiennent des dates arbitraires et peuvent représenter presque n'importe quel moment dans le temps, car la plage d'expression est généralement très large.
DateTime.new
# => Mon, 01 Jan -4712 00:00:00 +0000
Il est donc rassurant de savoir que DateTime peut gérer les articles de blog d'Aristote.
Au moment d'en choisir un, les différences sont désormais quelque peu subjectives. Historiquement, DateTime offre de meilleures options pour la manipulation des données sous forme de calendrier, mais nombre de ces méthodes ont également été transférées à Time, du moins dans l'environnement Rails.
Si vous travaillez avec des dates, je dirais qu'il faut utiliser DateTime. Time est pratique pour représenter des choses comme l'heure actuelle de la journée, ou des points dans un futur proche comme 10.minutes.from_now. Les deux ont beaucoup en commun, bien que, comme indiqué, DateTime puisse représenter un éventail de valeurs beaucoup plus large.
> Le temps est pratique pour représenter des choses telles que l'heure actuelle de la journée, ou des points dans un futur proche tels que 10.minutes.from_now. Pourquoi Time est-il plus pratique que DateTime à cette fin ?
Comme de ruby 2.0, la plupart des renseignements ci-dessus est hors de date.
En particulier, Time
est maintenant pratiquement indépendant. Elle peut être plus ou moins de même 63 bits loin de l'Époque:
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
La seule conséquence de l'utilisation de plus grandes valeurs de performance, ce qui est mieux quand Integer
s sont utilisés (contre Bignum
s (valeurs en dehors de l' Integer
de la gamme) ou Rational
s (lorsque nanosecondes sont suivis)):
Depuis Ruby 1.9.2, le Temps de la mise en œuvre utilise un signé 63 bits entier, Bignum ou Rationnelle. Le nombre entier est un nombre de nanosecondes depuis l'Époque ce qui peut représenter 1823-11-12 à 2116-02-20. Lorsque Bignum ou Rationnel est utilisé (avant 1823, après 2116, en vertu de l'ordre de la nanoseconde), le Temps plus lentement que lors de l'entier est utilisé. (http://www.ruby-doc.org/core-2.1.0/Time.html)
En d'autres termes, comme je le comprends, DateTime
plus couvre un plus large éventail de valeurs possibles que Time
.
En outre, deux précédemment sous silence les restrictions d' DateTime
devrait probablement être noté:
DateTime ne considère aucun leapseconds, ne pas suivre tout l'été règles de temps. (http://www.ruby-doc.org/stdlib-2.1.0/libdoc/date/rdoc/Date.html#class-Date-label-DateTime)
Tout d'abord, DateTime
n'a pas de notion de secondes intercalaires:
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_i
=> 1341100823
Deuxièmement, DateTime
a une compréhension très limitée des zones de temps et, en particulier, n'a aucune notion de l'heure d'été. Il gère les fuseaux horaires comme simple UTC + X décalages:
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>
Cela peut causer des problèmes lorsque les temps sont entrés comme de l'heure d'été, puis converti en une non-de l'heure d'été de fuseau horaire, sans garder une trace de l'corriger les décalages à l'extérieur de l' DateTime
lui-même (de nombreux systèmes d'exploitation peuvent déjà prendre soin de cela pour vous).
Dans l'ensemble, je dirais que de nos jours, Time
est le meilleur choix pour la plupart des applications.
C'est toujours vrai dans Ruby 2.5.1. ruby-doc.org/stdlib-2.5.1/libdoc/date/rdoc/DateTime.html : "DateTime ne prend en compte aucune seconde intercalaire, ne suit aucune règle d'heure d'été". Cependant, notez que DateTime a des avantages lorsque vous devez traiter des dates/heures du calendrier pré-grégorien. Cela n'a pas été mentionné ici auparavant mais est documenté dans la documentation de référence : "[ ] la classe Time de Ruby met en œuvre un calendrier grégorien proleptique et n'a aucun concept de réforme du calendrier [ ]." Je vais éditer ma réponse pour fournir quelques détails supplémentaires.
Time
n'a pas non plus de concept de secondes intercalaires, il n'est donc pas différent de DateTime
. Je ne sais pas où vous avez exécuté vos exemples, mais j'ai essayé Time.new(2012,6,30,23,59,60,0)
dans différentes versions de Ruby, de la 2.0 à la 2.7, et j'ai toujours obtenu 2012-07-01 00:00:00 +0000
.
La différence de performance ne peut être suffisamment soulignée... Time est en C, et DateTime est en Ruby :
>> Benchmark.bm do |bm|
?> bm.report('DateTime:') do
?> n1 = DateTime.now
>> n2 = DateTime.now
>> 1_000_000.times{ n1 < n2 }
>> end
>> bm.report('Time: ') do
?> n1 = Time.now
>> n2 = Time.now
>> 1_000_000.times{ n1 < n2 }
>> end
>> end
user system total real
DateTime: 4.980000 0.020000 5.000000 ( 5.063963)
Time: 0.330000 0.000000 0.330000 ( 0.335913)
Mise à jour (2/2012) :
Comme déjà mentionné dans le commentaire, la 1.9.3 a grandement amélioré DateTime
performance :
user system total real
DateTime: 0.330000 0.000000 0.330000 ( 0.333869)
Time: 0.300000 0.000000 0.300000 ( 0.306444)
Je pense que la réponse à la question " quelle est la différence ? " est l'une des réponses malheureusement courantes à cette question dans les bibliothèques standard de Ruby : les deux classes/libs ont été créées différemment par différentes personnes à différents moments. C'est l'une des conséquences malheureuses de la nature communautaire de l'évolution de Ruby par rapport au développement soigneusement planifié de quelque chose comme Java. Les développeurs veulent de nouvelles fonctionnalités mais ne veulent pas empiéter sur les API existantes, alors ils créent simplement une nouvelle classe - pour l'utilisateur final, il n'y a pas de raison évidente pour que les deux classes existent.
Cela vaut pour les bibliothèques de logiciels en général : souvent, la raison pour laquelle un code ou une API est tel qu'il est s'avère être historique plutôt que logique.
La tentation est de commencer par DateTime parce qu'il semble plus générique. Date... et Heure, c'est ça ? Non, c'est faux. Time fait également mieux les dates, et en fait peut analyser les fuseaux horaires là où DateTime ne le peut pas. Il est également plus performant.
J'ai fini par utiliser Time partout.
Par précaution, j'ai tendance à autoriser le passage d'arguments DateTime dans mes API temporelles et à les convertir. De même, si je sais que les deux ont la méthode qui m'intéresse, j'accepte l'une ou l'autre, comme cette méthode que j'ai écrite pour convertir les heures en XML (pour les fichiers XMLTV).
# 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
En outre, Time.new et DateTime.new traitent le fuseau horaire différemment. Je suis en GMT+7, donc Time.new(2011, 11, 1, 10, 30)
produit 2011-11-01 10:30:00 +0700
tandis que DateTime.new(2011, 11, 1, 10, 30)
produit Tue, 01 Nov 2011 10:30:00 +0000
.
Et comme nous le savons tous, le développement soigneusement planifié de Java a abouti à des API exclusivement simples et logiques.
@PhuongNguyen : Pourriez-vous s'il vous plaît ajouter cela comme une réponse afin que je puisse l'up-voter ? C'est précisément la raison pour laquelle j'ai décidé de choisir Time plutôt que DateTime.
J'ai trouvé que des choses comme l'analyse et le calcul du début/de la fin d'un jour dans différents fuseaux horaires sont plus faciles à faire avec DateTime, en supposant que vous utilisez les extensions ActiveSupport .
Dans mon cas, j'avais besoin de calculer la fin de la journée dans le fuseau horaire (arbitraire) d'un utilisateur en fonction de l'heure locale de l'utilisateur que j'ai reçue sous forme de chaîne, par exemple "2012-10-10 10:10 +0300".
Avec DateTime, c'est aussi simple que
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
Essayons maintenant de procéder de la même manière avec le temps :
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.
En fait, Time a besoin de connaître le fuseau horaire avant d'effectuer le parsing (notez aussi que c'est Time.zone.parse
pas 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
Dans ce cas, il est donc plus facile d'opter pour DateTime.
Vous utilisez cette technique en production maintenant, y a-t-il des inconvénients à cette technique ?
DateTime ne tient pas compte de l'heure d'été. Ainsi, au changement d'heure d'été, il ne fonctionnera pas correctement. DateTime.parse('2014-03-30 01:00:00 +0100').end_of_day
produit Sun, 30 Mar 2014 23:59:59 +0100
mais Time.zone = 'CET'; Time.zone.parse('2014-03-30 01:00:00').end_of_day
produit Sun, 30 Mar 2014 23:59:59 CEST +02:00
(CET=+01:00, CEST=+02:00 - remarquez que le décalage a changé). Mais pour ce faire, vous avez besoin de plus d'informations sur le fuseau horaire de l'utilisateur (pas seulement le décalage mais aussi l'utilisation ou non de l'heure d'été).
C'est peut-être une évidence, mais Time.zone.parse
es très utile lors de l'analyse d'heures avec différentes zones - cela vous oblige à réfléchir à la zone que vous devriez utiliser. Parfois, Time.find_zone fonctionne encore mieux.
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.
0 votes
La docs a une section en expliquant quand et comment les utiliser.