如何检查字符串是不是为有效日期
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(&: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(&: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
【讨论】:
以上是关于如何检查字符串是不是为有效日期的主要内容,如果未能解决你的问题,请参考以下文章