今天发现了一些奇怪的行为,我希望有人能解释一下。
我正在使用 strptime 来验证导入文件中的日期。在这种情况下,如果文件中的一行包含不符合格式 %Y/%m/%d (2017/01/25) 的日期,我想抛出错误。
我调用 strptime 如下:
Date.strptime('25/01/2017', '%Y/%m/%d')
我预计这会失败,因为 25 不符合当年的标准。然而,这成功了,提供了一个日期:
0025、01、20
如果我在 (01/25/2018) 左右交换月份和日期,它会失败,因为它确实检测到月份无效。
那么是什么给了?这似乎很奇怪,它不仅创造了这个看起来很精神的年份 (0025),而且更疯狂的是它毫无问题地忽略了字符串末尾的“17”。
提前致谢! :)
最佳答案
你必须考虑你实际说的话:
Date.strptime('25/01/2017', '%Y/%m/%d')
你是说你想要年份 0025
, 月 01
和天20
(它剥离其余部分)。最后你得到 0025-01-20
.
你不能只依赖 Date.strptime
为您进行验证。
最好的办法是通过正则表达式实际解析它并进行验证。
对于您的格式,一个可能的正则表达式(一种简单的方法):
'25/01/2017'.match(/\d{4}\/\d{2}\/\d{2}/)
在您的情况下,您将获得 nil
, 因为不匹配。
如果匹配成功,您将获得:
#<MatchData "2017/01/25">
.
问题是这不会检查日期的正确格式。您仍然需要检查是否 strptime
可以解析结果(就像 Tom Lord 提供的链接中的那样)。
另一方面,您也可以仅使用正则表达式来检查它,这可能相当复杂:(以下正则表达式检查 yyyy/mm/dd
格式):
^(?:(?:(?:(?:(?:[1-9]\d)(?:0[48]|[2468][048]|[13579][26])|(?:(?:[2468][048]|[13579][26])00))(\/)(?:0?2\1(?:29)))|(?:(?:[1-9]\d{3})(\/)(?:(?:(?:0?[13578]|1[02])\2(?:31))|(?:(?:0?[13-9]|1[0-2])\2(?:29|30))|(?:(?:0?[1-9])|(?:1[0-2]))\2(?:0?[1-9]|1\d|2[0-8])))))$
然后您可以立即知道日期格式是否正确,而不必使用 strptime
检查解析它.
编辑:
处理时间时,请记住始终执行您自己的检查!不要依赖函数。时间问题是你有很多异常(exception),即使你有 ISO 8601,也许其他一些应用程序可能不遵循它。
根据评论,我想深入挖掘 strptime
现在我想将注释粘贴到源代码中(在 date_s_strptime 函数和 data_core.c 中):
/*
* call-seq:
* Date.strptime([string='-4712-01-01'[, format='%F'[, start=Date::ITALY]]]) -> date
*
* Parses the given representation of date and time with the given
* template, and creates a date object. strptime does not support
* specification of flags and width unlike strftime.
*
* Date.strptime('2001-02-03', '%Y-%m-%d') #=> #<Date: 2001-02-03 ...>
* Date.strptime('03-02-2001', '%d-%m-%Y') #=> #<Date: 2001-02-03 ...>
* Date.strptime('2001-034', '%Y-%j') #=> #<Date: 2001-02-03 ...>
* Date.strptime('2001-W05-6', '%G-W%V-%u') #=> #<Date: 2001-02-03 ...>
* Date.strptime('2001 04 6', '%Y %U %w') #=> #<Date: 2001-02-03 ...>
* Date.strptime('2001 05 6', '%Y %W %u') #=> #<Date: 2001-02-03 ...>
* Date.strptime('sat3feb01', '%a%d%b%y') #=> #<Date: 2001-02-03 ...>
*
* See also strptime(3) and #strftime.
*/
你可以看到像 sat/feb 这样的字符串也被使用了,所以解析器可以处理字符串也就不足为奇了。 待续 - 深入研究 C 代码
关于Ruby strptime 不会在参数为 '25/01/2017' 的 %Y/%m/%d 上抛出 ArgumentError,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51060593/