Ruby on Rails中如何验证从PESEL提取的日期有效性
处理PESEL提取出生日期时的无效日期错误
问题背景
我在模型中实现了一个before_save方法,用于从PESEL号码中解析出生日期,但偶尔会遇到ArgumentError: invalid date or strptime format的错误,比如解析12/14/2022时失败。我尝试用valid?和valid_date?验证生成的日期,但完全没用——因为错误在调用strptime时就直接抛出了,根本到不了验证那一步。
另外,我没办法直接对PESEL号码做完整校验,只能在提取日期的环节处理合法性问题。PESEL的月份规则是:
- 19XX年:月份1-12
- 20XX年:月份21-32(实际月份=数值-20)
- 21XX年:月份41-52(实际月份=数值-40)
- 22XX年:月份61-72(实际月份=数值-60)
我的原始代码如下:
def set_birth_date return unless pesel_changed? day = pesel[4..5] case pesel[2..3].to_i when 0..19 month = pesel[2..3] year = pesel[0..1].prepend('19') when 20..39 month = (pesel[2..3].to_i - 20).to_s year = pesel[0..1].prepend('20') when 40..59 month = (pesel[2..3].to_i - 40).to_s year = pesel[0..1].prepend('21') when 60..79 month = (pesel[2..3].to_i - 60).to_s year = pesel[0..1].prepend('22') end # 原本打算加的验证逻辑,但根本走不到这一步 birth_date = Time.strptime("#{day}/#{month}/#{year}", '%d/%m/%Y') if birth_date.valid? self.birth_date = birth_date else errors.add(:pesel, I18n.t('activerecord.errors.models.profile.attributes.pesel.invalid')) end end
解决方案
核心问题是:Time.strptime遇到无效日期(比如14月、32日)时会直接抛出ArgumentError,而不是返回一个可验证的Time对象。所以我们需要先捕获这个异常,再结合提前的月份合法性校验来处理。
1. 捕获strptime的异常
用begin-rescue块包裹日期解析逻辑,当解析失败时直接添加错误提示。
2. 提前校验月份范围
在计算出实际月份后,先检查是否在1-12之间,避免无效月份进入解析步骤。
优化后的完整代码
def set_birth_date return unless pesel_changed? day = pesel[4..5] pesel_month = pesel[2..3].to_i month = nil year = nil case pesel_month when 0..19 month = pesel_month.to_s year = "19#{pesel[0..1]}" when 20..39 month = (pesel_month - 20).to_s year = "20#{pesel[0..1]}" when 40..59 month = (pesel_month - 40).to_s year = "21#{pesel[0..1]}" when 60..79 month = (pesel_month - 60).to_s year = "22#{pesel[0..1]}" else # 处理不在规则内的月份,直接添加错误 errors.add(:pesel, I18n.t('activerecord.errors.models.profile.attributes.pesel.invalid')) return end # 提前校验月份是否在1-12之间 unless month.to_i.between?(1, 12) errors.add(:pesel, I18n.t('activerecord.errors.models.profile.attributes.pesel.invalid')) return end begin birth_date = Time.strptime("#{day}/#{month}/#{year}", '%d/%m/%Y') self.birth_date = birth_date rescue ArgumentError errors.add(:pesel, I18n.t('activerecord.errors.models.profile.attributes.pesel.invalid')) end end
关键说明
- 异常捕获:因为
strptime对无效日期(比如2月30日、4月31日)会直接抛错,所以必须用rescue来捕获,才能正常添加错误而不中断程序。 - 提前月份校验:虽然PESEL规则里转换后的月份应该是1-12,但如果PESEL本身有错误(比如输入了80作为月份),提前校验可以避免进入解析步骤,提高效率。
- 返回逻辑:每次添加错误后都用
return终止方法,避免后续代码执行。
内容的提问来源于stack exchange,提问作者Shruikan
相关产品推荐
相关产品推荐

