在Ruby中与日期时间和时间进行相互转换


132

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


1
我不确定这是否是一个单独的问题,但是如何在日期和时间之间转换?
安德鲁·格林

8
在现代版本的Ruby中,公认的且评分最高的答案不再是最准确的答案。请参见下面的@theTinMan@PatrickMcKenzie的答案。
Phrogz'3

Answers:


50

您将需要进行两次略有不同的转换。

要从转换为 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。他们的代码重用策略允许这样做。


5
这将在1.9上中断,其中DateTime#sec_fraction返回一秒的毫秒数。对于1.9,您要使用:usec = dest.sec_fraction * 10 ** 6
dkubb 2011年

185
require 'time'
require 'date'

t = Time.now
d = DateTime.now

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

13
+1这可能不是执行效率最高的方法,但它确实有效,简洁而且可读性强。
Walt Jones

6
不幸的是,这仅在与当地时间打交道时有效。如果您以一个具有不同时区的DateTime或Time开头,则解析函数将转换为本地时区。您基本上会失去原始时区。
伯纳德

6
从ruby 1.9.1开始,DateTime.parse确实保留时区。(我无法访问较早的版本。)Time.parse不会保留时区,因为它表示POSIX标准的time_t,我认为这与纪元是整数差异。转换为时间应具有相同的行为。
anshul 2010年

1
你是对的。DateTime.parse在1.9.1中起作用,但在Time.parse中不起作用。无论如何,使用DateTime.new(...)和Time.new(..)出错的可能性较小(一致),并且可能更快。请参阅我的答案以获取示例代码。
伯纳德

1
嗨@anshul。我并不是说我在说:-)。使用Time.parse()时不会保留时区信息。这很容易测试。在上面的代码中,只需将d = DateTime.now替换为d = DateTime.new(2010,01,01,10,00,00,Rational(-2,24))。tt现在将显示d转换为您当地时区的日期。您仍然可以进行日期算术,除原始tz信息外的所有信息都将丢失。此信息是日期的上下文,通常很重要。看到这里:stackoverflow.com/questions/279769/…–
Bernard

63

作为一个更新的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

1
DateTime.to_time返回一个DateTime ... 1.9.3p327 :007 > ts = '2000-01-01 12:01:01 -0700' => "2000-01-01 12:01:01 -0700" 1.9.3p327 :009 > dt = ts.to_datetime => Sat, 01 Jan 2000 12:01:01 -0700 1.9.3p327 :010 > dt.to_time => Sat, 01 Jan 2000 12:01:01 -0700 1.9.3p327 :011 > dt.to_time.class => DateTime
Jesse Clark

哎呀。刚意识到这是Ruby on Rails问题,而不是Ruby问题:stackoverflow.com/questions/11277454/…。他们甚至在2.x行中针对此方法提交了一个错误,并将其标记为“无法修复”。糟糕的决定恕我直言。Rails行为完全破坏了底层的Ruby接口。
杰西·克拉克

12

不幸的是,DateTime.to_time, Time.to_datetimeand Time.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

完整的原始DateTime信息(包括时区)将保留。


2
时间很复杂,但是没有理由不提供不同内置时间类之间的内置转换。如果尝试获取4713 BC的UNIX time_t,则可以抛出RangeException(尽管BigNum负值会更好),但至少为此提供了一种方法。
马克·里德

1
Time#to_datetime似乎为我保留了tz:Time.local(0).to_datetime.zone #=> "-07:00"; Time.gm(0).to_datetime.zone #=> "+00:00"
Phrogz 2012年

@Phrogz UTC偏移量与时区不同。一个不变,另一个可以在每年的不同时间更改以节省夏令时。DateTime没有区域,它将忽略DST。时间尊重它,但仅在“本地”(系统环境)TZ中。
Andrew Vit

1

我正在尝试改进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获得相同的时间,从而失去了时区

另外,如果您有红宝石1.9,只是尝试的to_time方法


0

在进行此类转换时,在将一个对象转换为另一个对象时,应考虑时区的行为。我在这个stackoverflow 帖子中找到了一些很好的注释和示例。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.