Ruby中优雅的日期解析



我在控制器动作中有两个日期参数,如果它们为nil或解析失败,我希望将它们还原为默认值。

不幸的是,如果解析失败,DateTime.strptime似乎会抛出一个异常,这迫使我写这个怪物:

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

感觉不好的人。是否有任何方法来解析日期与Ruby标准库,不需要begin...rescue块?在这种情况下,慢性感觉有点过头了。

总的来说,我不同意其他解决方案,以这种方式使用rescue是不好的做法。我认为值得一提的是,以防有人试图将这个概念应用到不同的实现中。

我担心的是,您可能感兴趣的其他一些异常将被rescue隐藏,打破早期错误检测规则。

以下是Date而不是DateTime,但你会明白的:

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

不得不面对同样的问题,我结束了猴子修补Ruby如下:

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

任何异常值将在进入方法之前上升,只有ArgumentError被捕获(尽管我不知道任何其他可能的)。

内联rescue的唯一正确用法类似于:

f(x) rescue handle($!)

这些天我更喜欢而不是 monkey patch Ruby。相反,我把Date包装在Rich模块中,我把它放在lib/rich中,然后用: 调用它:
Rich::Date.safe_parse(date)

为什么不直接:

starting = DateTime.strptime(params[:starting], '%Y-%m-%d') rescue @meeting_range.first

目前我比较喜欢的方法是使用Dry::Types来表示类型强制,使用Dry::Monads来表示错误。

require "dry/types"
require "dry/monads"
Dry::Types.load_extensions(:monads)
Types = Dry::Types(default: :strict)
Types::Date.try("2021-07-27T12:23:19-05:00")
# => Success(Tue, 27 Jul 2021)
Types::Date.try("foo")
# => Failure(ConstraintError: "foo" violates constraints (type?(Date, "foo"))

所有现有的答案都有rescue。然而,我们可以使用一些"丑陋的"。

在Ruby 1.9.3版本中可用的方法(之前就有,但没有官方描述)。

这个方法很难看,因为它以下划线开头。然而,它符合目的。

这样,问题中的方法调用可以写成
starting = if params[:starting].present?
  parsed = DateTime._strptime(params[:starting], "%Y-%m-%d") || {}
  if parsed.count==3 && Date.valid_date?(parsed[:year], parsed[:month], parsed[:mday])
    @meeting_range.first
  end
else
    @meeting_range.first
end
  • 如果日期字符串与输入格式匹配,_strptime将返回包含所有3个日期部分的散列。所以parsed.count==3意味着这三个部分都存在。
  • 然而,进一步检查三个部分在日历中形成有效日期仍然是必要的,因为_strptime不会告诉你它们是无效的。

当你想获得日期作为对象,从字符串变量解析,有时传递的字符串值可能是nil,或空,或无效的日期字符串。我想写安全的方法,简称:

def safe_date(string_date)
  ::Date.parse(string_date)
rescue TypeError, ::Date::Error
  ::Date.today
end

例如-检查irb控制台:

3.0.2 :001 > safe_date
 => #<Date: 2022-08-29 ((2459821j,0s,0n),+0s,2299161j)>
3.0.2 :001 > safe_date('')
 => #<Date: 2022-08-29 ((2459821j,0s,0n),+0s,2299161j)> 
3.0.2 :002 > safe_date('29.12.2022')
 => #<Date: 2022-12-29 ((2459943j,0s,0n),+0s,2299161j)>
3.0.2 :003 > safe_date('29.13.2022')
 => #<Date: 2022-08-29 ((2459821j,0s,0n),+0s,2299161j)>

相关内容

  • 没有找到相关文章

最新更新