Comment convertir un horodatage Unix (secondes depuis Epoch) en Ruby DateTime?
DateTime.strptime
peut gérer des secondes depuis Epoch. Le nombre doit être converti en chaîne:
require 'date'
DateTime.strptime("1318996912",'%s')
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>
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 ****
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"
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)
Si vous vouliez seulement une date, vous pouvez faire Date.strptime(invoice.date.to_s, '%s')
où invoice.date
se présente sous la forme d'unFixnum
puis converti en un String
.