在 shell 中获取纪元时间戳的 POSIX 兼容方法是什么?

在 shell 中获取纪元时间戳的 POSIX 兼容方法是什么?

我刚刚注意到,POSIXdate似乎没有%s%N格式项。所以我不能使用那些。在我的 shell 脚本中获取纪元时间戳的替代方法是什么?

答案1

对于整数秒的纪元时间,则为:

awk 'BEGIN{srand(); print srand()}'

或者:

awk 'BEGIN{print srand(srand())}'

作为在 POSIX 中awksrand()不带参数使用当前时间作为伪随机生成器的种子。它还返回前一个种子,因此srand()上面的第二个返回用于前一个种子的纪元时间。

您可以通过以下方式获得小数部分:

echo|LC_ALL=C TZ=UTC0 diff -u /dev/null - |sed -n '2s/.*\(\.[0-9]*\).*/\1/p'

POSIX 确实指定了输出格式diff -u,并且当文件为 时使用当前时间-

但是,自从您在获得输出之前调用以来,可能已经过去了数千甚至数百万纳秒awk,甚至可能不是同一秒。但是,您可以通过<epochtime> % 60与以下内容进行比较来检查情况是否如此第二diff如果您愿意的话,可以将其作为标头中 (UTC) 时间戳的一部分。


1 关于这个awk解决方案,请注意 POSIX 过去并没有用太多的语言来表达它。我做到了提出异议不久前向 POSIX 询问了不明确的措辞,还指出在当今时代强制实现使用这种可怜的熵源是不合理的。相反,解决是明确要求srand()使用纪元时间。

答案2

编写一个 C 程序来调用time()并打印结果。借用示例程序time()函数的规范,我们称之为例如seconds.c

#include <stdio.h>
#include <stdint.h>
#include <time.h>

int main(void)
{
    time_t t = time(NULL);
    printf("%ju\n", (uintmax_t) t);
    return 0;
}

并用 进行编译c99 -o seconds seconds.c。 (如果我读到规格正确的是,这应该是编译它的标准方法。)


或者,您可以获取分解的日期并计算秒数。这相对容易,因为每一天的长度都是 86400 秒。我们确实需要去掉某些值的前导零,这样 shell 算术就不会将它们视为八进制:

作为外壳函数:

epochtime() {
    eval "$(date -u +'y=%Y j=%j h=%H m=%M s=%S')"
    s=${s#0}
    m=${m#0}
    h=${h#0}
    j=${j#0}
    j=${j#0}
    t=$(( ((y-1970)*365 + (y-1969)/4 + j - 1)*86400 + h*3600 + m*60 + s))
    echo "$t"
}

关于闰年计算的注意事项:纪元后的第一个闰年是 1972 年,并且 (1972-1969)/4 = 3/4 = 0 ,而 (1973-1969)/4 = 4/4 = 1 (依赖于整数截断)。因此 (year-1969)/4 给出了闰年的数量本年度。今年可能的闰日包含在%j值“一年中的某一天”中。该数字从 1 开始计数,但我们想要的是经过的完整天数,因此表达式中需要负一。

答案3

您可以使用自定义date格式字符串来完成此操作,该字符串为自纪元以来的秒数内的 POSIX 公式生成 shell 算术表达式:

secs=$((`TZ=GMT0 date \
+"((%Y-1600)*365+(%Y-1600)/4-(%Y-1600)/100+(%Y-1600)/400+1%j-1000-135140)\
*86400+(1%H-100)*3600+(1%M-100)*60+(1%S-100)"`))

该方法取自https://www.etalabs.net/sh_tricks.html

相关内容