2015 年 6 月 30 日闰秒期间 Amazon linux AMI 上的日历操作 运行

Calendar manipulation running on Amazon linux AMI during June 30, 2015 leap second

环境:Java 7 on Amazon Linux AMI(已修补)。注意本环境使用NTP

我们的一部分代码定期执行预定任务。通常,执行的确切时间并不是特别重要。但是,我们有类似的逻辑用于计算报告的间隔。报告期预计将在精确的时间范围内。

示例代码:

protected Calendar increment(ScheduledPeriodEnum scheduledPeriod, Calendar date) {
    Calendar next = CalendarUtils.getCalendar();
    next.setTimeInMillis(date.getTimeInMillis());
    switch (scheduledPeriod) {
    case ONE_DAY:
        next.add(Calendar.DAY_OF_MONTH, 1);
        break;
    case ONE_HOUR:
        next.add(Calendar.HOUR_OF_DAY, 1);
        break;
    case FIFTEEN_MINUTE:
        next.add(Calendar.MINUTE, 15);
        break;
    case ONE_MONTH:
        next.add(Calendar.MONTH, 1);
        break;
    default:
        throw new RuntimeException("Unhandled case: " + scheduledPeriod);
    }
    return next;
}

我们将时间保存为 unix 时间戳(长)值。 Calendar 个实例都在 UTC 时区。

据我们了解,Java 7 的日历实现不考虑闰秒。我们也相信 NTP 会更新 OS 时钟。

我们知道亚马逊 2012 年闰秒的崩溃。我们正与他们直接沟通,了解他们的运营准备情况。

具体问题:

  1. 如果我们 increment 日历在闰秒之前的小时边界上,它会在闰秒之后的小时边界上吗?还是整点前一秒?

  2. 我们应该如何test/validate这段代码如何跨越闰秒边界运行?被黑的 NIST leapfile 是个好方法吗?

  3. 有没有更合适的pattern/implementation来回避闰秒问题?

我不会太担心 2015 年的下一个闰秒 并假设 Linux-团队在此期间做了很多工作来解决任何问题关于闰秒处理代码,另请参阅 Linus Torvalds 的有趣采访。如果 linux 软件再次出错,那么你只能重新启动 Java 程序(这里 Java 只是后端)

现在让我们考虑一下其他情况。关于完全不知道任何闰秒的代码,如 java.util.Datejava.util.Calendar 的内容,请记住,此类代码只能看到 OS 时钟提供的内容。大多数 OS 只提供 UNIX 时间戳。如果它们与 NTP 同步,那么还要记住 NTP 时间戳不计算 NTP 协议中指定的闰秒。他们只是重复相同的时间戳。 Windows 可能会在稍后的任何时间触发时钟跳转,而 Linux 内核会尝试更精确,应用一些闰秒处理代码并立即操纵系统时钟。无论如何,OS 都只提供 POSIX 类时间戳。 Java 什么也没看到。

这也回答了您的第一个具体问题:一个 GregorianCalendar-对象将在增加一个小时后保持在一个小时的边界上。

关于你的第二个问题:

这里只是一秒钟的问题。但可能更麻烦的是,本地时钟甚至可能有几分钟的错误(然后在与 NTP 时钟同步后突然跳转)。后一种行为随时可能发生,而不仅仅是在 2015-06-30 结束时。所以我假设你真正的问题是时钟的单调性。这通常表明任何测试都应该使用类似可注入时钟机制的东西。例如,你可以编写一个 TimeSource 接口,它通过方法 public long currentTime() 产生任何 unix 时间戳(甚至可以通过计时器创建模拟跳转)然后在 JUnit-test 类 中使用此接口提供(假)时间并观察代码的行为。

ad 3.) 大多数人只会使用一个不知道任何闰秒的库,因为 POSIX 很容易理解和计算(尽管在涉及到这些时它是不正确的特别秒)。如果没有闰秒处理代码,这样的标准库中几乎不可能存在直接的问题。否则,如果您不愿意向用户隐藏闰秒,那么您也可以参考我在这个 SO-post.

中的回答

无论您的决定是什么,闰秒确实不足以选择合适的 library/implementation(线程安全、国际化等其他主题更为重要)。关于这些标准,旧的 Calendar-stuff 很糟糕。