Comment convertir un horodatage Unix (secondes depuis l'époque) en Ruby DateTime?

369

Comment convertir un horodatage Unix (secondes depuis l'époque) en Ruby DateTime?

Tronathan
la source

Réponses:

354

DateTime.strptimepeut gérer les secondes depuis l'époque. Le nombre doit être converti en chaîne:

require 'date'
DateTime.strptime("1318996912",'%s')
steenslag
la source
4
Cela ne gère pas les fractions de seconde
Dan Sandberg
45
Il gère les millisecondes avec ' %Qtho.
Mini John
3
Pour donner suite à la réponse de @ TheMiniJohn. Il semble Timenécessaire au lieu de DateTime. Alors utilisez Time.strptime("1318996912345",'%Q').to_fet vous verrez les millisecondes préservées, tout en DateTime.strptime("1318996912345",'%Q').to_fne les conservant pas.
skensell
625

Désolé, bref moment d'échec de la synapse. Voici la vraie réponse.

require 'date'

Time.at(seconds_since_epoch_integer).to_datetime

Petit exemple (cela prend en compte le fuseau horaire actuel du système):

$ date +%s
1318996912

$ irb

ruby-1.9.2-p180 :001 > require 'date'
 => true 

ruby-1.9.2-p180 :002 > Time.at(1318996912).to_datetime
 => #<DateTime: 2011-10-18T23:01:52-05:00 (13261609807/5400,-5/24,2299161)> 

Mise à jour supplémentaire (pour UTC):

ruby-1.9.2-p180 :003 > Time.at(1318996912).utc.to_datetime
 => #<DateTime: 2011-10-19T04:01:52+00:00 (13261609807/5400,0/1,2299161)>

Mise à jour récente : j'ai comparé les meilleures solutions dans ce fil tout en travaillant sur un service HA il y a une semaine ou deux, et j'ai été surpris de constater que cela Time.at(..)surpasse DateTime.strptime(..)(mise à jour: ajouté plus de repères).

# ~ % ruby -v
#  => ruby 2.1.5p273 (2014-11-13 revision 48405) [x86_64-darwin13.0]

irb(main):038:0> Benchmark.measure do
irb(main):039:1*   ["1318996912", "1318496912"].each do |s|
irb(main):040:2*     DateTime.strptime(s, '%s')
irb(main):041:2>   end
irb(main):042:1> end

=> #<Benchmark ... @real=2.9e-05 ... @total=0.0>

irb(main):044:0> Benchmark.measure do
irb(main):045:1>   [1318996912, 1318496912].each do |i|
irb(main):046:2>     DateTime.strptime(i.to_s, '%s')
irb(main):047:2>   end
irb(main):048:1> end

=> #<Benchmark ... @real=2.0e-05 ... @total=0.0>

irb(main):050:0* Benchmark.measure do
irb(main):051:1*   ["1318996912", "1318496912"].each do |s|
irb(main):052:2*     Time.at(s.to_i).to_datetime
irb(main):053:2>   end
irb(main):054:1> end

=> #<Benchmark ... @real=1.5e-05 ... @total=0.0>

irb(main):056:0* Benchmark.measure do
irb(main):057:1*   [1318996912, 1318496912].each do |i|
irb(main):058:2*     Time.at(i).to_datetime
irb(main):059:2>   end
irb(main):060:1> end

=> #<Benchmark ... @real=2.0e-05 ... @total=0.0>
Adam Eberlin
la source
1
Merci ... La réponse suivante est un peu plus succincte, j'ai trouvé Time.at mais j'essayais de trouver un équivalent DateTime.
Tronathan
27
C'est drôle mais Time.at (). To_datetime semble plus agréable que DateTime.strptime () simplement à cause de la lisibilité ...
Du
34
Ce n'est pas la même chose que l'anser ci-dessus, Time.at suppose le fuseau horaire actuel, où DateTime.strptime utilise UTC.
Vitaly Babiy
5
Il n'est pas trop surprenant que cela Time.atsurpasse DateTime.strptime. Ce dernier doit analyser une chaîne, ce qui est généralement beaucoup plus lent que la saisie directe d'un nombre.
Claw
2
Votre benchmark n'est pas exactement un test DateTime.strptimecar il crée deux nouvelles chaînes à chaque itération, ce qui est très cher. Ce n'est pas seulement l'analyse de chaînes comme @claw l'a dit
WattsInABox
67

Gestion des fuseaux horaires

Je veux juste clarifier, même si cela a été commenté afin que les futurs gens ne manquent pas cette distinction très importante.

DateTime.strptime("1318996912",'%s') # => Wed, 19 Oct 2011 04:01:52 +0000

affiche une valeur de retour en UTC et nécessite que les secondes soient une chaîne et génère un objet UTC Time, tandis que

Time.at(1318996912) # => 2011-10-19 00:01:52 -0400

affiche une valeur de retour dans le fuseau horaire LOCAL, nécessite normalement un argument FixNum, mais l'objet Time lui-même est toujours en UTC même si l'affichage ne l'est pas.

Donc, même si j'ai transmis le même entier aux deux méthodes, j'ai apparemment deux résultats différents en raison du fonctionnement de la #to_sméthode de la classe . Cependant, comme @Eero a dû me rappeler deux fois:

Time.at(1318996912) == DateTime.strptime("1318996912",'%s') # => true

Une comparaison d'égalité entre les deux valeurs de retour renvoie toujours true. Encore une fois, cela est dû au fait que les valeurs sont fondamentalement les mêmes (bien que différentes classes, la #==méthode s'en occupe pour vous), mais la #to_sméthode imprime des chaînes radicalement différentes. Bien que, si nous regardons les chaînes, nous pouvons voir qu'elles sont en effet la même heure, juste imprimées dans des fuseaux horaires différents.

Clarification de l'argument de la méthode

Les documents disent également "Si un argument numérique est donné, le résultat est en heure locale." ce qui est logique, mais était un peu déroutant pour moi car ils ne donnent aucun exemple d'arguments non entiers dans les documents. Ainsi, pour certains exemples d'arguments non entiers:

Time.at("1318996912")
TypeError: can't convert String into an exact number

vous ne pouvez pas utiliser un argument String, mais vous pouvez utiliser un argument Time dans Time.atet il renverra le résultat dans le fuseau horaire de l'argument:

Time.at(Time.new(2007,11,1,15,25,0, "+09:00"))
=> 2007-11-01 15:25:00 +0900

Repères

Après une discussion avec @AdamEberlin sur sa réponse, j'ai décidé de publier des repères légèrement modifiés pour que tout soit le plus égal possible. De plus, je ne veux plus jamais avoir à les reconstruire, c'est donc un endroit aussi bon que n'importe lequel pour les sauver.

Time.at (int) .to_datetime ~ 2,8 fois plus rapide

09:10:58-watsw018:~$ ruby -v
ruby 2.3.7p456 (2018-03-28 revision 63024) [universal.x86_64-darwin18]
09:11:00-watsw018:~$ irb
irb(main):001:0> require 'benchmark'
=> true
irb(main):002:0> require 'date'
=> true
irb(main):003:0>
irb(main):004:0* format = '%s'
=> "%s"
irb(main):005:0> times = ['1318996912', '1318496913']
=> ["1318996912", "1318496913"]
irb(main):006:0> int_times = times.map(&:to_i)
=> [1318996912, 1318496913]
irb(main):007:0>
irb(main):008:0* datetime_from_strptime = DateTime.strptime(times.first, format)
=> #<DateTime: 2011-10-19T04:01:52+00:00 ((2455854j,14512s,0n),+0s,2299161j)>
irb(main):009:0> datetime_from_time = Time.at(int_times.first).to_datetime
=> #<DateTime: 2011-10-19T00:01:52-04:00 ((2455854j,14512s,0n),-14400s,2299161j)>
irb(main):010:0>
irb(main):011:0* datetime_from_strptime === datetime_from_time
=> true
irb(main):012:0>
irb(main):013:0* Benchmark.measure do
irb(main):014:1*   100_000.times {
irb(main):015:2*     times.each do |i|
irb(main):016:3*       DateTime.strptime(i, format)
irb(main):017:3>     end
irb(main):018:2>   }
irb(main):019:1> end
=> #<Benchmark::Tms:0x00007fbdc18f0d28 @label="", @real=0.8680500000045868, @cstime=0.0, @cutime=0.0, @stime=0.009999999999999998, @utime=0.86, @total=0.87>
irb(main):020:0>
irb(main):021:0* Benchmark.measure do
irb(main):022:1*   100_000.times {
irb(main):023:2*     int_times.each do |i|
irb(main):024:3*       Time.at(i).to_datetime
irb(main):025:3>     end
irb(main):026:2>   }
irb(main):027:1> end
=> #<Benchmark::Tms:0x00007fbdc3108be0 @label="", @real=0.33059399999910966, @cstime=0.0, @cutime=0.0, @stime=0.0, @utime=0.32000000000000006, @total=0.32000000000000006>

**** modifié pour ne pas être complètement et totalement incorrect dans tous les sens ****

**** repères ajoutés ****

WattsInABox
la source
3
Semblait plausible, et j'ai déjà voté (je ne peux pas annuler maintenant), mais après vérification de votre réclamation concernant l'UTC, c'est faux. L'objet DateTime / Time résultant sera en UTC vs local, oui, mais l'horodatage d'origine est interprété comme étant en UTC dans les deux cas! Donc, le moment dans le temps est égal quelle que soit la méthode. Essayez Time.at(1318996912) == DateTime.strptime("1318996912",'%s')dans un fuseau horaire non UTC et vous verrez!
Eero
4
Je suis désolé, mais ce que vous avez corrigé est toujours faux! :-) Exécutez Time.use_zone "Samoa" do Time.at(1318996912) == DateTime.strptime("1318996912",'%s') endpour vérifier que les heures sont égales, il n'y a pas d'horodatage LOCAL et dans les deux cas, l'horodatage Unix est interprété comme étant en UTC. Time.at présente l'objet Time résultant dans le fuseau horaire local et DateTime.strptime présente l'objet DateTime résultant en UTC, mais quelle que soit la présentation, ils sont égaux, car ils sont le moment équivalent dans le temps.
Eero
L'instruction alors qu'elle Time.at(1318996912) # => 2011-10-19 00:01:52 -0400affiche une valeur de retour dans le fuseau horaire LOCAL ne semble pas exacte ... Pouvez-vous vérifier? Je crois que votre déclaration ne serait vraie que si vous Time.zone.at(1318996912)
utilisiez
Oui, cela semble exact. Mon ordinateur local est réglé sur EST et les heures s'affichent dans EST.
WattsInABox
Pouvez-vous donner un exemple où ce n'est pas le cas @BigRon? Quel fuseau horaire, version rubis, etc. ne se comporte pas de cette façon?
WattsInABox
10

