【问题标题】:How to check if a string is a valid date如何检查字符串是否为有效日期
【发布时间】:2011-02-26 16:52:21
【问题描述】:

我有一个字符串:"31-02-2010" 并想检查它是否是有效日期。 最好的方法是什么?

我需要一个方法,如果字符串是有效日期则返回 true,否则返回 false。

【问题讨论】:

  • 为什么不直接做一个 date_time 下拉菜单,而不是输入一个你必须验证的字符串?
  • 客户要求。我已经建议了,但不能这样做:)
  • 作为一般规则,您不应该在应用程序的前端进行输入验证吗?
  • 这里有更好的答案 - 这是如何做到的。 stackoverflow.com/questions/597328/…
  • 总是在后端验证,不管你的前端有多好,不要相信它!

标签: ruby-on-rails ruby string date


【解决方案1】:
require 'date'
begin
   Date.parse("31-02-2010")
rescue ArgumentError
   # handle invalid date
end

【讨论】:

  • 不是 Date 类的特性,是异常处理。
  • Date.parse('2012-08-13== 00:00:00') # => 2012 年 8 月 13 日星期一
  • 使用“catch-it-all”救援应该被认为是一种反模式。它可以隐藏其他我们没有预料到的错误,并使代码的调试变得异常困难。
  • 那么......如果它确实是一个反模式,你会如何回答这个问题?
  • 对不起,这是一个错误的答案。它不检查字符串是否是有效日期,它只是检查 Date.parse 可以解析字符串。 Date.parse 在日期方面似乎非常宽容,例如它将“FOOBAR_09_2010”解析为日期 2012-09-09。
【解决方案2】:

这是一个简单的单行:

DateTime.parse date rescue nil

我可能不建议在现实生活中的所有情况下都这样做,因为你强制调用者检查 nil,例如。特别是在格式化时。如果您返回默认日期|错误,它可能会更友好。

【讨论】:

  • DateTime.parse "123" 救援 nil 。这将返回一个真实的日期.. 2017 年 5 月 3 日
  • Date.strptime(date, '%d/%m/%Y') rescue nil
  • 不鼓励使用内联rescue
【解决方案3】:
d, m, y = date_string.split '-'
Date.valid_date? y.to_i, m.to_i, d.to_i

【讨论】:

  • 这非常简单,非常适合强制执行 xx-xx-YYYY 格式
  • 如果您想强制执行严格的单一格式日期字符串选项,那么这是最好的选择,因为它可以避免异常并且具有确定性。 Date.valid_date?是至少用于 Ruby 2 的方法。ruby-doc.org/stdlib-2.0.0/libdoc/date/rdoc/…
  • 什么版本的Ruby支持is_valid??尝试了 1.8、2.0 和 2.1。他们似乎都没有那个。不过,似乎所有人都有valid_date?
【解决方案4】:

解析日期可能会遇到一些问题,尤其是当它们采用 MM/DD/YYYY 或 DD/MM/YYYY 格式时,例如美国或欧洲使用的短日期。

Date#parse 试图找出使用哪个格式,但一年中的一个月中有很多天格式之间的歧义会导致解析问题。

我建议找出用户的 LOCALE 是什么,然后,基于此,您将知道如何使用 Date.strptime 进行智能解析。查找用户所在位置的最佳方法是在注册期间询问他们,然后在他们的偏好中提供设置以进行更改。假设您可以通过一些巧妙的启发式方法将其挖掘出来,并且不会打扰用户获取该信息,那么很容易失败,所以请询问。

这是一个使用Date.parse 的测试。我在美国:

>> Date.parse('01/31/2001')
ArgumentError: invalid date

>> Date.parse('31/01/2001') #=> #<Date: 2001-01-31 (4903881/2,0,2299161)>

第一个是美国的正确格式:mm/dd/yyyy,但 Date 不喜欢它。第二个对欧洲来说是正确的,但如果您的客户主要是美国,那么您会得到很多错误解析的日期。

Ruby 的Date.strptime 用法如下:

>> Date.strptime('12/31/2001', '%m/%d/%Y') #=> #<Date: 2001-12-31 (4904549/2,0,2299161)>

