为什么“23 条狗”在 pry 中被解析为 2015 年 11 月 23 日,而“3 条狗”给出了解析器错误?
Why does "23 Dogs" get parsed to 23 november 2015 in pry, but "3 Dogs" gives a parser error?
我在 Twitter 上找到了下面的代码片段(查看 post 源历史记录)。
[5] pry(main)> Date.parse('3 Dogs')
ArgumentError: invalid date
[6] pry(main)> Date.parse('23 Dogs')
=> Mon, 23 Nov 2015
这只是一个偷来的彩蛋吗?如果是这样,为什么这个特定的日期和结果?如果不是彩蛋,为什么 23 Dogs
解析为日期,但 3 Dogs
不解析?
这与Pry无关。如果您检查 Date::parse 的文档,您会看到 "If the optional second argument [comp
] is true [the default] and the detected year is in the range “00” to “99”, considers the year a 2-digit form and makes it full.".
这确实很奇怪。它检测到 "23"
在 "00".."30"
范围内(但不在 "3"
或任何 "0".."9"
范围内),因此它断定这是一个日期。请注意,"31".."99"
范围内的任何值也会引发异常。我预计 "30"
是范围的高端,因为目前是 11 月,而 11 月有 30 天。然后它似乎丢弃该信息并使用当前的年份和月份,并假设日期是 "23"
(或者如果输入 "31"
则引发异常)。任何人都可以解释发生了什么的细节吗?
所以跟pry没关系。我可以在 ruby 2.2.2 的 ruby 代码中重现您的报告,但根本不会加载 pry。
那么为什么 Date.parse 愿意解析“23 条狗”并想出一些东西呢?我不知道。我会说这是 Date 解析中的一些特质,甚至是错误;它试图解析各种事物,但这会导致一些奇怪的边缘情况。
要对已知固定格式的日期进行更可预测的解析,请使用 Date#strptime instead. For more sophisticated parsing of natural language dates in unpredictable formats, use the chronic gem。
就个人而言,我从不直接使用 Date.parse
,因为它有点不可预测,而是使用这两种方法中的一种。 (或特定格式解析方法,如 Date.iso8601
)。
我试图查看 Date.parse
的 MRI 代码,因为我很好奇我是否能弄清楚它在做什么。但很快就迷失在我无法理解或遵循的 C 代码中,不得不放弃。
有趣的是,这也确实在 JRuby 1.7.10 中重现(我还没有安装 jruby 9x)。 “23 条狗”解析为同一事物,“3 条狗”引发。嗯,也许 JRuby Java 代码对我们中的一些人来说比 MRI 的 C 代码更容易理解。但是我没有时间去尝试 through/debug JRuby 中的 Date#parse 正在做什么。它的核心 可能 开始于 here,尽管我可能没有找到当前版本实现的正确位置。您可以看到它会尝试依次根据多种不同格式解析日期,并在根据某种格式成功解析时停止。我们可以猜测该列表中有一些奇怪的格式以某种方式成功解析“23 只狗”而不是“3 只狗”。这可能不是彩蛋,也不是故意的;这只是试图通过猜测日期的格式并依次尝试各种格式来解析日期的一个奇怪的副作用,而不是一个非常复杂的算法。
update 好吧,至少在我正在查看的 jruby 代码中(这可能不是当前的实现,而是一些实现)
最终,在尝试了其他可能失败的解析之后,它会尝试 Date._parse_ddd
-- 两个输入。
Date._parse_ddd("23 dogs", e)
returns true
,并用 mday
组件填充 Date::Parse::Bag,但 Date._parse_ddd("3 dogs", e)
returns false 且不填Bag
。所以其他一切都从这里开始。
如果我们看一下 Date._parse_ddd 实现...有一些怪异的正则表达式和奇怪的逻辑。可能从 MRI 复制以与 MRI 一致,或者以其他方式与 MRI 行为一致。
我不想再调试了。如果你愿意,你可以。如您所见,JRuby 实现实际上是用 ruby 编写的,甚至没有用 Java 编写。
您或我或其他人可以尝试进一步调试(甚至可能使用 JRuby stdlib 实现上的交互式调试器)以弄清楚确切 发生了什么。但我相信答案基本上是 "it's a weird side effect of Date.parse not really knowing what format it's input is in, but just trying a bunch of things, using a not very sophisticated algorithm, sometimes weird things happen"
更多更新:请注意,Date.parse("03 dogs")
会解析而不是提升。所以它决定两个数字是可解析的,一个不是。但当然 Date.parse("3 May")
工作正常。并不是说 Date.parse
需要两位数的日期,只是它尝试了一大堆不同的解析方式,一个实际的好日期会被正确捕获,但一个坏的日期可能会被其中一种方式捕获认为这似乎足够好,但在这种情况下是错误的。
更多的想法所以它不是故意这样解析的。这是旨在捕捉其他日期的启发式规则的副产品。由于代码没有注释,我们无法准确说明哪些部分要捕获的日期类型。这是一堆拼凑起来的东西,试图以各种格式(包括国际格式)捕捉日期。
您可以查看测试以了解它要捕获的所有类型的日期。或者您可以尝试浏览代码以准确了解哪些行导致了您所看到的行为。代码令人困惑——尤其是 MRI 中的 C 代码,对我们大多数人来说。 JRuby 中的纯 ruby 代码对于我们 ruby 爱好者来说当然更具可读性。由于浏览代码既混乱又耗时,而且收益甚微(谁在乎?),您可能不会让其他人为您做这件事。
我在 Twitter 上找到了下面的代码片段(查看 post 源历史记录)。
[5] pry(main)> Date.parse('3 Dogs')
ArgumentError: invalid date
[6] pry(main)> Date.parse('23 Dogs')
=> Mon, 23 Nov 2015
这只是一个偷来的彩蛋吗?如果是这样,为什么这个特定的日期和结果?如果不是彩蛋,为什么 23 Dogs
解析为日期,但 3 Dogs
不解析?
这与Pry无关。如果您检查 Date::parse 的文档,您会看到 "If the optional second argument [comp
] is true [the default] and the detected year is in the range “00” to “99”, considers the year a 2-digit form and makes it full.".
这确实很奇怪。它检测到 "23"
在 "00".."30"
范围内(但不在 "3"
或任何 "0".."9"
范围内),因此它断定这是一个日期。请注意,"31".."99"
范围内的任何值也会引发异常。我预计 "30"
是范围的高端,因为目前是 11 月,而 11 月有 30 天。然后它似乎丢弃该信息并使用当前的年份和月份,并假设日期是 "23"
(或者如果输入 "31"
则引发异常)。任何人都可以解释发生了什么的细节吗?
所以跟pry没关系。我可以在 ruby 2.2.2 的 ruby 代码中重现您的报告,但根本不会加载 pry。
那么为什么 Date.parse 愿意解析“23 条狗”并想出一些东西呢?我不知道。我会说这是 Date 解析中的一些特质,甚至是错误;它试图解析各种事物,但这会导致一些奇怪的边缘情况。
要对已知固定格式的日期进行更可预测的解析,请使用 Date#strptime instead. For more sophisticated parsing of natural language dates in unpredictable formats, use the chronic gem。
就个人而言,我从不直接使用 Date.parse
,因为它有点不可预测,而是使用这两种方法中的一种。 (或特定格式解析方法,如 Date.iso8601
)。
我试图查看 Date.parse
的 MRI 代码,因为我很好奇我是否能弄清楚它在做什么。但很快就迷失在我无法理解或遵循的 C 代码中,不得不放弃。
有趣的是,这也确实在 JRuby 1.7.10 中重现(我还没有安装 jruby 9x)。 “23 条狗”解析为同一事物,“3 条狗”引发。嗯,也许 JRuby Java 代码对我们中的一些人来说比 MRI 的 C 代码更容易理解。但是我没有时间去尝试 through/debug JRuby 中的 Date#parse 正在做什么。它的核心 可能 开始于 here,尽管我可能没有找到当前版本实现的正确位置。您可以看到它会尝试依次根据多种不同格式解析日期,并在根据某种格式成功解析时停止。我们可以猜测该列表中有一些奇怪的格式以某种方式成功解析“23 只狗”而不是“3 只狗”。这可能不是彩蛋,也不是故意的;这只是试图通过猜测日期的格式并依次尝试各种格式来解析日期的一个奇怪的副作用,而不是一个非常复杂的算法。
update 好吧,至少在我正在查看的 jruby 代码中(这可能不是当前的实现,而是一些实现)
最终,在尝试了其他可能失败的解析之后,它会尝试
Date._parse_ddd
-- 两个输入。Date._parse_ddd("23 dogs", e)
returnstrue
,并用mday
组件填充 Date::Parse::Bag,但Date._parse_ddd("3 dogs", e)
returns false 且不填Bag
。所以其他一切都从这里开始。如果我们看一下 Date._parse_ddd 实现...有一些怪异的正则表达式和奇怪的逻辑。可能从 MRI 复制以与 MRI 一致,或者以其他方式与 MRI 行为一致。
我不想再调试了。如果你愿意,你可以。如您所见,JRuby 实现实际上是用 ruby 编写的,甚至没有用 Java 编写。
您或我或其他人可以尝试进一步调试(甚至可能使用 JRuby stdlib 实现上的交互式调试器)以弄清楚确切 发生了什么。但我相信答案基本上是 "it's a weird side effect of Date.parse not really knowing what format it's input is in, but just trying a bunch of things, using a not very sophisticated algorithm, sometimes weird things happen"
更多更新:请注意,Date.parse("03 dogs")
会解析而不是提升。所以它决定两个数字是可解析的,一个不是。但当然 Date.parse("3 May")
工作正常。并不是说 Date.parse
需要两位数的日期,只是它尝试了一大堆不同的解析方式,一个实际的好日期会被正确捕获,但一个坏的日期可能会被其中一种方式捕获认为这似乎足够好,但在这种情况下是错误的。
更多的想法所以它不是故意这样解析的。这是旨在捕捉其他日期的启发式规则的副产品。由于代码没有注释,我们无法准确说明哪些部分要捕获的日期类型。这是一堆拼凑起来的东西,试图以各种格式(包括国际格式)捕捉日期。
您可以查看测试以了解它要捕获的所有类型的日期。或者您可以尝试浏览代码以准确了解哪些行导致了您所看到的行为。代码令人困惑——尤其是 MRI 中的 C 代码,对我们大多数人来说。 JRuby 中的纯 ruby 代码对于我们 ruby 爱好者来说当然更具可读性。由于浏览代码既混乱又耗时,而且收益甚微(谁在乎?),您可能不会让其他人为您做这件事。