Ruby Benign vale for nil DateTime

914 views Asked by At

When comparing DateTimes, all objects being compared must be the same type. However, I have a data set that has nil dates. I want to treat these dates as older (or perhaps newer) than any other date. Is there a way to construct a benign value that will compare as older (or alternatively, newer) than any other date?

example:

data = [
  { name: :foo, timestamp: make_benign(some_valid_timestamp) },
  { name: :bar, timestamp: make_benign(nil)}
]

data.sort_by{|datum| datum[:timestamp]} #=> [<bar>, <foo>]
data.max_by {|datum| datum[:timestamp]} #=> <foo>
data.min_by {|datum| datum[:timestamp]} #=> <bar>

EDIT: I happen to be stuck on ruby 1.9 for this problem, so solutions for older versions of ruby would be nice. (But newer solutions also are nice for future reference)

1

There are 1 answers

11
Tom Lord On BEST ANSWER

From the docs, the requirement is not that "all objects are the same type". It says:

The other should be a date object or a numeric value as an astronomical Julian day number.

So for a benign value that is guaranteed to be before/after any date, you could use -Float::INFINITY and Float::INFINITY accordingly.

DateTime.now > Float::INFINITY  #=> false
DateTime.now > -Float::INFINITY #=> true

EDIT:

So we need a solution that works in Ruby 1.9 and Rails 3.2.9, huh...

Well the reason the above won't work is because of this monkeypatch in ActiveSupport:

class DateTime
  def <=>(other)
    super other.to_datetime
  end
end

This is particularly problematic. Unfortunately, you may need to just use a "very big/small number" instead...

However, if you're able to upgrade a little bit to Rails 3.2.13 (or apply this updated monkeypatch manually), where the method signature was changed to:

class DateTime
  def <=>(other)
    super other.kind_of?(Infinity) ? other : other.to_datetime
  end
end

...Then you can use Date::Infinity (TIL that's a thing) instead of Float::Infinity, and this "fixed" version of the method now handles it correctly:

DateTime.now > Date::Infinity.new  #=> false
DateTime.now > -Date::Infinity.new #=> true