ruby - Ruby strptime 不会在带有参数“25/01/2017”的 %Y/%m/%d 上抛出 ArgumentError
问题描述
今天发现了一些奇怪的行为,我希望有人能解释一下。
我正在使用 strptime 来验证导入文件中的日期。在这种情况下,如果文件中的一行包含不符合格式 %Y/%m/%d (2017/01/25) 的日期,我想抛出一个错误。
我调用 strptime 如下:
Date.strptime('25/01/2017', '%Y/%m/%d')
我预计这会失败,因为 25 不符合当年的标准。然而,这成功了,提供一个日期:
0025, 01, 20
如果我在(2018 年 1 月 25 日)前后交换月份和日期,它会失败,因为它确实检测到月份无效。
那么给了什么?看起来很奇怪,它不仅创造了这个看起来很精神的年份(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])))))$
然后你马上就知道日期的格式是否正确,你不必用 .parse 检查它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 代码
推荐阅读
- android - 无需服务和 GUI 应用程序即可连续执行
- docker - Terraform 的 Docker 容器无法启动
- javascript - ReCaptcha - Chrome 隐藏扩展
- javascript - 脚本运行两次,一次在 iframe 外的内容上,然后一次在 iframe 内的内容上
- apache-flink - Flink Streaming:TriggerResult.FIRE 和 TriggerResult.FIRE_AND_PURGE 的区别
- java - 生成与 3 位代码仅相差一位的所有 3 位代码
- internet-explorer - 如何启用/禁用 Adobe PDF Reader Active X Internet Explorer Browser Ext VIA Registry?
- javascript - Vuejs 和 Laravel:如何使用 http get 将数组传递给 Laravel 控制器
- java - 多线程和并发 - 如何使用精心设计的单例 bean 制作休息应用程序?
- ruby-on-rails - 对 PG 存储过程的 ActiveRecord 调用失败