How to check if a string is a valid date

I have a string: "31-02-2010" and want to check whether or not it is a valid date. What is the best way to do it?

I need a method which which returns true if the string is a valid date and false if it is not.

105274 次浏览
require 'date'
begin
Date.parse("31-02-2010")
rescue ArgumentError
# handle invalid date
end
d, m, y = date_string.split '-'
Date.valid_date? y.to_i, m.to_i, d.to_i

解析日期可能会遇到一些问题,特别是当它们是 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)>

发布这篇文章是因为它可能对以后的某些人有用。不知道这是否是一个“好”的方式来做它或不,但它为我工作,是可扩展的。

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.

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

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

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

下面是一个简单的提示:

DateTime.parse date rescue nil

我可能不建议在现实生活中的每种情况下都这样做,因为您会强制调用者检查是否为空,尤其是在格式化时。如果您返回一个默认的日期 | 错误,它可能更友好。

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

验证日期的另一种方法:

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)

更严格的解决方案

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

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

Clearly, at least one of these strings specifies the wrong weekday, but Ruby happily ignores that.

这里有一个方法不这样做; 它验证 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
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

方法:

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 [ : date ] = “12/10/2012,05/09/1520”,则返回 true 或 false

你可以试试下面这个简单的方法:

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

But you need to handle the exception.

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

Similar to the solution by @ironsand, I prefer to create an overridden instance method on String:

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