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

fin*_*oot 20 shell-script date posix timestamps time

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

Sté*_*las 31

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

\n
awk \'BEGIN{srand(); print srand()}\'\n
Run Code Online (Sandbox Code Playgroud)\n

或者:

\n
awk \'BEGIN{print srand(srand())}\'\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n

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

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

POSIX 确实指定了 的输出格式diff -u,并且当文件为-.

\n

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

\n
\n

\xc2\xb9 关于该awk解决方案,请注意 POSIX 过去并没有用那么多话来表达它。不久前我确实对 POSIX 的措辞不明确提出了反对,并指出在当今时代强制实现使用这种可怜的熵源是不合理的。相反,决议明确要求srand()使用纪元时间。

\n

  • 好吧,我没想到会这样:D (20认同)
  • @StéphaneChazelas 一方面,这当然是一个......富有想象力的解决方案。但另一方面,我希望你不要介意我这么说,它*绝对是丑陋的!*我,我永远不会使用它;太明显的是(尽管有像您提到的那样的痛苦讨论)它可能不会在任何地方或永远有效。 (5认同)
  • @ilkkachu 另请参阅 https://www.austingroupbugs.net/view.php?id=983#c3281 以获得更复杂的替代方案。 (4认同)
  • @StéphaneChazelas:哇哦,太棒了!不过,它似乎确实规定它只是 32 位,这使得这种方法已经停产。 (2认同)

ilk*_*chu 13

编写一个 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;
}
Run Code Online (Sandbox Code Playgroud)

并用 进行编译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"
}
Run Code Online (Sandbox Code Playgroud)

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


R..*_*ICE 7

您可以使用自定义格式字符串来完成此操作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)"`))
Run Code Online (Sandbox Code Playgroud)

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