回答:
Rubyの新しいバージョン(2.0以降)では、2つのクラスに大きな違いはありません。一部のライブラリは歴史的な理由でどちらか一方を使用しますが、新しいコードは必ずしも考慮する必要はありません。一貫性を保つために1つを選択するのがおそらく最善です。ライブラリが期待することを試してみてください。たとえば、ActiveRecordはDateTimeを優先します。
Ruby 1.9より前のバージョンと多くのシステムでは、Timeは1970年1月1日からの秒数を表す32ビットの符号付き値として表され、POSIX標準time_t
値の薄いラッパーであり、制限されています。
Time.at(0x7FFFFFFF)
# => Mon Jan 18 22:14:07 -0500 2038
Time.at(-0x7FFFFFFF)
# => Fri Dec 13 15:45:53 -0500 1901
Rubyの新しいバージョンでは、エラーを発生させることなく、より大きな値を処理できます。
DateTimeは、年、月、日、時、分、秒が個別に保存されるカレンダーベースのアプローチです。これは、SQL標準のDATETIMEフィールドのラッパーとして機能するRuby on Railsコンストラクトです。これらは任意の日付を含み、式の範囲は通常非常に大きいため、ほぼすべての時点を表すことができます。
DateTime.new
# => Mon, 01 Jan -4712 00:00:00 +0000
つまり、DateTimeがアリストテレスからのブログ投稿を処理できることは安心です。
いずれかを選択すると、違いはやや主観的になります。歴史的に、DateTimeはカレンダー形式で操作するためのより良いオプションを提供してきましたが、これらのメソッドの多くは、少なくともRails環境内でもTimeに移植されています。
[2018年7月を編集]
以下のすべてがRuby 2.5.1でも当てはまります。参照ドキュメントから:
DateTimeはうるう秒を考慮せず、夏時間のルールを追跡しません。
何このスレッドに記載されていないと、前のいくつかの利点の一つであるDateTime
:一方で、それはカレンダーの改革を認識しているTime
ではありません。
[…] RubyのTimeクラスは、前向きなグレゴリオ暦を実装し、カレンダーの改革という概念はありません[…]。
参照ドキュメントは、Time
過去、現在、または将来の日付/時刻のみを処理するDateTime
場合に使用し、たとえばシェイクスピアの誕生日を正確に変換する必要がある場合にのみ使用することを推奨しています:(強調を追加)
では、RubyでいつDateTimeを使用し、いつTimeを使用する必要があるのでしょうか。アプリはおそらく現在の日付と時刻を扱っているため、ほぼ間違いなくTimeを使用する必要があります。ただし、履歴のコンテキストで日付と時刻を処理する必要がある場合は、DateTime […] を使用する必要があります。タイムゾーンも処理する必要がある場合は、幸運を祈る-鉄道の導入により標準時の必要性が必要となったのは19世紀まででなかったため、おそらく現地の太陽時を扱うことになることを覚えておいてください。そして最終的にはタイムゾーン。
[/ 2018年7月を編集]
Ruby 2.0以降、他の回答のほとんどの情報は古くなっています。
特に、Time
今は実質的に拘束力がない。エポックから63ビットも離れていても、離れていてもかまいません。
irb(main):001:0> RUBY_VERSION
=> "2.0.0"
irb(main):002:0> Time.at(2**62-1).utc # within Integer range
=> 146138514283-06-19 07:44:38 UTC
irb(main):003:0> Time.at(2**128).utc # outside of Integer range
=> 10783118943836478994022445751222-08-06 08:03:51 UTC
irb(main):004:0> Time.at(-2**128).utc # outside of Integer range
=> -10783118943836478994022445747283-05-28 15:55:44 UTC
より大きな値を使用した場合の唯一の結果はパフォーマンスであるはずです。これは、Integer
sを使用した場合(v Bignum
s(Integer
範囲外の値)またはRational
s(ナノ秒を追跡した場合))の方が優れています。
Ruby 1.9.2以降、Timeの実装では、符号付き63ビット整数、BignumまたはRationalを使用しています。整数は、エポック以降のナノ秒数で、1823-11-12〜2116-02-20を表すことができます。BignumまたはRationalが使用されている場合(1823より前、2116より後、ナノ秒未満)、整数が使用されている場合よりも時間がかかります。(http://www.ruby-doc.org/core-2.1.0/Time.html)
言い換えれば、私が理解している限りでDateTime
は、は潜在的な値のより広い範囲をもはやカバーしていませんTime
。
さらに、前述の2つの制限にDateTime
おそらく注意する必要があります。
DateTimeはうるう秒を考慮せず、夏時間のルールを追跡しません。(http://www.ruby-doc.org/stdlib-2.1.0/libdoc/date/rdoc/Date.html#class-Date-label-DateTime)
まず、DateTime
うるう秒の概念はありません。
irb(main):001:0> RUBY_VERSION
=> "2.0.0"
irb(main):002:0> require "date"
=> true
irb(main):003:0> t = Time.new(2012,6,30,23,59,60,0)
=> 2012-06-30 23:59:60 +0000
irb(main):004:0> dt = t.to_datetime; dt.to_s
=> "2012-06-30T23:59:59+00:00"
irb(main):005:0> t == dt.to_time
=> false
irb(main):006:0> t.to_i
=> 1341100824
irb(main):007:0> dt.to_time.to_i
=> 1341100823
上記の例がで動作するTime
ためには、OSがうるう秒をサポートし、タイムゾーン情報がTZ=right/UTC irb
(たとえば多くのUnixシステムで)正しく設定されている必要があります。
第二に、DateTime
タイムゾーンの理解が非常に限られており、特に夏時間の概念がありません。タイムゾーンを単純なUTC + Xオフセットとしてほとんど処理します。
irb(main):001:0> RUBY_VERSION
=> "2.0.0"
irb(main):002:0> require "date"
=> true
irb(main):003:0> t = Time.local(2012,7,1)
=> 2012-07-01 00:00:00 +0200
irb(main):004:0> t.zone
=> "CEST"
irb(main):005:0> t.dst?
=> true
irb(main):006:0> dt = t.to_datetime; dt.to_s
=> "2012-07-01T00:00:00+02:00"
irb(main):007:0> dt.zone
=> "+02:00"
irb(main):008:0> dt.dst?
NoMethodError: undefined method `dst?' for #<DateTime:0x007f34ea6c3cb8>
これにより、時刻がDSTとして入力され、DateTime
それ自体の外部で正しいオフセットを追跡せずにDST以外のタイムゾーンに変換されると、問題が発生する可能性があります(多くのオペレーティングシステムが実際に既にこの処理を行っている場合があります)。
全体として、今日でTime
はほとんどのアプリケーションに適しています。
また、加算に関する重要な違いにも注意してください。Timeオブジェクトに数値を追加すると、秒単位でカウントされますが、DateTimeに数値を追加すると、日単位でカウントされます。
Time
うるう秒の概念もないため、と違いはありませんDateTime
。あなたがあなたの例をどこで実行したかはわかりませんが、私はTime.new(2012,6,30,23,59,60,0)
2.0から2.7までのさまざまなRubyバージョンで試しましたが、常にを手に入れました2012-07-01 00:00:00 +0000
。
Time
うるう秒をサポートするかどうかは、OSとタイムゾーンの構成によって異なります。例:TZ=right/UTC ruby -e 'p Time.new(2012,6,30,23,59,60,0)'
=> 2012-06-30 23:59:60 +0000
一方TZ=UTC ruby -e 'p Time.new(2012,6,30,23,59,60,0)'
=> 2012-07-01 00:00:00 +0000
。
「違いは何ですか」への答えは、Ruby標準ライブラリにおけるこの質問への不幸な一般的な答えの1つだと思います。2つのクラス/ライブラリは、異なる人々によって異なる時期に異なる方法で作成されました。これは、Javaのようなものの慎重に計画された開発と比較した、Rubyの進化のコミュニティの性質の不幸な結果の1つです。開発者は新しい機能を望んでいますが、既存のAPIを踏襲したくないので、新しいクラスを作成するだけです。エンドユーザーにとって、2つが存在する明確な理由はありません。
これは、ソフトウェアライブラリ全般に当てはまります。多くの場合、一部のコードまたはAPIが論理的ではなく歴史的であることが判明するのはそのためです。
より一般的であるように思われるので、誘惑はDateTimeから始めることです。日付と時間ですよね?違う。Timeは日付も適切に処理し、実際にはDateTimeができないタイムゾーンを解析できます。また、パフォーマンスも向上します。
どこでもTimeを使用してしまいました。
ただし、念のため、DateTime引数をTimey APIに渡して変換できるようにする傾向があります。また、両方に興味のあるメソッドがあることがわかっている場合は、どちらかを受け入れます。このメソッドのように、時間をXMLに変換するために記述しました(XMLTVファイルの場合)
# Will take a date time as a string or as a Time or DateTime object and
# format it appropriately for xmtlv.
# For example, the 22nd of August, 2006 at 20 past midnight in the British Summertime
# timezone (i.e. GMT plus one hour for DST) gives: "20060822002000 +0100"
def self.format_date_time(date_time)
if (date_time.respond_to?(:rfc822)) then
return format_time(date_time)
else
time = Time.parse(date_time.to_s)
return format_time(time)
end
end
# Note must use a Time, not a String, nor a DateTime, nor Date.
# see format_date_time for the more general version
def self.format_time(time)
# The timezone feature of DateTime doesn't work with parsed times for some reason
# and the timezone of Time is verbose like "GMT Daylight Saving Time", so the only
# way I've discovered of getting the timezone in the form "+0100" is to use
# Time.rfc822 and look at the last five chars
return "#{time.strftime( '%Y%m%d%H%M%S' )} #{time.rfc822[-5..-1]}"
end
Time.new(2011, 11, 1, 10, 30)
プロデュースし2011-11-01 10:30:00 +0700
ながらDateTime.new(2011, 11, 1, 10, 30)
プロデュースしTue, 01 Nov 2011 10:30:00 +0000
ます。
ActiveSupport拡張機能を使用している場合、異なるタイムゾーンでの1日の開始/終了の解析や計算などがDateTimeで行う方が簡単であることがわかりました。
私の場合、文字列として受信したユーザーのローカル時間に基づいて、ユーザーのタイムゾーン(任意)で1日の終わりを計算する必要がありました。たとえば、 "2012-10-10 10:10 +0300"
DateTimeを使用すると、次のように簡単です
irb(main):034:0> DateTime.parse('2012-10-10 10:10 +0300').end_of_day
=> Wed, 10 Oct 2012 23:59:59 +0300
# it preserved the timezone +0300
次に、Timeを使用して同じ方法で試してみましょう。
irb(main):035:0> Time.parse('2012-10-10 10:10 +0300').end_of_day
=> 2012-10-10 23:59:59 +0000
# the timezone got changed to the server's default UTC (+0000),
# which is not what we want to see here.
実際、Timeは解析前にタイムゾーンを知る必要があります(またTime.zone.parse
、ではなくであることに注意してくださいTime.parse
)。
irb(main):044:0> Time.zone = 'EET'
=> "EET"
irb(main):045:0> Time.zone.parse('2012-10-10 10:10 +0300').end_of_day
=> Wed, 10 Oct 2012 23:59:59 EEST +03:00
したがって、この場合は、DateTimeを使用する方が間違いなく簡単です。
DateTime.parse('2014-03-30 01:00:00 +0100').end_of_day
は生成されますSun, 30 Mar 2014 23:59:59 +0100
が、Time.zone = 'CET'; Time.zone.parse('2014-03-30 01:00:00').end_of_day
生成されますSun, 30 Mar 2014 23:59:59 CEST +02:00
(CET = + 01:00、CEST = + 02:00-オフセットが変更されていることに注意してください)。ただし、これを行うには、ユーザーのタイムゾーンに関する詳細情報(オフセットだけでなく、時間の節約が使用されているかどうかも必要)が必要です。
Time.zone.parse
ある非常に異なるゾーンで時間を解析するときに便利-それはあなたが使用する必要のある区域について考えるようにあなたを強制します。Time.find_zoneの方が優れている場合があります。
DateTime
は、指定したオフセット(+0100)を解析しました。Time
オフセットを指定しませんでしたが、タイムゾーン( "CET"はオフセットを記述せず、タイムゾーンの名前です)。タイムゾーンは1年を通して異なるオフセットを持つことができますが、オフセットはオフセットであり、常に同じです。
カスタムのインスタンス化でタイムゾーンの扱い方を検討してください。
irb(main):001:0> Time.new(2016,9,1)
=> 2016-09-01 00:00:00 -0400
irb(main):002:0> DateTime.new(2016,9,1)
=> Thu, 01 Sep 2016 00:00:00 +0000
irb(main):003:0> Time.new(2016,9,1).to_i
=> 1472702400
irb(main):004:0> DateTime.new(2016,9,1).to_i
=> 1472688000
これは、時間範囲などを作成するときに注意が必要です。
場合によっては動作が大きく異なるようです:
Time.parse("Ends from 28 Jun 2018 12:00 BST").utc.to_s
「2018-06-28 09:00:00 UTC」
Date.parse("Ends from 28 Jun 2018 12:00 BST").to_time.utc.to_s
「2018-06-27 21:00:00 UTC」
DateTime.parse("Ends from 28 Jun 2018 12:00 BST").to_time.utc.to_s
「2018-06-28 11:00:00 UTC」
Date
(当然のことながら)は日付のみを解析し、a Date
がに変換されるときはTime
、常にローカルタイムゾーンの午前0時を時刻として使用します。との違いはTime
、BSTを理解しないTimeDate
という事実に由来しますTime
。これは、タイムゾーンがTime
(たとえば、夏時間に関して)通常より正確に処理されることを考えると、驚くべきことです。したがって、この場合、DateTime
文字列全体を正しく解析するだけです。
Niels Ganserの回答に加えて、次の議論を検討してください。
ことに注意してくださいルビースタイルガイドは、非常に明確にこの上の位置を述べています:
日時なし
過去のカレンダーの改革を説明する必要がない限り、DateTimeを使用しないでください。その場合は、開始引数を明示的に指定して、意図を明確に示してください。
# bad - uses DateTime for current time DateTime.now # good - uses Time for current time Time.now # bad - uses DateTime for modern date DateTime.iso8601('2016-06-29') # good - uses Date for modern date Date.iso8601('2016-06-29') # good - uses DateTime with start argument for historical date DateTime.iso8601('1751-04-23', Date::ENGLAND)