使用日期和 bash 减去时间

使用日期和 bash 减去时间

SE 网络上的所有其他问题都涉及假设日期为now) 或仅指定日期的情况 ()。

我想要做的是提供一个日期和时间,然后从中减去一个时间。
这是我首先尝试的:

date -d "2018-12-10 00:00:00 - 5 hours - 20 minutes - 5 seconds"

结果是2018-12-10 06:39:55- 增加了 7 个小时。然后减去20:05分钟。

man阅读了的 和info页后date,我想我已经解决了这个问题:

date -d "2018-12-10T00:00:00 - 5 hours - 20 minutes - 5 seconds"

但是,同样的结果。 7个小时从哪里来?

我也尝试了其他日期,因为我想那天可能有 7200 闰秒,谁知道呢,哈哈。但结果相同。

再举几个例子:

$ date -d "2018-12-16T00:00:00 - 24 hours" +%Y-%m-%d_%H:%M:%S
2018-12-17_02:00:00

$ date -d "2019-01-19T05:00:00 - 2 hours - 5 minutes" +%Y-%m-%d_%H:%M:%S
2019-01-19_08:55:00

但这里就变得有趣了。如果我省略输入时间,它工作正常:

$ date -d "2018-12-16 - 24 hours" +%Y-%m-%d_%H:%M:%S
2018-12-15_00:00:00

$ date -d "2019-01-19 - 2 hours - 5 minutes" +%Y-%m-%d_%H:%M:%S
2019-01-18_21:55:00

$ date --version
date (GNU coreutils) 8.30

我缺少什么?

更新:Z我在最后添加了一个,它改变了行为:

$ date -d "2019-01-19T05:00:00Z - 2 hours" +%Y-%m-%d_%H:%M:%S
2019-01-19_04:00:00

但我还是很困惑。这方面的内容并没有太多GNU 信息页面关于日期。

我猜这是一个时区问题,但引用日历维基ISO 8601:

如果没有给出带有时间表示的 UTC 关系信息,则假定时间为当地时间。

这就是我想要的。我的当地时间也设置正确。我不确定为什么在我提供日期时间并想要从中减去一些内容的简单情况下,日期会与时区混淆。难道不应该先从日期字符串中减去小时吗?即使它确实先将其转换为日期然后进行减法,如果我省略任何减法,我就会得到我想要的:

$ date -d "2019-01-19T05:00:00" +%Y-%m-%d_%H:%M:%S
2019-01-19_05:00:00

所以如果这确实是一个时区问题,这种疯狂从何而来?

答案1

最后一个例子应该已经为你澄清了一些事情:时区

$ TZ=UTC date -d "2019-01-19T05:00:00Z - 2 hours" +%Y-%m-%d_%H:%M:%S
2019-01-19_03:00:00
$ TZ=Asia/Colombo date -d "2019-01-19T05:00:00Z - 2 hours" +%Y-%m-%d_%H:%M:%S 
2019-01-19_08:30:00

由于输出明显因时区而异,我怀疑在没有指定时区的情况下对时间字符串采取了一些不明显的默认值。测试了几个值,似乎是UTC-05:00,虽然我不确定那是什么。

$ TZ=UTC date -d "2019-01-19T05:00:00 - 2 hours" +%Y-%m-%d_%H:%M:%S%Z
2019-01-19_08:00:00UTC
$ TZ=UTC date -d "2019-01-19T05:00:00Z - 2 hours" +%Y-%m-%d_%H:%M:%S%Z
2019-01-19_03:00:00UTC
$ TZ=UTC date -d "2019-01-19T05:00:00" +%Y-%m-%d_%H:%M:%S%Z           
2019-01-19_05:00:00UTC

它仅在执行日期算术时使用。


看来这里的问题- 2 hours不是视为算术,但作为时区说明符:

# TZ=UTC date -d "2019-01-19T05:00:00 - 2 hours" +%Y-%m-%d_%H:%M:%S%Z --debug
date: parsed datetime part: (Y-M-D) 2019-01-19 05:00:00 UTC-02
date: parsed relative part: +1 hour(s)
date: input timezone: parsed date/time string (-02)
date: using specified time as starting value: '05:00:00'
date: starting date/time: '(Y-M-D) 2019-01-19 05:00:00 TZ=-02'
date: '(Y-M-D) 2019-01-19 05:00:00 TZ=-02' = 1547881200 epoch-seconds
date: after time adjustment (+1 hours, +0 minutes, +0 seconds, +0 ns),
date:     new time = 1547884800 epoch-seconds
date: timezone: TZ="UTC" environment value
date: final: 1547884800.000000000 (epoch-seconds)
date: final: (Y-M-D) 2019-01-19 08:00:00 (UTC)
date: final: (Y-M-D) 2019-01-19 08:00:00 (UTC+00)
2019-01-19_08:00:00UTC

因此,不仅没有进行任何算术运算,而且似乎还对时间进行了夏令时1 小时的调整,导致我们的时间有些荒谬。

这也适用于加法:

