Unterschiedliche Zeitübergänge

Die maximale Genauigkeit von Zeiten in Ruby liegt im Nanosekundenbereich. Also 0,000.000.001 Sekunde. Das trifft für Time genauso zu, wie für DateTime.
Allerdings hat Ruby on Rails ein unterschiedliches Verständnis vom Endes des Tages im Nanosekundenbereich.

Zeiten in Rails

Ruby on Rails hat die Zeit Klassen um nützliche Funktionalitäten erweitert. So zum Beispiel end_of_day:

DateTime.now.end_of_day
# => Sun, 20 Nov 2016 23:59:59 +0100
Time.now.end_of_day
# => 2016-11-20 23:59:59 +0100

Achtung! Immer, wenn die Zeitzone relevant ist, sollte Time.current verwendet werden.

Das Problem in Rails

Da Time genauso wie DateTime im Nanosekundenbereich liegen:

DateTime.now.nsec
# => 249095319
Time.now.nsec
# => 483323251

sei angenommen, dass das Endes eines Tages für Objekte beider Klassen gleich sei:

RSpec.describe '#end_of_day' do
  it 'returns the same milliseconds as DateTime' do
    expect(Time.now.end_of_day.nsec).to eq(DateTime.now.end_of_day.nsec)
  end
end

Das ist allerdings nicht der Fall:

1) #end_of_day returns always same milliseconds
       expected: 0
            got: 999999999
1 example, 1 failure

Das Problem: das Ende eines Tages ist für Time also genau 999999999 Nanosekunden später, als es für DateTime ist.

Die Implementierung

Dieser Unterschied scheint etwas unerwartet, ist aber tatsächlich so gewollt. Die Implementierung an beiden Klassen:

# File activesupport/lib/active_support/core_ext/time/calculations.rb, line 173
def end_of_day
  change(
    :hour => 23,
    :min => 59,
    :sec => 59,
    :usec => Rational(999999999, 1000)
  )
end

# File activesupport/lib/active_support/core_ext/date_time/calculations.rb, line 113
def end_of_day
  change(:hour => 23, :min => 59, :sec => 59)
end

Daher ist es ratsam, mit Time zu arbeiten, wenn es um das Ende eines Tages geht. Denn der Zeitraum zwischen 23:59:59 und 24:00:00 ist laut DateTime undefniert: er ist weder vor Ende des Tages noch nach Beginn eines Tages:

(DateTime.tomorrow.beginning_of_day - 0.1.second) < DateTime.now.end_of_day
# => false

Das trifft auf alle Zeitübergänge zu. Also sämtliche Tagesenden (#end_of_month und #end_of_year), aber auch #end_of_hour und #end_of_minute.

Update - 05.03.2017:

Das Rails Core Team hat die Inkonsistenz behoben. Der PR #28242 wurde gemergt. Vorraussichtlich ab Version 5.1 wird DateTime ebenfalls die usec für alle end_of Methoden setzen.
YEEE!