在命令行中,距离给定日期n天前的日期是什么?

8
例如,给定日期为“2016-12-31”,n为2,则期望输出为“2016-12-29”。

我调查了date命令,并从当前日期获取n天前很容易:
date -d“2 days ago”+%Y-%m-%d

1
我将标签从[tag:unix]更改为[tag:linux],因为这种表现力的-d行为是GNU扩展,在POSIXBSD等中找不到。 - pilcrow
尽管@pilcrow的评论,我提供了一个BSD答案以及解决GNU或BSD的方法。另外请注意,GNU不需要Linux内核,也不总是意味着Linux发行版。我添加了“gnu”标签,并将“unix”标签返回,同时保留“linux”。 - Adam Katz
2个回答

16

请提供您想要提取两天的日期:

$ date -d "2016-12-31 2 days ago" +%Y-%m-%d
2016-12-29

语法上稍微好一点:

$ date -d "2016-12-31 -2 days" +%Y-%m-%d
2016-12-29

2
@cheng 确实,它非常强大!但请注意,有时候它的工作方式可能会有些奇怪:如果第一个参数不是1,则日期处理会出现错误 - fedorqui

2
如果您没有GNU日期工具,也许您有BSD日期。在这种情况下,您可以这样做:
date -jf %s $(( $(date +%s) - 86400 * 2 ))

请注意,这不是 POSIX,也不是 GNU。
这告诉 BSD date 不要更改时钟(-j),并将输入视为 UNIX 时间(1970/01/01 00:00:00 UTC 后的秒数,-f %s)。提供的时间是当前时间的数学替换($(date +%s),使用相同的格式但作为输出),减去一天的秒数(86400)乘以二。
我过去编写过查找 GNU 然后转向 BSD 的代码:
# Usage: _epoch2datefmt UNIX_TIME [+FORMAT]
if date -d @1484850180 +%s >/dev/null 2>&1
  then _epoch2datefmt() { date -d @"$@"; }    # GNU
  else _epoch2datefmt() { date -jf %s "$@"; } # BSD
fi

对于这个问题,我们还需要反向进行操作。BSD日期也需要这种格式:
# Usage: _date2epoch YYYY-MM-DD [INPUT_FORMAT]
if date -d 2017-01-19 +%s >/dev/null 2>&1
  then _yyyymmdd2epoch() { date -d "$1" +%s; }       # GNU
  else _yyyymmdd2epoch() { date -jf "$2" "$1" +%s; } # BSD
fi

以下是如何使用GNU或BSD计算给定日期前两天的方法:
_epoch2datefmt $(( $(_date2epoch 2016-12-31 %Y-%m-%d) - 2 * 86400 )) +%Y-%m-%d

从内部到外部,这将2016年12月31日转换为UNIX时间,减去两天(一天有86400秒),然后将结果UNIX时间(答案)传递回YYYY-MM-DD格式进行转换。
如果你希望将所有内容放在一个函数中,那么代码可能是这样的:
# Usage: _date_helper INPUT_FORMAT DATE [+OUTPUT_FORMAT]
if date -d@1484850180 +%s >/dev/null 2>&1
  then _date_helper() { local A=; [ "$1" = %s ] && A=@; shift; date -d "$A$@"; }
  else _date_helper() { date -jf "$@"; }
fi

“我必须使用一些巧妙的方法来处理输入UNIX时间(如第一个参数所示的%s),因为GNU的date -d需要一个前导符号@。如果必要,这将保存该前缀在$A中,清除格式(GNU很聪明,不需要它),然后将其余部分传递给GNU date命令。BSD版本应该是不言自明的。”
“以下是如何运行此函数以获取“2016年12月31日前两天”的方法:”
_date_helper %s $(( $(_date_helper %Y-%m-%d 2016-12-31 +%s) - 86400 * 2)) +%Y-%m-%d

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接