RubyでDateTimeとTimeに/から変換


132

RubyでDateTimeオブジェクトとTimeオブジェクトをどのように変換しますか?


1
これが別の質問であるかどうかはわかりませんが、日付と時刻をどのように変換しますか?
Andrew Grimm

8
受け入れられた最高の評価の回答は、Rubyの最新バージョンではもはや最も正確ではありません。以下の@theTinMan@PatrickMcKenzie による回答を参照してください。
Phrogz、2012年

回答:


50

わずかに異なる2つの変換が必要です。

変換するの 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 Cookbookから取得されました。彼らのコード再利用ポリシーはこれを許可します。


5
これは、DateTime#sec_fractionが1秒間のミリ秒数を返す1.9で中断します。1.9では、次のように使用します:usec = dest.sec_fraction * 10 ** 6
dkubb

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が異なるタイムゾーンで開始した場合、解析関数はローカルタイムゾーンに変換されます。基本的に、元のタイムゾーンを失います。
Bernard、

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/...
バーナード・

63

Rubyの生態系の状態に更新されるように、DateDateTime及び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

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
ジェシークラーク

おっとっと。これはRuby on Railsの問題であり、Rubyの問題ではないことに気づきました:stackoverflow.com/questions/11277454/…。彼らは、2.x行でこのメソッドに対してバグを提出し、「修正しない」とマークしていました。恐ろしい決定私見。Railsの動作は、基礎となるRubyインターフェースを完全に破壊します。
ジェシークラーク

12

残念ながら、DateTime.to_time, Time.to_datetimeおよびTime.parse関数はタイムゾーン情報を保持しません。変換中にすべてがローカルタイムゾーンに変換されます。日付の計算は引き続き機能しますが、元のタイムゾーンで日付を表示することはできません。そのコンテキスト情報はしばしば重要です。たとえば、ニューヨークの営業時間中に実行されたトランザクションを表示したい場合は、オーストラリアのローカルタイムゾーン(ニューヨークより12時間進んでいる)ではなく、元のタイムゾーンで表示するほうがよいでしょう。

以下の変換メソッドは、そのtz情報を保持します。

Ruby 1.8については、Gordon Wilsonの回答をご覧ください。古き良き信頼性の高いRubyクックブックからです。

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年のUNIX time_tを取得しようとすると(BigNumの負の値の方が適切ですが)、RangeExceptionをスローできますが、少なくともそのためのメソッドを提供します。
Mark Reed、

1
Time#to_datetime私にとってtzを保持するように見える:Time.local(0).to_datetime.zone #=> "-07:00"; Time.gm(0).to_datetime.zone #=> "+00:00"
Phrogz

@Phrogz UTCオフセットはタイムゾーンと同じではありません。1つは一定で、もう1つは夏時間のために1年の異なる時間に変更できます。DateTimeにはゾーンがなく、DSTは無視されます。時間はそれを尊重しますが、「ローカル」(システム環境)TZでのみです。
Andrew Vit 2013

1

ゴードンウィルソンソリューションを改善するために、これが私の試みです。

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メソッドを試してください


0

このような変換を行うときは、1つのオブジェクトから別のオブジェクトに変換するときのタイムゾーンの動作を考慮する必要があります。このStackoverflowの投稿で、良いメモと例を見つけました。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.