echo 20171231 | xargs -i date -d "{} +1 day" | xargs -i date -d "{} -1 month"
**Fri Dec 1 00:00:00 PST 2017**
在这种情况下,当日期格式化命令通过管道传输时,我得到的是 12 月 1 日。
echo 20171231 | xargs -i date -d "{} +1day -1 month"
Sat Dec 2 00:00:00 PST 2017
而当日期格式包含在单个数据命令中时,得到的结果为 Dec 2。
在上面的命令中,-1 个月的优先级高于 +1 天。
有人可以帮助我理解这是如何运作的吗?
答案1
不,没有优先顺序。
具有讽刺意味的是,这个问题本月刚刚出现在 Debian 用户的邮件列表中,其中有人指出,对于阅读所谓的人类可读的自然语言日期操作命令的人来说,GNU 工具的行为date
似乎非常不一致。 Vincent Lefèvre 举了这些例子:
jdebp % 日期 +%Y-%m-%d -d '2003-02-01 - 1 个月' 2003-01-01 jdebp % 日期 +%Y-%m-%d -d '2003-02-01 - 31 天' 2003-01-01 jdebp % 日期 +%Y-%m-%d -d '2003-02-01 - 31 天 + 1 个月' 2003-01-29 jdebp % 日期 +%Y-%m-%d -d '2003-02-01 - 1 个月 + 1 个月' 2003-02-01 jdebp % 日期 +%Y-%m-%d -d '2003-09-01 1 天前 + 1 个月' 2003-09-30 jdebp % 日期 +%Y-%m-%d -d '2003-09-01 1 天前' 2003-08-31 jdebp % 日期 +%Y-%m-%d -d '2003-08-31 + 1 个月' 2003-10-01 净水量 %
内部实际发生的情况date
是,在计算过程中,它正在构造一些带有负值的中间无效日期,例如2003-03-(-30)
。然后它重新规范化使用 C 语言标准库中的函数完成所有操作后,这些无效日期。
这是什么不是正在做的是重新规范化在每一步,就像人类一样。所以 2003-02-01 减去 31 天对 GNUdate
程序来说是一个无效日期,2月30日负数,而不是人类可能计算的一月份的有效日期。添加一个月,这将成为 3 月的无效日期,仍然是负 30 日,最终重新规范化为 1 月的该日期,因为当然调整为-30
大于零的数字会跳回整个 2 月。其他示例中未重整化的无效日期为2003-10-00
、2003-09-00
和2003-09-31
。
将此应用于您的示例:
2017-12-31 + 1 day
是,它在程序的输出中2017-12-32
重新标准化为。2018-01-01
2018-01-01 - 1 month
是,它在程序的输出中2018-00-01
重新标准化为。2017-12-01
2017-12-31 + 1 day - 1 month
是,它在程序的输出中2017-11-32
重新标准化为。2017-12-02
正如你所看到的你在每一步都重新规范化你不会得到与一次性应用所有更改相同的结果,因为 GNUdate
一次性应用多个更改不在每一步重新规范化。
进一步阅读
- 迈克尔·斯通 (2013-11-21)。
/bin/date
: 日期解析不一致。 Debian 错误 #729952。 - 乌尔夫·齐比斯 (2017-03-15)。 适得其反的计算顺序
date
。 GNU 错误#26101。 - 文森特·勒菲弗 (2018-02-06)。围绕“不会修复”错误标签的政策。 debian 用户。
答案2
在我看来,date
首先返回一个月,然后按此顺序添加日期。
如果更改管道中操作的顺序,您将获得与其他操作相同的结果。
$ echo 20171231 | xargs -i date -d "{} -1 month" | xargs -i date -d "{} +1 day"
Sat Dec 2 00:00:00 EET 2017
问题是,从 12 月 31 日往回追溯一个月是有问题的。上个月的同一天是 11 月 31 日,但 11 月只有 30 天。从某种意义上说,11 月 31 日与 12 月 1 日相同,因此给出后者有一定的逻辑性。
当然,另一种选择是从 12 月 31 日到 11 月 30 日,但这也并非完全没有问题。那么应该Nov 30 - 1 month
是10月30日还是10月31日?
您可能需要手动实现所需的逻辑。
答案3
出现这种情况的原因是:
$ a=20171231; b=$(date -d "$a -1 month"); echo "<$b>"
<Fri Dec 1 00:00:00 PST 2017>
命令日期转到 12 月初。
当您添加 1 天时,它会转到 day 2
。
相反,当这一天是 1 月 1 日时,它会回溯到整整一个月
$ a=20180101; b=$(date -d "$a -1 month"); echo "<$b>"
<Fri Dec 1 00:00:00 PST 2017>
这是日期的内部特征。
date
返回上个月与给定日期相同的日期:
$ a=20171110; b=$(date -d "$a -1 month"); echo "<$b>"
<Tue Oct 10 00:00:00 PST 2017>
也就是说,11 月的指定日期可以追溯到 10 月。但如果这一天是 31 号,就会遇到麻烦。例如,从 10 月 31 日开始,就没有 9 月 31 日了,因此可以追溯到 10 月 1 日:
$ a=20171031; b=$(date -d "$a -1 month"); echo "<$b>"
<Sun Oct 1 00:00:00 AST 2017>
仅当日期为 01 时,月移才是准确的,二月是一个特别奇数的月份:
$ a=20170331; b=$(date -d "$a -1 month"); echo "<$b>"
<Fri Mar 3 00:00:00 AST 2017>
因为没有2月31日。