【问题标题】:Ruby strptime doesn't throw ArgumentError on %Y/%m/%d with parameter '25/01/2017'Ruby strptime 不会在带有参数“25/01/2017”的 %Y/%m/%d 上抛出 ArgumentError
【发布时间】:2018-06-27 10:34:11
【问题描述】:

今天发现了一些奇怪的行为,希望有人能解释一下。

我正在使用 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”。

提前致谢! :)

【问题讨论】:

  • 2017 --> 20 似乎是更通用的方法:任何尾随字符总是被忽略。你可以做Date.strptime('25/01/20 BLA BLA BLA', '%Y/%m/%d') 并得到相同的结果。
  • 至于为什么25被解释为0025而不是错误,不过,我不明白。据我了解,%Y 应该要求 最少 4 位数字才能有效,但显然情况并非如此。可能是一个错误?
  • 您可以在this question 中找到与您的问题相关且对您有帮助的答案。

标签: ruby date strptime


【解决方案1】:

你必须想想你实际上说了什么:

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检查解析它。

编辑:

在处理时间时,请记住始终进行自己的检查!不要依赖函数。时间问题是您有很多例外,即使您有 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 代码

【讨论】:

  • 我结束了最初的正则表达式检查,然后是剥离时间。正如 Tom Lord 所说,我本以为 %Y 在第一个正斜杠之前会出现 4 个字符,这对我来说是一个公平的假设。它的实际工作方式似乎与文档描述的方式非常不直观。
  • @MathewBarnard 看到我的第一个编辑。当我找到它时,我将发布有关 C 代码详细信息的更多信息。现在看看评论,它对这个功能说了很多。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-12-22
  • 2021-06-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多