如何检查字符串是不是为有效日期

Posted

技术标签:

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

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

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

【问题讨论】:

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

【讨论】:

我个人最喜欢这种方法。在我的应用程序中,我选择覆盖字符串:***.com/a/64190303/987115【参考方案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】:

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

/(\d1,2[-\/]\d1,2[-\/]\d4)|(\d4[-\/]\d1,2[-\/]\d1,2)/.match("31-02-2010")

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

/(\d2-\d2-\d4)/.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

【讨论】:

以上是关于如何检查字符串是不是为有效日期的主要内容,如果未能解决你的问题,请参考以下文章

接收字符串并使用 date-fns 验证它是不是是有效日期

如何检查文件是不是为有效的 HDF5 文件?

检查字符串是不是为有效 URL 的最佳正则表达式是啥?

我可以验证字符串是 Presto 中的有效日期吗

如何检查字符串是日期 [重复]

如何检查uri字符串是不是有效