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 年闰秒的崩溃。我们正与他们直接沟通,了解他们的运营准备情况。
具体问题:
如果我们 increment
日历在闰秒之前的小时边界上,它会在闰秒之后的小时边界上吗?还是整点前一秒?
我们应该如何test/validate这段代码如何跨越闰秒边界运行?被黑的 NIST leapfile 是个好方法吗?
有没有更合适的pattern/implementation来回避闰秒问题?
我不会太担心 2015 年的下一个闰秒 并假设 Linux-团队在此期间做了很多工作来解决任何问题关于闰秒处理代码,另请参阅 Linus Torvalds 的有趣采访。如果 linux 软件再次出错,那么你只能重新启动 Java 程序(这里 Java 只是后端)
现在让我们考虑一下其他情况。关于完全不知道任何闰秒的代码,如 java.util.Date
和 java.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 很糟糕。
环境: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 年闰秒的崩溃。我们正与他们直接沟通,了解他们的运营准备情况。
具体问题:
如果我们
increment
日历在闰秒之前的小时边界上,它会在闰秒之后的小时边界上吗?还是整点前一秒?我们应该如何test/validate这段代码如何跨越闰秒边界运行?被黑的 NIST leapfile 是个好方法吗?
有没有更合适的pattern/implementation来回避闰秒问题?
我不会太担心 2015 年的下一个闰秒 并假设 Linux-团队在此期间做了很多工作来解决任何问题关于闰秒处理代码,另请参阅 Linus Torvalds 的有趣采访。如果 linux 软件再次出错,那么你只能重新启动 Java 程序(这里 Java 只是后端)
现在让我们考虑一下其他情况。关于完全不知道任何闰秒的代码,如 java.util.Date
和 java.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 很糟糕。