Как преобразовать метку времени Unix (секунды с начала эпохи) в Ruby DateTime?

369

Как конвертировать метку времени Unix (секунды с начала эпохи) в Ruby DateTime?

Tronathan
источник

Ответы:

354

DateTime.strptimeможет справиться с секундами с эпохи. Число должно быть преобразовано в строку:

require 'date'
DateTime.strptime("1318996912",'%s')
steenslag
источник
4
Это не обрабатывает доли секунды
Дэн Сандберг
45
Это действительно обрабатывает миллисекунды с 'тем %Q.
Мини Джон
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)>

Недавнее обновление : я тестировал лучшие решения в этой теме, работая над сервисом высокой доступности неделю или две назад, и был удивлен, обнаружив, что он 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
Это не то же самое, что и в предыдущем примере. 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

Сравнение равенства между двумя возвращаемыми значениями все еще возвращает 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,8 раза быстрее

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>

**** отредактировано, чтобы не быть полностью и полностью неверным во всех отношениях ****

**** добавлены ориентиры ****

WattsInABox
источник
3
Казалось правдоподобным, и я уже проголосовал (не могу отменить сейчас), но при дальнейшей проверке ваше требование в отношении UTC не соответствует действительности. Результирующий объект DateTime / Time будет в UTC по сравнению с локальным, да, но исходная временная метка интерпретируется как UTC в обоих случаях! Таким образом, момент времени равен независимо от метода. Попробуйте Time.at(1318996912) == DateTime.strptime("1318996912",'%s')в не-UTC часовом поясе, и вы увидите!
Eero
4
Извините, но то, что вы исправили, все еще неправильно! :-) Выполните, Time.use_zone "Samoa" do Time.at(1318996912) == DateTime.strptime("1318996912",'%s') endчтобы убедиться, что времена равны, нет локальной метки времени, и в обоих случаях метка времени Unix интерпретируется как UTC. Time.at представляет результирующий объект Time в местном часовом поясе и DateTime.strptime представляет результирующий объект DateTime в UTC, но независимо от представления они равны, поскольку являются эквивалентным моментом времени.
Eero
В то время какTime.at(1318996912) # => 2011-10-19 00:01:52 -0400 утверждение отображает возвращаемое значение в местном часовом поясе, кажется, не является точным ... Можете ли вы проверить? Я полагаю, что ваше утверждение было бы правдой только в том случае, если бы вы использовалиTime.zone.at(1318996912)
BigRon
Да, это кажется точным. Мой локальный компьютер установлен в EST, а время отображается в EST.
WattsInABox
Можете ли вы привести пример, где это не так @BigRon? Какой часовой пояс, версия ruby ​​и т. Д. Не ведут себя таким образом?
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

наконец, strftime используется для форматирования даты

Пример:

    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

ставит («Мне было бы 1 миллиард секунд на:» + время)

sbutterworth
источник
0

Если вы хотели просто дату, вы можете сделать, Date.strptime(invoice.date.to_s, '%s')где invoice.dateприходит в виде Fixnumи затем преобразован в String.

pjammer
источник
3
Time.at(1500923406).to_date.to_s=>"2017-07-24"
Хлоя