Ruby strptime 不会在参数为“25/01/2017”的 %Y/%m/%d 上引发 ArgumentError
Ruby strptime doesn't throw ArgumentError on %Y/%m/%d with parameter '25/01/2017'
今天发现了一些奇怪的行为,我希望有人能解释一下。
我正在使用 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 提供的 link 中那样)。
另一方面,您也可以仅使用正则表达式检查它,这可能相当复杂:(以下正则表达式检查 yyyy/mm/dd
格式):
^(?:(?:(?:(?:(?:[1-9]\d)(?:0[48]|[2468][048]|[13579][26])|(?:(?:[2468][048]|[13579][26])00))(\/)(?:0?2(?:29)))|(?:(?:[1-9]\d{3})(\/)(?:(?:(?:0?[13578]|1[02])(?:31))|(?:(?:0?[13-9]|1[0-2])(?:29|30))|(?:(?:0?[1-9])|(?:1[0-2]))(?:0?[1-9]|1\d|2[0-8])))))$
然后您会立即知道日期格式是否正确,而不必使用 strptime
.
检查解析它
编辑:
处理时间时记得始终执行您自己的检查!不要依赖函数。时间问题是你有很多例外,即使你有 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 代码
今天发现了一些奇怪的行为,我希望有人能解释一下。
我正在使用 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 提供的 link 中那样)。
另一方面,您也可以仅使用正则表达式检查它,这可能相当复杂:(以下正则表达式检查 yyyy/mm/dd
格式):
^(?:(?:(?:(?:(?:[1-9]\d)(?:0[48]|[2468][048]|[13579][26])|(?:(?:[2468][048]|[13579][26])00))(\/)(?:0?2(?:29)))|(?:(?:[1-9]\d{3})(\/)(?:(?:(?:0?[13578]|1[02])(?:31))|(?:(?:0?[13-9]|1[0-2])(?:29|30))|(?:(?:0?[1-9])|(?:1[0-2]))(?:0?[1-9]|1\d|2[0-8])))))$
然后您会立即知道日期格式是否正确,而不必使用 strptime
.
编辑:
处理时间时记得始终执行您自己的检查!不要依赖函数。时间问题是你有很多例外,即使你有 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 代码