Une commande pour convertir la date et l'heure au format Unix, puis en chaîne

    DateTime.strptime(Time.now.utc.to_i.to_s,'%s').strftime("%d %m %y")

    Time.now.utc.to_i #Converts time from Unix format
    DateTime.strptime(Time.now.utc.to_i.to_s,'%s') #Converts date and time from unix format to DateTime

enfin strftime est utilisé pour formater la date

Exemple:

    irb(main):034:0> DateTime.strptime("1410321600",'%s').strftime("%d %m %y")
    "10 09 14"
Tejasvi Manmatha
la source
Une chose à noter est que le format d'époque n'a pas de fuseau horaire, donc le chaînage de utc avant de chaîner to_i n'est pas nécessaire dans Time.now.utc.to_i.
Trevor McCasland
1

Cela vous indique la date du nombre de secondes à partir du moment où vous exécutez le code.

time = Time.new + 1000000000 #date in 1 billion seconds

met (temps)

selon l'heure actuelle, je réponds à la question qu'il imprime 047-05-14 05:16:16 +0000(1 milliard de secondes à l'avenir)

ou si vous voulez compter des milliards de secondes à partir d'un moment particulier, c'est au format Time.mktime(year, month,date,hours,minutes)

time = Time.mktime(1987,8,18,6,45) + 1000000000

met ("je serais âgé de 1 milliard de secondes sur:" + temps)

sbutterworth
la source
0

Si vous vouliez juste une date, vous pouvez faire Date.strptime(invoice.date.to_s, '%s')invoice.datevient sous la forme d'un Fixnumpuis converti en un String.

pjammer
la source
3
Time.at(1500923406).to_date.to_s=>"2017-07-24"
Chloé