环境: Amazon上的Java 7(修补程序)。请注意,在此环境中使用了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时间戳(long)值。Calendar
实例都位于UTC时区。
我们的理解是,Java 7的Calendar实现不考虑闰秒。我们也相信NTP会更新操作系统时钟。
我们知道亚马逊在2012年的第二次飞跃失败。我们正与他们直接沟通,以了解他们的行动准备工作。
具体问题:
increment
一个日历,它在闰秒之前的一个小时的边界上,它会在闰秒之后的一个小时的边界上吗?或者在一小时前一秒钟?发布于 2015-01-14 19:00:32
我不会太担心2015年的下一个闰秒,并假设Linux团队在此期间已经做了大量的工作来解决闰秒处理代码的任何问题,也请参阅与莱纳斯·托瓦尔兹的这个有趣的访谈。如果linux软件再次出错,那么您只能重新启动Java程序(这里Java只是后端)。
现在让我们考虑一下其他可能发生的事情。对于那些完全不知道任何闰秒的代码,比如java.util.Date
和java.util.Calendar
,请记住,这些代码只看到操作系统时钟所提供的东西。大多数操作系统只是提供UNIX时间戳。如果它们与NTP同步,那么也要记住,NTP-时间戳不像NTP-协议中指定的那样计算闰秒。他们只是重复同样的时间戳。Windows可能会在任何时候触发时钟跳转,而Linux内核则试图更加精确,应用一些闰秒处理代码,并立即操作系统时钟。总之,这两个操作系统只提供类似POSIX的时间戳。Java只是看到..。没什么。
这也回答了您的第一个具体问题:在添加一个小时后,GregorianCalendar
**-object将保持在一个小时的边界上。
关于你的第二个问题::
这仅仅是一秒钟的问题。但可能更麻烦的是,本地时钟甚至在几分钟内就可能出错(然后在与NTP时钟同步之后突然跳转)。后一种行为可以在任何时候发生,而不仅仅是在2015年底-06-30年间。所以我假设你真正的问题是时钟的单调性,。这通常表明,任何测试都应该使用类似于可注入时钟机制的东西。例如,您可以编写一个TimeSource
**-interface,它可以通过方法** public long currentTime()
生成任何unix时间戳(甚至可以进行由计时器创建的模拟跳转),然后在JUnit测试类中使用这个接口来提供(假的)时间并观察代码的行为。
(第3章)大多数人只使用一个不知道任何闰秒的库,因为POSIX很容易理解和计算(尽管在这些特殊的秒中它是不正确的)。如果没有闰秒处理代码,就很难有一个直接根植于这样的标准库的问题。否则,如果您不愿意向用户隐藏闰秒,那么您也可以在这个所以-邮寄中查阅我的答案。
--无论您的决定是什么,闰秒对于选择适当的库/实现都不够重要(其他主题,如线程安全、国际化等等,要重要得多)。对于这些条件,旧的Calendar
**-stuff 是相当糟糕的。**
https://stackoverflow.com/questions/27911432
复制相似问题