【讨论】:

  • 您的 LOCALE 似乎已关闭。我明白了 >> Date.parse('01/31/2001') => Wed, 31 Jan 2001 >> ?> Date.parse('31/01/2001') ArgumentError: invalid date
  • 不确定我是否在这里很愚蠢,但这似乎只解释了如何解析日期,而不是如何验证它。接受的答案使用 ArgumentError 异常,但这似乎不是一个好主意,原因在评论中注明。
  • 存在无法验证日期的已知情况。这是日期和月份值不明确的地方,您必须知道传入日期字符串的格式。而且,这就是答案所说的。如果它看起来像01/01/2014,那是月份,哪一天?在美国月份将是第一个月,世界其他地区将是第二个月。
  • 阻止您验证日期的模棱两可只会阻止您解析日期,但您已经很好地尝试使用已知信息解析它。使用您必须尝试验证的一些内容会很好。你能想到如何在不使用 Date.parse 和 Date.strptime 创建的异常的情况下做到这一点吗?
  • “mm/dd/yyyy”或“dd/mm/yyyy”格式的日期解析需要知道日期的格式。没有它,我们无法确定它是哪一个,除非我们从一个源/用户那里取样,然后使用这两种形式进行解析,然后查看哪种形式产生的异常最多,然后使用另一种。当解析来自多个来源的日期时,这会发生故障,尤其是当它们是全局的或手动输入的时候。处理日期的唯一准确方法是不允许手动输入,强制用户使用日期选择器,或者只允许明确的 ISO 日期格式。
【解决方案5】:

Date.valid_date? *date_string.split('-').reverse.map(&amp;:to_i)

【讨论】:

  • 太好了,谢谢。这个似乎至少在 1.8、2.0 和 2.1 中可用。请注意,您需要先require "date",否则您会得到“未定义的方法”。
  • 此方法可以引发异常“ArgumentError:参数数量错误”而不是返回 false。例如,如果您的字符串包含斜杠而不是破折号
  • 也许我们可以做这样的事情来处理格式错误的日期:Date.valid_date? *Array.new(3).zip(date_string.split('-')).transpose.last.reverse.map(&amp;:to_i)
【解决方案6】:

我想扩展Date 类。

class Date
  def self.parsable?(string)
    begin
      parse(string)
      true
    rescue ArgumentError
      false
    end
  end
end

示例

Date.parsable?("10-10-2010")
# => true
Date.parse("10-10-2010")
# => Sun, 10 Oct 2010
Date.parsable?("1")
# => false
Date.parse("1")
# ArgumentError: invalid date from (pry):106:in `parse'

【讨论】:

【解决方案7】:

另一种验证日期的方法:

date_hash = Date._parse(date.to_s)
Date.valid_date?(date_hash[:year].to_i,
                 date_hash[:mon].to_i,
                 date_hash[:mday].to_i)

【讨论】:

  • 在此基础上,您可以将其简化为:Date.valid_date?(*Date._parse(date).values)
【解决方案8】:

对所有日期尝试正则表达式:

/(\d{1,2}[-\/]\d{1,2}[-\/]\d{4})|(\d{4}[-\/]\d{1,2}[-\/]\d{1,2})/.match("31-02-2010")

仅适用于带有前导零、去年和破折号的格式:

/(\d{2}-\d{2}-\d{4})/.match("31-02-2010")

[-/] 表示 - 或 /,正斜杠必须被转义。 你可以在http://gskinner.com/RegExr/上测试这个

添加以下行,如果您使用第一个正则表达式,它们将全部突出显示,没有第一个和最后一个 /(它们用于 ruby​​ 代码)。

2004-02-01
2004/02/01
01-02-2004
1-2-2004
2004-2-1

【讨论】:

  • 这个正则表达式只匹配一些固定的格式,不关心日期的语义。您的匹配器允许日期,例如 32-13-2010,这是错误的。
  • 如果您想粗略估计是否可以将任意字符串解析为日期,那就足够了。
  • 其中“粗略估计”意味着像 '00/00/0000''99-99/9999' 这样的值是可能的候选值。
  • 一个很好的例子,说明自定义正则表达式是一个坏主意!
【解决方案9】:

更严格的解决方案

如果您指定所需的日期格式,则更容易验证日期的正确性。然而,即便如此,对于我的用例来说,Ruby 还是有点太宽容了:

Date.parse("Tue, 2017-01-17", "%a, %Y-%m-%d") # works
Date.parse("Wed, 2017-01-17", "%a, %Y-%m-%d") # works - !?

