获得昨天';Linux上bash中的s日期,DST安全

获得昨天';Linux上bash中的s日期,DST安全,linux,bash,Linux,Bash,我有一个在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天前”的实施方式是减去24小时,而2013-03-11 0:35之前的24小时CDT是2013-03-09 23:35 CST,这导致了“20

我有一个在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天前”
的实施方式是减去24小时,而
2013-03-11 0:35之前的24小时CDT
2013-03-09 23:35 CST
,这导致了
“2013-03-09”
的结果


那么,在Linux上的bash中,哪种DST安全的好方法可以获取昨天的日期呢?

我认为这应该是可行的,不管您运行它的频率和时间如何

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小时前可以是今天或前天

你肯定昨天是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
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 -v-1d +%F
上周

date -v-1w +%F
你可以用

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

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

只需使用
date
和trusty seconds:

date=$(date -d "yesterday" '+%Y-%m-%d')
正如您正确地指出的,如果您依赖于英国时间算法,那么很多关于底层计算的细节都是隐藏的。例如,
-d昨天
,和
-d 1天前
将有不同的行为

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

date -d @$(( $(date +"%s") - 24*3600)) +"%Y-%m-%d"
另一份报告指出了这一点。此表单在具有不同
日期
命令行标志的平台上更易于移植,与语言无关(例如法语区域设置中的“昨天”与“hier”),坦率地说(从长远来看)更容易记住,因为您已经知道了。否则,你可能会问自己:“我想要的是2小时前的
-d
还是2小时前的
-d
还是昨天的
-d
还是1天前的
-d?”)。这里唯一棘手的是
@

用猛击和其他武器武装:

date=$(date -d "yesterday" '+%Y-%m-%d')
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.
所以

#internal printf以秒为单位获取当前unix时间
#外部printf根据格式将其吐出
printf“%(%Y-%m-%d)T\n”$($(printf“%(%s)T“-1)-24*3600))
或者,与temp变量等效(外部子shell可选,但保持环境变量干净)

(
现在=$(printf“%(%s)T“-1);
printf“%(%Y-%m-%d)T\n”$(现在为-24*3600);
)
注意:尽管manpage声明
%()T
格式化程序的任何参数都不会假定默认值
-1
,但我似乎得到了一个0(谢谢,bash手册版本4.3.48)

稍后使用此选项:

date=$(date -d "yesterday" '+%Y-%m-%d')
由于此问题标记为“DST安全” 使用fork to
date
命令意味着延迟,使用pure bash内置有一种简单而高效的方法:

printf -v tznow '%(%z %s)T' -1
TZ=${tznow% *} printf -v yesterday '%(%Y-%m-%d)T' $(( ${tznow#* } - 86400 ))
echo $yesterday
在系统友好的情况下,这比将fork转换为
日期要快得多

从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'

您是否总是在同一时间运行它,是否重复运行?@tink它每天00:35运行。如果您有兴趣在OSX中使用GNU样式,也可以使用
gdate
,在自制的
coreutils
软件包中提供。您的意思是
date
不同。您将如何将其分配给变量以供以后使用?@null-与分配任何其他shell命令的输出相同。变量=$(date-d“昨天13:00”+%Y-%m-%d')…对于GNU日期:
date-d昨天13:00-I
这取决于这样一个事实,即如果我理解正确,夏季和冬季之间的切换总是在夜间进行的?@Nicolasraul:这不是保证;但将这项活动安排在清晨是一个广受欢迎的惯例。另一方面,IIRC没有一个地方会在接近当地中午时发生切换。因此,“1300J永远不在DST边界上”是一个合理的假设。如果您需要处理Mac OS X
date
,它不支持
昨日
语法…您应该将格式更改为
%Y%m%d
,以匹配问题。无论如何,我投了更高的票,因为它工作正常。虽然这段代码可以解决OP的问题,但最好包含一个解释,说明您的代码如何解决OP的问题。这样,未来的访问者可以从您的帖子中学习,并将其应用到自己的代码中。因此,它不是一种编码服务,而是一种知识资源。高质量、完整的答案强化了这一观点,而且更有可能被投票支持。这些特性,再加上要求