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


114

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

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


2
为什么不只是下拉date_time而不是输入必须验证的字符串?
被腐蚀

客户的要求。我已经建议过,但是不能这样做:)
Salil 2010年

通常,您不应该在应用程序的前端进行输入验证吗?
Seanny123 2013年

这里有更好的答案-这就是方法。 stackoverflow.com/questions/597328/...
N13

3
无论前端多么出色,请始终在后端进行验证,请不要相信它!
SparK's

Answers:


112
require 'date'
begin
   Date.parse("31-02-2010")
rescue ArgumentError
   # handle invalid date
end

27
它不是Date类的功能,而是异常处理。
码头-奥利维尔·蒂博

3
Date.parse('2012-08-13 == 00:00:00')#=> 2012
Bob

13
使用“全面捕获”救援应视为一种反模式。它可以隐藏我们不期望的其他错误,并使代码调试极为困难。
yagooar 2013年

8
所以...如果确实是反模式,您将如何回答这个问题?
马修·布朗

11
抱歉,这是一个错误的答案。它不检查字符串是否是有效日期,仅检查Date.parse可以解析该字符串。对于日期,Date.parse似乎是非常宽容的,例如,它将“ FOOBAR_09_2010”解析为日期2012-09-09。
2013年

54

这是一个简单的班轮:

DateTime.parse date rescue nil

我可能不建议您在现实生活中的每种情况下都严格执行此操作,因为您迫使调用者检查是否为零。特别是在格式化时。如果返回默认日期|错误,则可能会更友好。


8
DateTime.parse“ 123”救援为nil。这将返回真实日期。2017
baash05 2015年

3
Date.strptime(date, '%d/%m/%Y') rescue nil
bonafernando

1
rescue不建议使用内联。
smoyth

52
d, m, y = date_string.split '-'
Date.valid_date? y.to_i, m.to_i, d.to_i

这非常简单,适合执行xx-xx-YYYY格式
2013年

如果要强制使用严格的单一格式日期字符串选项,则这是最佳选择,因为它避免了异常并且具有确定性。Date.valid_date?是至少用于Ruby 2的方法 。ruby
doc.org/

4
Ruby支持什么版本is_valid??尝试了1.8、2.0和2.1。他们似乎都没有那个。似乎都有valid_date?
Henrik N

29

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

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

我建议您找出用户的本地地址,然后基于此,您将知道如何使用 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,2001年1月31日>>?> Date.parse('31 / 01/2001')ArgumentError:无效的日期
hoyhoy 2011年

不知道我是否在这里呆滞,但这似乎仅解释了如何解析日期,而不是如何验证日期。接受的答案使用ArgumentError异常,但这似乎不是一个好主意,因为其中的注释中指出了这些原因。
cesoid 2014年

在一种已知情况下,您无法验证日期。这是日期和月份值不明确的地方,您必须知道传入日期字符串的格式。而且,这就是答案的意思。如果看起来像01/01/2014,那么哪个月和哪一天?在美国,月度排名第一,世界其他地区排名第二。
Tin Man

只能在某种程度上阻止您验证日期的模糊性,以至于它也阻止您解析日期,但是您已经尝试了使用已知信息进行解析。最好使用一些您想要尽力进行验证的东西。您能想到不使用Date.parse和Date.strptime创建的异常怎么做吗?
cesoid 2015年

以“ mm / dd / yyyy”或“ dd / mm / yyyy”格式解析日期需要先输入日期格式。否则,除非我们从一个来源/用户那里进行采样,然后使用两种形式进行解析,然后查看哪种形式产生的异常最多,而使用另一种形式,则我们无法确定它是哪种。当解析来自多个来源的日期时,尤其是当它们是全局的或手动输入时,此方法将失效。处理日期的唯一准确方法是不允许手动输入,强迫用户使用日期选择器或仅允许明确的ISO日期格式。
Tin Man

26

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


太好了,谢谢。似乎至少在1.8、2.0和2.1中可用。请注意,您require "date"首先需要获得“未定义的方法”。
Henrik N

2
此方法可以引发异常“ ArgumentError:错误的参数数量”,而不是返回false。例如,如果您的字符串包含斜杠而不是破折号
vincentp

2
也许我们可以做这样的事情来处理格式错误的日期:Date.valid_date? *Array.new(3).zip(date_string.split('-')).transpose.last.reverse.map(&:to_i)
vincentp

9

我想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'


3

对于所有日期,请尝试使用正则表达式:

/(\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

2
此正则表达式仅匹配某些固定格式,而无需关心日期的语义。您的匹配器允许输入32-13-2010错误的日期。
yagooar 2012年

2
如果您想粗略估计是否可以将任意字符串解析为日期,就足够了。
尼尔·古德曼

“粗略估计”是指类似的值'00/00/0000''99-99/9999'并且可能是候选值。
Tin Man

1
自定义正则表达式的最佳示例就是BAD IDEA!
汤姆·罗德

3

更严格的解决方案

如果指定所需的日期格式,则更容易验证日期的正确性。但是,即使那样,对于我的用例来说,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)转换回与Date.strptime根据其解析的相同输入字符串format

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

@saGii不符合指定的格式(iso8601-请参见Date.today().iso8601);%m被零填充。如果你想要的东西不同,请参阅ruby-doc.org/stdlib-2.4.0/libdoc/date/rdoc/...
内森龙

2

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

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.

0
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


0

对于此示例,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

-1

方法:

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.