如何使用 GNU 工具计算和格式化日期持续时间,而不是结果日期?

如何使用 GNU 工具计算和格式化日期持续时间,而不是结果日期?

到目前为止,我发现的所有大量文章似乎都集中在获取结果日期,这仍然有用,但不是我在这种情况下想要的。

示例链接:Unix & Linux SE - 快速计算日期差异

datediff其他问答中,这更多的是我想要引用的日期/时间持续时间结果,它本质上使用 Unix 时间戳数学与前面的转换,但我希望我的粒度降到秒,这就是为什么我不' t 除以 86400。

有了可用的秒数持续时间,我现在想将其格式化为类似使用 GNUdate命令的方式:date -d "@69600" "+ %Y years %m months %e days %H:%M:%S"

我意识到一切本质上都是 Unix 纪元的日期/时间持续时间,但现在我遇到的问题是日、月和年数字不从 0 开始,这不能正确表示我想要的持续时间值。

我可以开始剪切这种格式并应用更多expr命令来从每个值中减去 1 或 1970,但我想知道除了链接数学之外是否还有其他更简单的方法来处理日期/时间计算以实现持续时间结果和格式化步骤在一起。也许 GNU 中还有其他date我可以利用的选项,或者另一个可以接受 2 个日期/时间参数并立即给出我正在寻找的结果的工具。

如果能在 Mac 版 Homebrew 中使用它,那就太好了:)。

简单的日期数学链接:Walker News - Linux Shell 脚本中的日期算术

随机日期格式化链接:

答案1

日期工具datediff(抱歉,不是 GNU),(以前ddiffdateutils.ddiff在 Debian 上):

$ dateutils.ddiff -f '%Y years, %m months, %d days, %H:%0M:%0S' \
    '2012-01-23 15:23:01' '2017-06-01 09:24:00'
5 years, 4 months, 8 days, 18:00:59

(日期采用 UTC,添加类似于--from-zone=Europe/London相应时区中的本地时间的日期。--from-zone=localtime可能适用于系统中的默认时区;--from-zone="${TZ#:}"只要$TZ指定 IANA 时区文件的路径(例如TZ=:Europe/London,不是TZ=GMT0BST,M3.5.0/1:00:00,M10.5.0/2:00:00POSIX 风格的 TZ 规范))。

ast-开放date,所以仍然不能使用 GNU 工具,抱歉(如果用作ksh93shell,则date可以作为内置工具使用),您可以使用date -E给出 2 个带单位的数字的格式来获取两个日期之间的差异:

$ date -E 1970-01-01 2017-06-01
47Y04M
$ date -E 2017-01-01 2017-06-01
4M29d
$ date -E 12:12:01 23:01:43
10h49m

请注意以上任何内容小时是不明确的,因为在采用 DST(23、24 或 25)的区域中,天的小时数不同,而月份和年份的天数也不同,因此比上述两个单位更精确可能没有多大意义。

例如,2015-01-01 00:00:00 和 2016-01-01 00:00:00 之间或 2016-01-01 00:00:00 和 2017-01-01 00 之间正好有一年: 00:00,但这不是相同的持续时间(一种情况是 365*24 小时,另一种情况是 366*24 小时)

2017-03-24 12:00:00 和 2017-03-25 12:00:00 之间或 2017-03-25 12:00:00 和 2017-03-26 12:00:00 之间正好有一天,但当这些是欧洲国家当地时间(于2017年3月26日切换到夏令时)时,一天一种情况是 24 小时,另一种情况是 23 小时。

换句话说,提及年、月、周或日的持续时间仅在与其要应用的边界之一(和时区)相关联时才有意义。因此,如果不知道要应用的时间(从或到),则无法将秒数转换为这样的持续时间(除非您想使用日(24 小时)、月(30*24 小时)或年(365*24 小时))。

在某种程度上,即使以秒为单位的持续时间也是不明确的。为了简单起见,Unix 纪元时间中的秒被定义为给定地球日1的第 86400 部分。随着地球自转速度减慢,这些秒数变得越来越长。

所以类似(使用 GNU date):

