web-dev-qa-db-fra.com

Comment convertir un horodatage Unix (secondes depuis Epoch) en Ruby DateTime?

Comment convertir un horodatage Unix (secondes depuis Epoch) en Ruby DateTime?

347
Tronathan

DateTime.strptime peut gérer des secondes depuis Epoch. Le nombre doit être converti en chaîne:

require 'date'
DateTime.strptime("1318996912",'%s')
331
steenslag

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

require 'date'

Time.at(seconds_since_Epoch_integer).to_datetime

bref 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 ultérieure (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 de ce fil alors que je travaillais sur un service de haute disponibilité il y a une semaine ou deux. J'ai été surpris de constater que Time.at(..) surpasse DateTime.strptime(..) (mise à jour: ajout de points de référence).

# ~ % 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>
605
Adam Eberlin

Gestion du fuseau horaire

Je tiens simplement à préciser, même si cela a été commenté afin que les futurs citoyens ne passent pas à côté de 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 requiert que les secondes soient une chaîne et génère un objet Heure UTC, 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 au format UTC bien que l'affichage ne le soit pas.

Ainsi, même si j'ai passé le même nombre entier aux deux méthodes, il semble que je dispose de deux résultats différents en raison du fonctionnement de la méthode #to_s 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 renvoyées renvoie toujours la valeur true. Encore une fois, c'est parce que les valeurs sont fondamentalement les mêmes (bien que la classe soit différente, la méthode #== s'en occupe pour vous), mais la méthode #to_s imprime des chaînes radicalement différentes. Bien que, si nous regardons les chaînes, nous pouvons voir qu’elles sont bien à la même heure, elles sont simplement imprimées dans des fuseaux horaires différents.

Clarification de l'argument de la méthode

La documentation indique également "Si un argument numérique est donné, le résultat est à l'heure locale." ce qui a du sens, mais m'a un peu dérouté car ils ne donnent aucun exemple d'argument non entier dans la documentation. Donc, pour certains exemples d'arguments non entiers:

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

vous ne pouvez pas utiliser d'argument String, mais vous pouvez utiliser un argument Time dans Time.at et le résultat sera renvoyé 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

Benchmarks

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

Time.at (int) .to_datetime ~ 2.8x 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>

**** édité pour ne pas être complètement et totalement incorrect à tous égards ****

**** ajout de points de repère ****

61
WattsInABox

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"
8
Tejasvi Manmatha

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

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

met (temps)

en fonction de l'heure actuelle, je réponds à la question imprimée 047-05-14 05:16:16 +0000 (1 milliard de secondes à l'avenir)

ou si vous voulez compter des milliards de secondes à partir d'une heure donnée, il est au format Time.mktime(year, month,date,hours,minutes)

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

met ("je serais vieux de 1 milliard de secondes sur:" + temps)

1
sbutterworth

Si vous vouliez seulement une date, vous pouvez faire Date.strptime(invoice.date.to_s, '%s')invoice.date se présente sous la forme d'unFixnum puis converti en un String.

0
pjammer