使用日期和 bash 减去时间

con*_*tti 24 gnu date

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

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

date -d "2018-12-10 00:00:00 - 5 hours - 20 minutes - 5 seconds"
Run Code Online (Sandbox Code Playgroud)

这导致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"
Run Code Online (Sandbox Code Playgroud)

但是,同样的结果。它甚至从哪里得到 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
Run Code Online (Sandbox Code Playgroud)

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

$ 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
Run Code Online (Sandbox Code Playgroud)

我错过了什么?

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

$ date -d "2019-01-19T05:00:00Z - 2 hours" +%Y-%m-%d_%H:%M:%S
2019-01-19_04:00:00
Run Code Online (Sandbox Code Playgroud)

不过我还是很困惑。关于日期的GNU 信息页面中没有太多关于此的内容。

我猜这是一个时区问题,但在ISO 8601上引用The Calendar Wiki

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

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

$ date -d "2019-01-19T05:00:00" +%Y-%m-%d_%H:%M:%S
2019-01-19_05:00:00
Run Code Online (Sandbox Code Playgroud)

所以,如果这真的是一个时区问题,那么这种疯狂从何而来?

Olo*_*rin 25

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

$ 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
Run Code Online (Sandbox Code Playgroud)

由于输出明显因时区而异,我怀疑对没有指定时区的时间字符串采用了一些不明显的默认值。测试几个值,它似乎是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
Run Code Online (Sandbox Code Playgroud)

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


看来这里的问题是,- 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
Run Code Online (Sandbox Code Playgroud)

因此,不仅没有进行任何算术运算,时间上似乎还有夏令时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
Run Code Online (Sandbox Code Playgroud)

调试多一点,解析似乎是:2019-01-19T05:00:00 - 2-2作为时区),和hours(= 1小时),有一个隐含的加法。如果您使用分钟,则更容易查看:

# 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
Run Code Online (Sandbox Code Playgroud)

所以,好吧,日期算术正在完成,只是不是我们要求的。¯\(?)/¯

  • 可能是 `date` 中的一个可能的错误,根据 ISO 8601 标准,如果没有提供时区,则应假定本地时间(区),而在算术运算的情况下,则不是。不过对我来说很奇怪的问题。 (2认同)
  • 哇。现在不知何故是有道理的。好吧,至少它解释了它。非常感谢你弄清楚这一点。对我来说,听起来他们的解析器需要更新,但显然,对于这样的格式和空格,您不想通过“ - 2 小时”指定时区,这肯定会引起混乱。如果他们不想更新解析器,至少手册应该得到关于这一点的注释。 (2认同)
  • 今天我了解了 date 的 `--debug` 选项!很好的解释。 (2认同)

pLu*_*umo 7

当您首先将输入日期转换为 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
Run Code Online (Sandbox Code Playgroud)

  • 这是有效的,因为`date -I` 还输出时区说明符(例如`2018-12-10T00:00:00+02:00`),它修复了[Olorin 的回答](https://unix.stackexchange. com/a/497643/170373) (3认同)

mr.*_*tic 6

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
Run Code Online (Sandbox Code Playgroud)

或者

$ date -d "2018-12-10 00:00:00Z -5 hours -20 minutes -5 seconds"
Sun Dec  9 18:39:55 GMT 2018
Run Code Online (Sandbox Code Playgroud)

(虽然您不能随意使用“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 节。

  • 或者,为了彻底起见,通过重新排序参数,在日期时间之后不保留任何内容: date -d "-5 hours 2018-12-10 00:00:00"` (2认同)

nxn*_*nev 5

TLDR:这不是错误。您刚刚发现了date. 当执行运算时间date,使用时区无关的格式(如Unix时间)或阅读非常仔细的文档,以了解如何正确使用此命令。


GNUdate使用您的系统设置(TZ环境变量,如果未设置,则为系统默认值)来确定使用-d/--date选项提供的日期和+format参数报告的日期的时区。--date选项还允许您覆盖其自己的选项参数的时区,但它不会覆盖+format. 这就是混乱的根源,恕我直言。

考虑到我的时区是 UTC-6,比较以下命令:

$ date -d '1970-01-01 00:00:00' '+Normal: %F %T %:z%nUnix: %s'
Normal: 1970-01-01 00:00:00 -06:00
Unix: 21600
Run Code Online (Sandbox Code Playgroud)
$ date -d '1970-01-01 00:00:00 UTC' '+Normal: %F %T %:z%nUnix: %s'
Normal: 1969-12-31 18:00:00 -06:00
Unix: 0
Run Code Online (Sandbox Code Playgroud)
$ TZ='UTC0' date -d '1970-01-01 00:00:00 UTC' '+Normal: %F %T %:z%nUnix: %s'
Normal: 1970-01-01 00:00:00 +00:00
Unix: 0
Run Code Online (Sandbox Code Playgroud)

第一个将我的时区用于-d+format。第二个使用 UTC-d但我的时区为+format. 第三个对两者都使用 UTC。

现在,比较以下简单的操作:

$ date -d '1970-01-01 00:00:00 UTC +1 day' '+Normal: %F %T %:z%nUnix: %s'
Normal: 1970-01-01 18:00:00 -06:00
Unix: 86400
Run Code Online (Sandbox Code Playgroud)
$ TZ='UTC0' date -d '1970-01-01 00:00:00 UTC +1 day' '+Normal: %F %T %:z%nUnix: %s'
Normal: 1970-01-02 00:00:00 +00:00
Unix: 86400
Run Code Online (Sandbox Code Playgroud)

即使 Unix 时间告诉我同样的事情,“正常”时间也因我自己的时区而有所不同。

如果我想做同样的操作但只使用我的时区:

$ TZ='CST+6' date -d '1970-01-01 00:00:00 -06:00 +1 day' '+Normal: %F %T %:z%nUnix: %s'
Normal: 1970-01-02 00:00:00 -06:00
Unix: 108000
Run Code Online (Sandbox Code Playgroud)

  • Upvoted 提到纪元/Unix 时间,imo 是进行时间算术的唯一合理方法 (6认同)