2008-11-11 10 views
120

Comment convertir entre un objet DateTime et un objet Heure dans Ruby?Convertir vers/à partir DateTime et heure dans Ruby

+1

Je ne sais pas si cela devrait être une question distincte, mais comment convertir une date et une heure? –

+8

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] (http://stackoverflow.com/a/8511699/405017) et [par @PatrickMcKenzie] (http://stackoverflow.com/a/280026/405017) ci-dessous. – Phrogz

Répondre

44

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

Pour convertir de Time à 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 

ajustements similaires à ce jour vous permettra de convertir DateTime-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 proviennent du Ruby Cookbook d'O'Reilly. Leur réutilisation du code policy le permet.

+5

Cela se casse sur 1.9 où DateTime # sec_fraction renvoie le nombre de millisecondes en une seconde.Pour 1.9, vous voulez utiliser: usec = dest.sec_fraction * 10 ** 6 – dkubb

163
require 'time' 
require 'date' 

t = Time.now 
d = DateTime.now 

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

+1 Cela peut ne pas être le plus efficace dans l'exécution, mais cela fonctionne, c'est concis, et c'est très lisible. –

+6

Malheureusement, cela ne fonctionne vraiment que lorsqu'il s'agit de temps locaux. Si vous commencez avec un DateTime ou un Time avec un fuseau horaire différent, la fonction d'analyse sera convertie en fuseau horaire local. Vous perdez fondamentalement le fuseau horaire d'origine. – Alkaline

+6

À partir de ruby ​​1.9.1, DateTime.parse conserve le fuseau horaire. (Je n'ai pas accès aux versions antérieures.) Time.parse ne conserve pas le fuseau horaire, car il représente le standard POSIX, ce qui, selon moi, est une différence entière par rapport à l'époque. Toute conversion en Time doit avoir le même comportement. – anshul

9

Malheureusement, les DateTime.to_time, Time.to_datetime et Time.parse fonctions ne conservent pas les informations de fuseau horaire. Tout est converti en fuseau horaire local lors de la conversion. Les arithmétiques de date fonctionnent toujours mais vous ne pourrez pas afficher les dates avec leurs fuseaux horaires d'origine. Cette information contextuelle est souvent importante. 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 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, voir Gordon Wilson's answer. C'est à partir du bon vieux Ruby Cookbook fiable.

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 

Cette imprime les éléments suivants

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

Les informations DateTime original complet, y compris fuseau horaire est maintenu.

+4

wow, ça craint .. Pourquoi traite avec le temps si dang dur !! – allyourcode

+1

Le temps est compliqué .... –

+2

Le temps est compliqué, mais il n'y a aucune excuse pour ne pas fournir de conversion intégrée entre différentes classes de temps intégrées. Vous pouvez lancer une exception 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 au moins fournir une méthode pour cela. –

53

En tant que mise à jour de l'état de l'écosystème Ruby, Date, DateTime et Time ont maintenant des méthodes de conversion entre les différentes classes. En utilisant 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

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 => sam., 01 janv. 2000 12:01 : 01 -0700 1.9.3p327: 011> dt.to_time.class => DateTime' –

+0

Oups. Je viens de réaliser qu'il s'agit d'un problème Ruby on Rails pas un problème Ruby: http://stackoverflow.com/questions/11277454/weird-ruby-behavior-in-datetime-to-time-conversion. Ils ont même eu un bug déposé contre cette méthode dans la ligne 2.x et l'ont marqué "ne va pas réparer". Horrible décision à mon humble avis. Le comportement Rails casse complètement l'interface Ruby sous-jacente. –

1

Amélioration de la solution Gordon Wilson, voici mon essai:

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 

Vous obtiendrez le même temps UTC, perdre le fuseau horaire (malheureusement)

Aussi, si vous avez ruby ​​1.9, essayez simplement la méthode to_time

Questions connexes