在 Ruby 中转换日期时间和时间

在 Ruby 中如何在 DateTime 和 Time 对象之间进行转换?

165282 次浏览

你需要两个稍微不同的转换。

要从 Time 转换到 DateTime,你可以修改时间类如下:

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

类似的调整日期将让您转换 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 烹饪书,它们的代码重用 政策允许这样做。

require 'time'
require 'date'


t = Time.now
d = DateTime.now


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

遗憾的是,DateTime.to_time, Time.to_datetimeTime.parse函数不保留时区信息。在转换期间,所有内容都转换为本地时区。日期算法仍然可以工作,但是您将无法显示具有原始时区的日期。上下文信息通常很重要。例如,如果我想看到在纽约的营业时间内执行的交易,我可能更愿意看到它们显示在原来的时区,而不是我在澳大利亚的本地时区(比纽约提前12小时)。

下面的转换方法确实保留了 tz 信息。

对于 Ruby 1.8,看看 Gordon Wilson 的回答,它来自可靠的 Ruby Cookbook。

对于 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

完整的原始日期时间信息,包括时区保留。

作为对 Ruby 生态系统状态的更新,DateDateTimeTime现在有了在不同类之间进行转换的方法。使用 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

改进 Gordon Wilson 的解决方案,以下是我的尝试:

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方法

在进行这种转换时,应该考虑从一个对象转换到另一个对象时时区的行为。我在这个堆栈溢出的 邮寄中发现了一些很好的注释和例子。

你可以使用 to_date,例如。

> Event.last.starts_at
=> Wed, 13 Jan 2021 16:49:36.292979000 CET +01:00
> Event.last.starts_at.to_date
=> Wed, 13 Jan 2021