显然,这些字符串中至少有一个指定了错误的工作日,但 Ruby 很高兴地忽略了这一点。

这是一个没有的方法;它验证date.strftime(format) 根据format 转换回与Date.strptime 解析的相同输入字符串。

module StrictDateParsing
  # If given "Tue, 2017-01-17" and "%a, %Y-%m-%d", will return the parsed date.
  # If given "Wed, 2017-01-17" and "%a, %Y-%m-%d", will error because that's not
  # a Wednesday.
  def self.parse(input_string, format)
    date = Date.strptime(input_string, format)
    confirmation = date.strftime(format)
    if confirmation == input_string
      date
    else
      fail InvalidDate.new(
        "'#{input_string}' parsed as '#{format}' is inconsistent (eg, weekday doesn't match date)"
      )
    end
  end

  InvalidDate = Class.new(RuntimeError)
end

【讨论】:

  • 这正是我想要的。谢谢!
  • 这对于像“2019-1-1”这样的情况会失败,这是一个有效的日期,但 strftime 使它成为 2019-01-01
  • @saGii 不符合指定格式(iso8601 - 请参阅Date.today().iso8601); %m 是零填充的。如果您想要不同的东西,请参阅ruby-doc.org/stdlib-2.4.0/libdoc/date/rdoc/…
【解决方案10】:

发布此内容是因为它可能对以后的某人有用。不知道这是否是一种“好”的方法,但它对我有用并且是可扩展的。

class String

  def is_date?
  temp = self.gsub(/[-.\/]/, '')
  ['%m%d%Y','%m%d%y','%M%D%Y','%M%D%y'].each do |f|
  begin
    return true if Date.strptime(temp, f)
      rescue
       #do nothing
    end
  end

  return false
 end
end

这个 String 类的附加组件允许您在第 4 行指定分隔符列表,然后在第 5 行指定有效格式列表。这不是火箭科学,但它非常容易扩展,让您只需检查一个字符串,如所以:

"test".is_date?
"10-12-2010".is_date?
params[:some_field].is_date?
etc.

【讨论】:

    【解决方案11】:

    您可以尝试以下方法,这是最简单的方法:

    "31-02-2010".try(:to_date)
    

    但是你需要处理异常。

    【讨论】:

      【解决方案12】:

      与@ironsand 的解决方案类似,我更喜欢在String 上创建一个覆盖的实例方法:

      class String
        def valid_datetime?
          to_datetime
          true
        rescue ArgumentError
          false
        end
      end
      

      【讨论】:

        【解决方案13】:
        require 'date'
        #new_date and old_date should be String
        # Note we need a ()
        def time_between(new_date, old_date)
          new_date = (Date.parse new_date rescue nil)
          old_date = (Date.parse old_date rescue nil)
          return nil if new_date.nil? || old_date.nil?
          (new_date - old_date).to_i
        end
        
        puts time_between(1,2).nil?
        #=> true
        puts time_between(Time.now.to_s,Time.now.to_s).nil?
        #=> false
        

        【讨论】:

          【解决方案14】:

          此示例的 Date.parse 未引发异常:

          Date.parse("12!12*2012")
          => Thu, 12 Apr 2018
          
          Date.parse("12!12&2012")
          => Thu, 12 Apr 2018
          

          我更喜欢这个解决方案:

          Date.parse("12!12*2012".gsub(/[^\d,\.,\-]/, ''))
          => ArgumentError: invalid date
          
          Date.parse("12-12-2012".gsub(/[^\d,\.,\-]/, ''))
          => Wed, 12 Dec 2012
          
          Date.parse("12.12.2012".gsub(/[^\d,\.,\-]/, ''))
          => Wed, 12 Dec 2012
          

          【讨论】:

            【解决方案15】:

            方法:

            require 'date'
            def is_date_valid?(d)
              Date.valid_date? *"#{Date.strptime(d,"%m/%d/%Y")}".split('-').map(&:to_i) rescue nil
            end
            

            用法:

            config[:dates].split(",").all? { |x| is_date_valid?(x)}
            

            如果 config[:dates] = "12/10/2012,05/09/1520" 则返回 true 或 false

            【讨论】:

              猜你喜欢
              • 2018-06-15
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2020-06-23
              • 2011-11-18
              • 1970-01-01
              • 2018-02-02
              相关资源
              最近更新 更多