192

我有一个在 Linux 上运行的 shell 脚本,并使用此调用以YYYY-MM-DD格式获取昨天的日期:

date -d "1 day ago" '+%Y-%m-%d'

它在大多数情况下都有效,但是当脚本在昨天早上运行时2013-03-11 0:35 CDT它返回"2013-03-09"而不是"2013-03-10".

据推测,夏令时(昨天开始)是罪魁祸首。我猜它的"1 day ago"实施方式减去了 24 小时,而 24 小时前2013-03-11 0:35 CDT2013-03-09 23:35 CST,这导致了"2013-03-09".

那么在 Linux 上以 bash 格式获取昨天日期的 DST 安全的好方法是什么?

4

10 回答 10

316

我认为这应该有效,无论您运行它的频率和时间如何......

date -d "yesterday 13:00" '+%Y-%m-%d'
于 2013-03-13T00:23:13.677 回答
77

Mac OSX下,日期的工作方式略有不同:

对于昨天

date -v-1d +%F

上周

date -v-1w +%F
于 2014-03-13T09:23:37.670 回答
18

这也应该有效,但也许太多了:

date -d @$(( $(date +"%s") - 86400)) +"%Y-%m-%d"
于 2013-03-13T00:24:25.637 回答
11

如果您确定脚本在一天的前几个小时运行,您可以简单地执行

  date -d "12 hours ago" '+%Y-%m-%d'

顺便说一句,如果脚本每天在 00:35 运行(通过 crontab?),您应该问问自己,如果 DST 更改在那一小时内会发生什么;脚本无法运行,或在某些情况下运行两次。不过,现代的实现在这方面cron非常聪明。

于 2013-03-13T00:30:25.243 回答
6
date -d "yesterday" '+%Y-%m-%d'

以后要使用它:

date=$(date -d "yesterday" '+%Y-%m-%d')
于 2020-01-19T13:20:04.367 回答
5

这是一个同样适用于 Solaris 和 AIX 的解决方案。

操纵时区可以在几个小时内更改时钟。由于夏令时,24 小时前可以是今天或前天。

你确定昨天是 20 或 30 小时前。哪一个?好吧,最近的一个不是今天。

echo -e "$(TZ=GMT+30 date +%Y-%m-%d)\n$(TZ=GMT+20 date +%Y-%m-%d)" | grep -v $(date +%Y-%m-%d) | tail -1

bash 需要 echo 命令中使用的 -e 参数,但不适用于 ksh。在 ksh 中,您可以使用不带 -e 标志的相同命令。

当您的脚本将在不同的环境中使用时,您可以使用#!/bin/ksh 或#!/bin/bash 启动脚本。您还可以将 \n 替换为换行符:

echo "$(TZ=GMT+30 date +%Y-%m-%d)
$(TZ=GMT+20 date +%Y-%m-%d)" | grep -v $(date +%Y-%m-%d) | tail -1
于 2014-01-22T12:55:43.933 回答
5

您可以使用

date -d "30 days ago" +"%d/%m/%Y"

要获取 30 天前的日期,同样您可以将 30 替换为 x 天数

于 2015-03-16T16:25:57.793 回答
1

只需使用date可靠的秒数:

正如您正确指出的那样,如果您依赖英语时间算术,则有关基础计算的许多细节都将被隐藏。例如-d yesterday, 并且-d 1 day ago会有不同的行为。

相反,您可以可靠地依赖自 unix 纪元 UTC 以来的(精确记录的)秒数,并使用 bash 算法来获得您想要的时刻:

date -d @$(( $(date +"%s") - 24*3600)) +"%Y-%m-%d"

另一个答案中指出了这一点。这种形式在具有不同命令行标志的平台上更便携,与date语言无关(例如,法语语言环境中的“昨天”与“hier”),坦率地说(从长远来看)更容易记住,因为好吧,你已经知道了。否则,您可能会不断问自己:“是这样-d 2 hours ago还是-d 2 hour ago再次?” 或“是我想要的吗?”)-d yesterday-d 1 day ago这里唯一棘手的地方是@.

用 bash 武装,别无其他:

仅在 bash 上进行 Bash,您还可以通过 printf 内置函数获取昨天的时间:

 %(datefmt)T
     causes printf to output the date-time string resulting from using
     datefmt as a format string for strftime(3).  The  corresponding  argu‐
     ment  is an integer representing the number of seconds since the
     epoch.  Two special argument values may be used: -1 represents the
     current time, and -2 represents the time the shell was invoked.
     If no argument is specified, conversion behaves as if -1 had
     been  given.
     This is an exception to the usual printf behavior.

所以,

# inner printf gets you the current unix time in seconds
# outer printf spits it out according to the format
printf "%(%Y-%m-%d)T\n" $(( $(printf "%(%s)T" -1) - 24*3600 ))

或者,等效地使用临时变量(外部子外壳可选,但保持环境变量清洁)。

(
  now=$(printf "%(%s)T" -1);
  printf "%(%Y-%m-%d)T\n" $((now - 24*3600));
)

注意:尽管手册页声明%()T格式化程序的任何参数都不会假定为默认值-1,但我似乎得到了一个 0 代替(谢谢,bash 手册版本 4.3.48)

于 2018-03-29T08:26:41.323 回答
1

因为这个问题被标记为 "DST safe"

并且使用 fork 命令来暗示延迟,有一种使用纯 bash内置date的简单且更有效的方法:

printf -v tznow '%(%z %s)T' -1
TZ=${tznow% *} printf -v yesterday '%(%Y-%m-%d)T' $(( ${tznow#* } - 86400 ))
echo $yesterday

这比必须分叉到更系统友好的方式要快得多date

V>=5.0开始,有一个新变量$EPOCHSECONDS

printf -v tz '%(%z)T' -1
TZ=$tz printf -v yesterday '%(%Y-%m-%d)T' $(( EPOCHSECONDS - 86400 ))
echo $yesterday
于 2020-05-18T07:14:07.540 回答
1

您可以使用:

date -d "yesterday 13:55" '+%Y-%m-%d'

或者您想要检索的任何时间都将由 bash 检索。

月份:

date -d "30 days ago" '+%Y-%m-%d'
于 2020-05-18T08:27:15.907 回答