Як перетворити часову позначку unix (секунди з епохи) в Ruby DateTime?


Відповіді:


354

DateTime.strptimeможе обробляти секунди з епохи. Номер повинен бути перетворений у рядок:

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

4
Це не справляється з дробовими секундами
Ден Сандберг

45
Він обробляє мілісекунди з ' %Qtho.
Міні Джон

3
Продовжити відповідь на відповідь @ TheMiniJohn. Схоже, Timeце потрібно замість цього DateTime. Тож використовуйте, Time.strptime("1318996912345",'%Q').to_fі ви побачите збережені мілісекунди, а DateTime.strptime("1318996912345",'%Q').to_fне збережете їх.
skensell

625

Вибачте, короткий момент помилки синапсу. Ось справжня відповідь.

require 'date'

Time.at(seconds_since_epoch_integer).to_datetime

Короткий приклад (тут враховується поточний часовий пояс системи):

$ 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)> 

Подальше оновлення (для 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)>

Нещодавнє оновлення : під час роботи над сервісом HA, я порівняв основні рішення в цій темі, і здивовано виявив, що це Time.at(..)перевершує DateTime.strptime(..)(оновлення: додано більше орієнтирів).

# ~ % 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>

1
Дякую ... Наступна відповідь є трохи більш короткою, я знайшов Time.at, але намагався знайти еквівалент DateTime.
Тронатан

27
Це смішно, але Time.at (). To_datetime здається приємнішим ніж DateTime.strptime () просто через читабельність ... Принаймні для мене все одно
tybro0103

34
Це не те саме, що вищезгаданий anser, Time.at передбачає поточний часовий пояс, де DateTime.strptime використовує UTC.
Віталій Бабій

5
Це не надто дивно, що Time.atперевершує DateTime.strptime. Останній повинен розбирати рядок, який, як правило, набагато повільніше, ніж безпосередньо приймати число.
Кліп

2
Ваш базовий показник не є точно тестуючим, DateTime.strptimeоскільки він створює дві нові рядки, кожна ітерація, що дуже дорого. Це не просто розбір рядків, як сказав
@claw

67

Обробка часового поясу

Я просто хочу уточнити, хоча це було прокоментовано, щоб майбутні люди не пропустили цієї дуже важливої ​​відзнаки.

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

відображає значення повернення в UTC і вимагає, щоб секунди були рядком і виводили об'єкт часу UTC, тоді як

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

відображає повернене значення у часовому поясі LOCAL, як правило, потрібен аргумент FixNum, але сам об’єкт Time все ще знаходиться в UTC, хоча дисплей це не так.

Отже, хоча я передав однакове ціле число обом методам, я, мабуть, два різні результати через те, як #to_sпрацює метод класу . Однак, як @Eero довелося мені двічі нагадувати про:

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

Порівняння рівності між двома поверненими значеннями все ще повертає істину. Знову ж таки, це тому, що значення в основному однакові (хоча різні класи, #==метод це забезпечує для вас), але #to_sметод друкує різко різні рядки. Хоча, якщо ми подивимось на рядки, ми можемо побачити, що вони справді є одним і тим же часом, просто надрукованими в різних часових поясах.

Роз'яснення аргументу методу

Документи також кажуть "Якщо наведено числовий аргумент, результат знаходиться в локальному часі". що має сенс, але мене трохи бентежить, оскільки вони не наводять жодних прикладів не цілих аргументів у документах. Отже, для деяких нецілих прикладів аргументів:

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

ви не можете використовувати аргумент String, але ви можете використовувати аргумент Time в Time.atі він поверне результат у часовий пояс аргументу:

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

Орієнтири

Після дискусії з @AdamEberlin щодо його відповіді я вирішив опублікувати трохи змінені орієнтири, щоб зробити все максимально рівним. Крім того, мені ніколи не хочеться знову будувати їх, щоб це було гарне місце, як і будь-яке, щоб зберегти їх.

Time.at (int) .to_datetime ~ 2.8x швидше

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>

**** відредаговано так, щоб не бути абсолютно та абсолютно невірним у всіх відношеннях ****

**** додано орієнтири ****


3
Здавалося, правдоподібно, і я вже подав заявку (не можу скасувати відмову зараз), але після подальшої перевірки вашої претензії щодо UTC є неправдивою. Отриманий в результаті об'єкт DateTime / Time буде в UTC та локальному, так, але початкова мітка часу трактується як UTC у обох випадках! Тож момент часу рівний незалежно від методу. Спробуйте Time.at(1318996912) == DateTime.strptime("1318996912",'%s')в часовому поясі без UTC, і ви побачите!
Ееро

4
Вибачте, але те, що ви виправили, все-таки помиляється! :-) Запустіть, Time.use_zone "Samoa" do Time.at(1318996912) == DateTime.strptime("1318996912",'%s') endщоб переконатися, що час однаковий, немає часової позначки LOCAL, і в обох випадках часова мітка Unix трактується як UTC. Time.at представляє отриманий об’єкт Time у локальному часовому поясі та DateTime.strptime представляє отриманий об’єкт DateTime в UTC, але незалежно від презентації вони рівні, оскільки є рівнозначним моментом часу.
Ееро

Заява, тоді як Time.at(1318996912) # => 2011-10-19 00:01:52 -0400відображає значення повернення у часовому поясі LOCAL , не видається точним ... Чи можете ви підтвердити це? Я вважаю, що ваше твердження було б правдивим лише в тому випадку, якщо ви використовувалиTime.zone.at(1318996912)
BigRon

Так, це здається точно. Моя локальна машина встановлена ​​на EST, а час відображається в EST.
WattsInABox

Чи можете ви навести приклад, коли це не так @BigRon? Який часовий пояс, рубінова версія тощо не веде себе таким чином?
WattsInABox

10

Одна команда для перетворення часу дати у формат Unix, а потім у рядок

    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

нарешті строповий текст використовується для форматування дати

Приклад:

    irb(main):034:0> DateTime.strptime("1410321600",'%s').strftime("%d %m %y")
    "10 09 14"

Варто зазначити, що формат епохи не має часового поясу, тому ланцюжок utc перед ланцюжком to_i не потрібна Time.now.utc.to_i.
Тревор Маккасланд

1

Це повідомляє про дату кількості секунд у майбутньому з моменту виконання коду.

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

ставить (час)

відповідно до поточного часу, я відповідаю на питання, яке воно друкує 047-05-14 05:16:16 +0000 (1 мільярд секунд у майбутньому)

або якщо ви хочете відлічити мільярд секунд за певний час, це у форматі Time.mktime(year, month,date,hours,minutes)

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

put ("мені було б 1 мільярд секунд:" + час)


0

Якщо ви хотіли просто побачити, ви можете робити Date.strptime(invoice.date.to_s, '%s')там, де invoice.dateнадходить у формі, Fixnumа потім перетворити на а String.


3
Time.at(1500923406).to_date.to_s=>"2017-07-24"
Хлоя
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.