web-dev-qa-db-ja.com

RubyのDateTimeとTimeの違い

Ruby=)のDateTimeクラスとTimeクラスの違いは何ですか。また、どの要因によってどちらが選択されるのですか?

215
Tom Lehman

Ruby(2.0+))の新しいバージョンは、2つのクラスの間に大きな違いはありません。一部のライブラリは、歴史的な理由でどちらか一方を使用しますが、新しいコードは必ずしも考慮する必要はありません一貫性のために1つを選択するのがおそらく最善であるため、ライブラリが期待するものを試してみてください。

Ruby 1.9より前のバージョンおよび多くのシステムでは、時刻は、UTC 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は、年、月、日、時間、分、秒が個別に保存されるカレンダーベースのアプローチです。これはRuby on Rails SQL標準のDATETIMEフィールドのラッパーとして機能するコンストラクトです。これらは任意の日付を含み、ほぼすべての時点を表すことができます。通常、表現の範囲は非常に広いです。

DateTime.new
# => Mon, 01 Jan -4712 00:00:00 +0000

そのため、DateTimeがアリストテレスからのブログ投稿を処理できることを安心させています。

どちらかを選択する場合、違いは今やや主観的です。歴史的には、DateTimeはカレンダー形式で操作するためのより良いオプションを提供していましたが、これらのメソッドの多くは少なくともRails環境内でTimeに移植されました。

173
tadman

[2018年7月編集]

以下のすべては、Ruby 2.5.1。 参照ドキュメント から:

DateTimeはうるう秒を考慮せず、夏時間規則を追跡しません。

このスレッドでこれまで注目されていなかったのは、DateTimeの数少ない利点の1つです。Timeはそうではありませんが、カレンダーの改革を認識しています。

[…] RubyのTimeクラスは、予言的なグレゴリオ暦を実装しており、カレンダー改革の概念はありません[…]。

参照文書は、近過去、現在、または将来の日付/時刻のみを扱う場合はTimeを使用し、たとえばシェークスピアの誕生日を正確に変換する必要がある場合にのみDateTimeを使用することを推奨して終了します:(強調追加)

だから、いつRubyでDateTimeを使うべきか、そして、いつTimeを使うべきか?あなたのアプリはおそらく現在の日付と時刻ただし、履歴コンテキストで日付と時刻を処理する必要がある場合は、DateTime[…]。タイムゾーンも処理する必要がある場合は、幸運を祈ります-おそらく、ローカルの太陽の時間を処理することに留意してください。鉄道は標準時間と最終的にはタイムゾーンの必要性を必要としました。

[/ 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

大きな値を使用することの唯一の結果はパフォーマンスであり、Integersを使用する場合(vs. Bignums(Integer範囲外の値)またはRationals(ナノ秒の場合)追跡されます)):

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

つまり、私が理解している限り、DateTimeTimeよりも広い範囲の潜在的な値をカバーしていません。

さらに、以前に言及されていないDateTimeの2つの制限に注意する必要があります。

DateTimeはle秒を考慮せず、夏時間規則を追跡しません。 ( 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に数値を追加すると、日単位でカウントされます。

95
Niels Ganser

時代遅れ!下記参照...

パフォーマンスの違いを十分に強調することはできません...時間はCで、DateTimeはRubyです。

>> Benchmark.bm do |bm|
?>   bm.report('DateTime:') do
?>     n1 = DateTime.now
>>     n2 = DateTime.now
>>     1_000_000.times{ n1 < n2 }
>>   end
>>   bm.report('Time:    ') do
?>     n1 = Time.now
>>     n2 = Time.now
>>     1_000_000.times{ n1 < n2 }
>>   end
>> end
      user     system      total        real
DateTime:  4.980000   0.020000   5.000000 (  5.063963)
Time:      0.330000   0.000000   0.330000 (  0.335913)

更新(2012年2月):

コメントですでに述べたように、1.9.3はDateTimeのパフォーマンスを大幅に改善しました。

       user     system      total        real
DateTime:  0.330000   0.000000   0.330000 (  0.333869)
Time:      0.300000   0.000000   0.300000 (  0.306444)
66

「違いは何ですか」への答えは、Ruby標準ライブラリ:この2つのクラス/ライブラリは、異なる時間に異なる人々によって異なる方法で作成されました。 Javaのような慎重に計画された開発と比較して、Rubyの進化のコミュニティの性質の不幸な結果の1つです。両者が存在する明白な理由はありません。

これは一般にソフトウェアライブラリに当てはまります。多くの場合、何らかのコードまたはAPIが論理的というよりも歴史的であることが判明した理由です。

誘惑はDateTimeから始めることです。なぜなら、それはより一般的だからです。日付...と時間ですか?違う。時間も日付の方が優れており、実際にはDateTimeができないタイムゾーンを解析できます。また、パフォーマンスが向上します。

私はどこでもTimeを使用することになりました。

安全のために、TimeTime APIにDateTime引数を渡して、変換することを許可する傾向があります。また、両方が私が興味を持っているメソッドを持っていることを知っている場合、私は時間を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
43
Rhubarb

DateTimeを使用すると、さまざまなタイムゾーンでの1日の開始/終了の解析や計算などの処理が簡単になりますActiveSupport拡張機能を使用している場合

私の場合、文字列として受け取ったユーザーの現地時間に基づいて、ユーザーのタイムゾーン(任意)で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.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を使用する方が間違いなく簡単です。

10

カスタムインスタンスを使用してタイムゾーンを異なる方法で処理する方法を検討してください。

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

これは、時間範囲などを作成するときに注意が必要です。

4
gr8scott06

場合によっては、動作が非常に異なるようです。

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」

1
Alexey Strizhak