n 天前从命令行上的给定日期开始

n days ago from a given date on command line

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

我调查 date 命令并从当前日期开始 n 天前很容易:
date -d "2 days ago" +%Y-%m-%d

只需提及要从中提取两天的日期:

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

或者更好一点grammatically-wise:

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

如果您没有 GNU 日期,也许您有 BSD date。在这种情况下,您可以这样做:

date -jf %s $(( $(date +%s) - 86400 * 2 ))

请注意,这不是 POSIX,也不是 GNU。

这告诉 BSD date 不要更改时钟 (-j) 并将输入作为 UNIX time(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 "" +%s; }       # GNU
  else _yyyymmdd2epoch() { date -jf "" "" +%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=; [ "" = %s ] && A=@; shift; date -d "$A$@"; }
  else _date_helper() { date -jf "$@"; }
fi

(由于 GNU 的 date -d 需要一个前导 @,因此我在处理 UNIX 时间输入时不得不有点技巧(如第一个参数 %s 所示)。如有必要,将该前缀保存在 $A 中,清除格式(GNU 很聪明,否则不需要它),然后将其余部分传递给 GNU date 命令。BSD 版本应该是 self-explanatory.)

以下是如何 运行 使用此函数获得 "two days before 2016-12-31" 的方法:

_date_helper %s $(( $(_date_helper %Y-%m-%d 2016-12-31 +%s) - 86400 * 2)) +%Y-%m-%d