# TZ=UTC date -d "2019-01-19T05:00:00 + 5:30 hours" +%Y-%m-%d_%H:%M:%S%Z --debug
date: parsed datetime part: (Y-M-D) 2019-01-19 05:00:00 UTC+05:30
date: parsed relative part: +1 hour(s)
date: input timezone: parsed date/time string (+05:30)
date: using specified time as starting value: '05:00:00'
date: starting date/time: '(Y-M-D) 2019-01-19 05:00:00 TZ=+05:30'
date: '(Y-M-D) 2019-01-19 05:00:00 TZ=+05:30' = 1547854200 epoch-seconds
date: after time adjustment (+1 hours, +0 minutes, +0 seconds, +0 ns),
date:     new time = 1547857800 epoch-seconds
date: timezone: TZ="UTC" environment value
date: final: 1547857800.000000000 (epoch-seconds)
date: final: (Y-M-D) 2019-01-19 00:30:00 (UTC)
date: final: (Y-M-D) 2019-01-19 00:30:00 (UTC+00)
2019-01-19_00:30:00UTC

进一步调试,解析似乎是:(时区)和( 2019-01-19T05:00:00 - 2= 1 小时),并隐含添加。如果您使用分钟来代替,则更容易查看:-2hours

# TZ=UTC date -d "2019-01-19T05:00:00 - 2 minutes" +%Y-%m-%d_%H:%M:%S%Z --debug
date: parsed datetime part: (Y-M-D) 2019-01-19 05:00:00 UTC-02
date: parsed relative part: +1 minutes
date: input timezone: parsed date/time string (-02)
date: using specified time as starting value: '05:00:00'
date: starting date/time: '(Y-M-D) 2019-01-19 05:00:00 TZ=-02'
date: '(Y-M-D) 2019-01-19 05:00:00 TZ=-02' = 1547881200 epoch-seconds
date: after time adjustment (+0 hours, +1 minutes, +0 seconds, +0 ns),
date:     new time = 1547881260 epoch-seconds
date: timezone: TZ="UTC" environment value
date: final: 1547881260.000000000 (epoch-seconds)
date: final: (Y-M-D) 2019-01-19 07:01:00 (UTC)
date: final: (Y-M-D) 2019-01-19 07:01:00 (UTC+00)
2019-01-19_07:01:00UTC

所以,日期算术正在完成,但不是我们要求的。  ̄\(ツ)/ ̄

答案2

当您首先将输入日期转换为 ISO 8601 时,它可以正常工作:

$ date -d "$(date -Iseconds -d "2018-12-10 00:00:00") - 5 hours - 20 minutes - 5 seconds"
So 9. Dez 18:39:55 CET 2018

答案3

GNUdate确实支持简单的日期算术,尽管 @sudodus 的答案中所示的纪元时间计算有时更清晰(并且更便携)。

当时间戳中没有指定时区时使用 +/- 会在解析其他内容之前触发接下来尝试匹配时区。

这是一种方法,使用“ago”而不是“-”:

$ date -d "2018-12-10 00:00:00 5 hours ago 20 minutes ago 5 seconds ago"
Sun Dec  9 18:39:55 GMT 2018

或者

$ date -d "2018-12-10 00:00:00Z -5 hours -20 minutes -5 seconds"
Sun Dec  9 18:39:55 GMT 2018

(虽然您不能任意使用“Z”,但它在我的区域中有效,但这使其成为 UTC/GMT 区域时间戳 - 使用您自己的区域,或通过附加到${TZ:-$(date +%z)}时间戳来代替 %z/%Z。)

添加这些形式的额外时间项调整时间:

  • “5小时前”减去5小时
  • “4小时”添加(隐式)4小时
  • “3 小时后”添加(显式)3 小时(旧版本不支持)

可以使用任何顺序的许多复杂调整(尽管像“上周一之后 14 周”这样的相对和可变术语会带来麻烦;-)

(这里还有另一个小陷阱,date总是会给出有效日期,因此date -d "2019-01-31 1 month"给出 2019-03-03,就像“下个月”一样)

考虑到支持的时间和日期格式多种多样,时区解析必然是马虎的:它可以是单个或多字母后缀、小时或小时:分钟偏移量、名称“America/Denver”(甚至是文件名)在变量的情况下TZ)。

您的2018-12-10T00:00:00版本不起作用,因为“T”只是一个分隔符,而不是时区,在末尾添加“Z”也可以按预期工作(取决于所选区域的正确性)。

看: https://www.gnu.org/software/tar/manual/html_node/Date-input-formats.html 特别是第 7.7 节。

答案4

这个解决方案很容易理解,但稍微复杂一些,所以我将其展示为 shellscript。

  • 转换为“自 1970-01-01 00:00:00 UTC 以来的秒数”
  • 添加或减去差值
  • date使用最终命令行转换回人类可读的格式

外壳脚本:

#!/bin/bash

startdate="2018-12-10 00:00:00"

ddif="0"          # days
diff="-5:-20:-5"  # hours:minutes:seconds

#-----------------------------------------------------------------------------

ss1970in=$(date -d "$startdate" "+%s")  # seconds since 1970-01-01 00:00:00 UTC
printf "%11s\n" "$ss1970in"

h=${diff%%:*}
m=${diff#*:}
m=${m%:*}
s=${diff##*:}
difs=$(( (((ddif*24+h)*60)+m)*60+s ))
printf "%11s\n" "$difs"

ss1970ut=$((ss1970in + difs))  # add/subtract the time difference
printf "%11s\n" "$ss1970ut"

date -d "@$ss1970ut" "+%Y-%m-%d %H:%M:%S"

相关内容