在 Linux 上用 bash 获取昨天的日期,DST-safe。

我有一个运行在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小时,而在2013-03-11 0:35 CDT之前的24小时是2013-03-09 23:35 CST,这导致了"2013-03-09"的结果。

那么,在Linux上用bash获取昨天日期的dst安全的好方法是什么呢?

215401 次浏览

我认为这应该工作,不管你多久和什么时候运行它…

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

这应该也能起作用,但可能有点过头了:

date -d @$(( $(date +"%s") - 86400)) +"%Y-%m-%d"

如果您确定脚本在一天的头几个小时运行,那么您可以简单地这样做

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

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

这里的解决方案也适用于Solaris和AIX。

操作时区可以改变时钟几个小时。 由于是夏时制,24小时前可以是今天,也可以是前天

你确定昨天是二三十小时前。哪一个?最近的一次不是今天。

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

echo命令中使用的-e参数在bash中是必需的,但在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

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

在昨天

date -v-1d +%F

上周

date -v-1w +%F

你可以使用

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

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

只需使用date和可靠的秒:

正如您正确地指出的那样,如果您依赖于英语时间算术,那么关于底层计算的许多细节都是隐藏的。例如,-d yesterday-d 1 day ago将有不同的行为。

相反,你可以可靠地依赖(精确记录的)自unix epoch UTC的秒数,以及bash算术来获得你想要的时刻:

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

这是在另一个回答中指出的。这种形式在不同的date命令行标志的平台上更易于移植,是语言独立的(例如。“yesterday”和“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 ))

或者,等价地使用一个temp变量(外部子shell可选,但保持环境变量干净)。

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

注意:尽管manpage声明没有%()T格式化器的参数将假设默认的-1,但我似乎得到了一个0(谢谢,bash手动版本4.3.48)

date -d "yesterday" '+%Y-%m-%d'

稍后使用:

date=$(date -d "yesterday" '+%Y-%m-%d')

因为这个问题被标记为 “DST安全”

并且使用fork to date命令隐含延迟,有一个简单而更有效的方法使用纯bash内置:

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

这比必须< em >叉< / em >date更快,更系统友好。

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

printf -v tz '%(%z)T' -1
TZ=$tz printf -v yesterday '%(%Y-%m-%d)T' $(( EPOCHSECONDS - 86400 ))
echo $yesterday

你可以使用:

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

或者你想检索的任何时间都将通过bash检索。

月:

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