141 votes

Convertir en/depuis DateTime et Heure en Ruby

Comment convertir un objet DateTime en un objet Time en Ruby ?

1 votes

Je ne sais pas si cela doit faire l'objet d'une question distincte, mais comment convertir une date en heure ?

10 votes

Les réponses acceptées et les mieux notées ne sont plus les plus précises sous les versions modernes de Ruby. Voir les réponses par @theTinMan y par @PatrickMcKenzie ci-dessous.

194voto

anshul Points 2964
require 'time'
require 'date'

t = Time.now
d = DateTime.now

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

13 votes

+1 Ce n'est peut-être pas le plus efficace en termes d'exécution, mais ça marche, c'est concis et c'est très lisible.

6 votes

Malheureusement, cela ne fonctionne vraiment que lorsqu'il s'agit d'heures locales. Si vous commencez par une DateTime ou une Time avec un fuseau horaire différent, la fonction d'analyse convertira le fuseau horaire local. En fait, vous perdez le fuseau horaire d'origine.

6 votes

Depuis la version 1.9.1 de ruby, DateTime.parse préserve le fuseau horaire. (Je n'ai pas accès aux versions antérieures.) Time.parse ne préserve pas le fuseau horaire, car il représente le standard POSIX time_t, qui, je crois, est une différence entière de l'époque. Toute conversion en Time devrait avoir le même comportement.

68voto

the Tin Man Points 69148

Comme une mise à jour de l'état de l'écosystème Ruby, Date , DateTime y Time ont maintenant des méthodes pour convertir entre les différentes classes. Utilisation de 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 votes

DateTime.to_time renvoie 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

0 votes

Oups. Je viens de réaliser que c'est un problème de Ruby on Rails, pas un problème de Ruby : stackoverflow.com/questions/11277454/ . Ils ont même déposé un bogue contre cette méthode dans la ligne 2.x et l'ont marqué "ne sera pas corrigé". Une décision horrible, selon moi. Le comportement de Rails brise totalement l'interface Ruby sous-jacente.

53voto

Gordon Wilson Points 14721

Vous aurez besoin de deux conversions légèrement différentes.

Pour convertir de Time a DateTime vous pouvez modifier la classe de temps comme suit :

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

Des ajustements similaires à Date vous permettront de convertir DateTime a 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

Notez que vous devez choisir entre l'heure locale et l'heure GM/UTC.

Les deux extraits de code ci-dessus sont tirés de l'ouvrage O'Reilly's Livre de recettes Ruby . Leur réutilisation du code politique permet ça.

5 votes

Cela ne fonctionnera pas avec la version 1.9 où DateTime#sec_fraction renvoie le nombre de millisecondes dans une seconde. Pour la version 1.9, vous devez utiliser : usec = dest.sec_fraction * 10**6

20voto

Patrick McKenzie Points 3158

Ce n'est pas vraiment si difficile.

require 'date'

date_time = DateTime.now
# #<DateTime: blah>
date_time.to_time
# #<Time: blah>

time = Time.now
# #<Time: blah>
time.to_datetime
# #<DateTime: blah>

Après vous require "date" vous obtenez toutes sortes de nouvelles méthodes sur Time .

12voto

Alkaline Points 1625

Malheureusement, le DateTime.to_time, Time.to_datetime y Time.parse Les fonctions ne conservent pas les informations relatives au fuseau horaire. Tout est converti en fuseau horaire local lors de la conversion. L'arithmétique des dates fonctionne toujours, mais vous ne pourrez pas afficher les dates avec leur fuseau horaire d'origine. Ces informations contextuelles sont souvent importantes. Par exemple, si je veux voir les transactions effectuées pendant les heures de bureau à New York, je préfère probablement les voir affichées dans leur fuseau horaire d'origine, et non dans mon fuseau horaire local en Australie (qui a 12 heures d'avance sur New York).

Les méthodes de conversion ci-dessous conservent cette information tz.

Pour Ruby 1.8, regardez Réponse de Gordon Wilson . C'est tiré du bon vieux livre de recettes Ruby.

Pour Ruby 1.9, c'est un peu plus facile.

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

Le résultat est le suivant

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

Les informations complètes de l'heure de la date d'origine, y compris le fuseau horaire, sont conservées.

2 votes

Le temps est compliqué, mais il n'y a aucune excuse pour ne pas fournir une conversion intégrée entre les différentes classes de temps intégrées. Vous pouvez lancer une RangeException si vous essayez d'obtenir un time_t UNIX pour 4713 BC (bien qu'une valeur négative BigNum serait plus agréable), mais fournissez au moins une méthode pour cela.

1 votes

Time#to_datetime semble préserver tz pour moi : Time.local(0).to_datetime.zone #=> "-07:00"; Time.gm(0).to_datetime.zone #=> "+00:00"

0 votes

@Phrogz Le décalage UTC n'est pas la même chose que le fuseau horaire. L'un est constant, l'autre peut changer à différentes périodes de l'année pour l'heure d'été. DateTime n'a pas de zone, il ignore l'heure d'été. Time le respecte, mais uniquement dans la ZT "locale" (environnement du système).

Prograide.com

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.

Powered by:

X