d1='2016-01-01 00:00:00' d2='2017-01-01 00:00:00'
eval "$(date -d "@$(($(date -d "$d2" +%s) - $(date -d "$d1" +%s)))" +'
  delta="$((%-Y - 1970)) years, $((%-m - 1)) months, $((%-d - 1)) days, %T"')"
echo "$delta"

或者在fish

date -d@(expr (date -d $d2 +%s) - (date -d $d1 +%s)) +'%Y %-m %-d %T' | \
  awk '{printf "%s years, %s months, %s days, %s\n", $1-1970, $2-1, $3-1, $4, $5}'

通常不会为您提供正确的信息,因为时间增量适用于 1970 年,而不是实际开始日期 2016 年。

例如,上面给出了我(在欧洲/伦敦时区):

1 years, 0 months, 1 days, 01:00:00

代替

1 years, 0 months, 0 days, 00:00:00

(这对你来说可能仍然是一个足够好的近似值)。


1从技术上讲,虽然一天有 86400 Unix 秒长,但网络连接系统通常使用 SI 秒将其时钟与原子钟同步。当原子钟显示 12:00:00.00 时,那些 Unix 系统也会显示 12:00:00.00。只有在引入闰秒时例外,其中要么有一个 Unix 秒持续 2 秒,要么有几秒持续更长的时间,以便将额外的秒涂抹在更长的时间段上。

因此,可以知道两个 Unix 时间戳(由与原子钟同步的系统发布)之间的确切持续时间(以原子秒为单位):获取两个时间戳之间的 Unix 纪元时间差并添加闰数之间添加的秒数。

datediff还有一个-f %rS获取数量真实的两个日期之间的秒数:

$ dateutils.ddiff -f %S '1990-01-01 00:00:00' '2010-01-01 00:00:00'
631152000
$ dateutils.ddiff -f %rS '1990-01-01 00:00:00' '2010-01-01 00:00:00'
631152009

差异在于 1990 年至 2010 年期间增加了 9 个闰秒。

现在,这个数量真实的秒数不会帮助您计算天数,因为天数没有恒定的数量真实的秒。这 2 个日期之间正好有 20 年的时间,而这 9 个闰秒会妨碍达到该值。另请注意,仅在不与其他格式说明符组合时才有效ddiff%rS此外,未来的闰秒无法提前得知,而且最近的闰秒信息也可能无法获得。例如,我的系统不知道 2012-07-01 之后的内容。

答案2

好的,假设您有两个date兼容的字符串,定义了您想要计算其间持续时间的两个时间点:

echo $(($(date +%s -d "3 days ago")-$(date +%s -d "4 weeks 2 hours 1 minutes 33 seconds ago"))) | sed 's/-//'

但我们进一步说,您希望它更具可读性:

convertsecs() {
    hours=$(($1/3600))
    minutes=$(($1%3600/60))
    seconds=$(($1%60))
}
totalduration="$(($(date +%s -d "3 days ago")-$(date +%s -d "4 weeks 2 hours 1 minutes 33 seconds ago"))) | sed 's/-//'"
convertsecs "$totalduration"
echo "Duration was ${hours} hours, ${minutes} minutes, and ${seconds} seconds."

答案3

时间是一件复杂的事情,在计算时间的同时管理你的期望非常重要。我认为在一般情况下,在技术上不可能准确而精确地执行您所要求的操作,因为一旦您考虑诸如闰秒之类的事情,给定的秒数可能对应于不同的分钟数,而闰秒本身不会发生在完全可预测的基础上。

换句话说,虽然我们说一天是 24 小时,但不是每一个一天正好是 24 小时,所以如果你想知道给定的持续时间有多少天加分钟加秒,你需要将该持续时间实时锚定在某个地方,以便知道其中是否有闰秒,或者即使 4 月 29 日是否存在于该持续时间的某个地方。

更奇特的是,并非每个月都有相同的天数。

如果这不是问题,只需通过基本算术计算秒数即可;用于date转换为自纪元以来的秒数,除以 86400 得到天数,余数除以 3600 得到小时数,余数得到分钟数,依此类推。

相关内容