Преобразование в/из DateTime и Time в Ruby

Как вы конвертируете между объектом DateTime и Time в Ruby?

Ответ 1

Вам понадобится две несколько разных конверсии.

Чтобы преобразовать из Time в DateTime, вы можете изменить класс Time следующим образом:

require 'date'
class Time
  def to_datetime
    # Convert seconds + microseconds into a fractional number of seconds
    seconds = sec + Rational(usec, 10**6)

    # Convert a UTC offset measured in minutes to one measured in a
    # fraction of a day.
    offset = Rational(utc_offset, 60 * 60 * 24)
    DateTime.new(year, month, day, hour, min, seconds, offset)
  end
end

Аналогичные настройки Date позволят вам преобразовать DateTime в Time .

class Date
  def to_gm_time
    to_time(new_offset, :gm)
  end

  def to_local_time
    to_time(new_offset(DateTime.now.offset-offset), :local)
  end

  private
  def to_time(dest, method)
    #Convert a fraction of a day to a number of microseconds
    usec = (dest.sec_fraction * 60 * 60 * 24 * (10**6)).to_i
    Time.send(method, dest.year, dest.month, dest.day, dest.hour, dest.min,
              dest.sec, usec)
  end
end

Обратите внимание, что вам нужно выбирать между местным временем и временем GM/UTC.

Оба приведенных выше фрагмента кода взяты из O'Reilly Ruby Cookbook. Их повторное использование кода policy позволяет это.

Ответ 2

require 'time'
require 'date'

t = Time.now
d = DateTime.now

dd = DateTime.parse(t.to_s)
tt = Time.parse(d.to_s)

Ответ 3

Как обновление состояния экосистемы Ruby, Date, DateTime и Time теперь имеют методы для преобразования между различными классами. Использование Ruby 1.9.2 +:

pry
[1] pry(main)> ts = 'Jan 1, 2000 12:01:01'
=> "Jan 1, 2000 12:01:01"
[2] pry(main)> require 'time'
=> true
[3] pry(main)> require 'date'
=> true
[4] pry(main)> ds = Date.parse(ts)
=> #<Date: 2000-01-01 (4903089/2,0,2299161)>
[5] pry(main)> ds.to_date
=> #<Date: 2000-01-01 (4903089/2,0,2299161)>
[6] pry(main)> ds.to_datetime
=> #<DateTime: 2000-01-01T00:00:00+00:00 (4903089/2,0,2299161)>
[7] pry(main)> ds.to_time
=> 2000-01-01 00:00:00 -0700
[8] pry(main)> ds.to_time.class
=> Time
[9] pry(main)> ds.to_datetime.class
=> DateTime
[10] pry(main)> ts = Time.parse(ts)
=> 2000-01-01 12:01:01 -0700
[11] pry(main)> ts.class
=> Time
[12] pry(main)> ts.to_date
=> #<Date: 2000-01-01 (4903089/2,0,2299161)>
[13] pry(main)> ts.to_date.class
=> Date
[14] pry(main)> ts.to_datetime
=> #<DateTime: 2000-01-01T12:01:01-07:00 (211813513261/86400,-7/24,2299161)>
[15] pry(main)> ts.to_datetime.class
=> DateTime

Ответ 4

К сожалению, функции DateTime.to_time, Time.to_datetime и Time.parse не сохраняют информацию о часовом поясе. Во время преобразования все преобразуется в локальный часовой пояс. Арифметика даты по-прежнему работает, но вы не сможете отображать даты со своими исходными часовыми поясами. Эта контекстная информация часто важна. Например, если я хочу видеть транзакции, совершаемые в рабочее время в Нью-Йорке, я, вероятно, предпочитаю видеть их в своих оригинальных часовых поясах, а не в моем местном часовом поясе в Австралии (что на 12 часов раньше Нью-Йорка).

Ниже приведены методы преобразования, которые хранят эту информацию.

Для Ruby 1.8, посмотрите ответ Гордона Уилсона. Это от старой доброй надежной рубиновой кулинарной книги.

Для Ruby 1.9 это немного проще.

require 'date'

# Create a date in some foreign time zone (middle of the Atlantic)
d = DateTime.new(2010,01,01, 10,00,00, Rational(-2, 24))
puts d

# Convert DateTime to Time, keeping the original timezone
t = Time.new(d.year, d.month, d.day, d.hour, d.min, d.sec, d.zone)
puts t

# Convert Time to DateTime, keeping the original timezone
d = DateTime.new(t.year, t.month, t.day, t.hour, t.min, t.sec, Rational(t.gmt_offset / 3600, 24))
puts d

Отпечатано следующее

2010-01-01T10:00:00-02:00
2010-01-01 10:00:00 -0200
2010-01-01T10:00:00-02:00

Полная исходная информация DateTime, включая часовой пояс, сохраняется.

Ответ 5

Улучшение решения Гордона Уилсона, вот моя попытка:

def to_time
  #Convert a fraction of a day to a number of microseconds
  usec = (sec_fraction * 60 * 60 * 24 * (10**6)).to_i
  t = Time.gm(year, month, day, hour, min, sec, usec)
  t - offset.abs.div(SECONDS_IN_DAY)
end

Вы получите то же время в UTC, потеряв часовой пояс (к сожалению)

Кроме того, если у вас есть ruby ​​1.9, просто попробуйте to_time метод