Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Graceful date-parsing in Ruby

Tags:

I have two date parameters in a controller action that I would like to fall-back to a default value if they are nil, or parsing fails.

Unfortunately, it seems that DateTime.strptime throws an exception if parsing fails, which forces me to write this monstrosity:

starting = if params[:starting].present?
  begin
    DateTime.strptime(params[:starting], "%Y-%m-%d")
  rescue
    @meeting_range.first
  end
else
  @meeting_range.first
end

Feels bad man. Is there any way to parse a date with the Ruby stdlib that doesn't require a begin...rescue block? Chronic feels like overkill for this situation.

like image 322
Adam Lassek Avatar asked Apr 25 '11 20:04

Adam Lassek


2 Answers

In general, I can't agree with the other solution, using rescue in this way is bad practice. I think it's worth mentioning in case someone else tries to apply the concept to a different implementation.

My concern is that some other exception you might be interested in will be hidden by that rescue, breaking the early error detection rule.

The following is for Date not DateTime but you'll get the idea:

Date.parse(home.build_time) # where build_time does not exist or home is nil
Date.parse(calculated_time) # with any exception in calculated_time

Having to face the same problem I ended up monkey patching Ruby as follows:

# date.rb
class Date
  def self.safe_parse(value, default = nil)
    Date.parse(value.to_s)
  rescue ArgumentError
    default
  end
end

Any exception in value will be rose before entering the method, and only ArgumentError is caught (although I'm not aware of any other possible ones).

The only proper use of inline rescue is something similar to this:

f(x) rescue handle($!)

Update

These days I prefer to not monkey patch Ruby. Instead, I wrap my Date in a Rich module, which I put in lib/rich, I then call it with:

Rich::Date.safe_parse(date)
like image 83
ecoologic Avatar answered Sep 19 '22 13:09

ecoologic


Why not simply:

starting = DateTime.strptime(params[:starting], '%Y-%m-%d') rescue @meeting_range.first
like image 27
Amadan Avatar answered Sep 19 '22 13:09

Amadan