带闰秒的时间长度

7

我需要在代码中按照固定日期时间安排任务。为此,我使用了ScheduledExecutorService的方法schedule(Runnable command, long delay, TimeUnit unit);

如何根据闰秒计算此延迟?

目前我使用Duration.between()但它似乎不知道闰秒:

ZonedDateTime date1 = ZonedDateTime.of(2015, 06, 30, 23, 59, 59, 000000, ZoneOffset.UTC);
ZonedDateTime date2 = ZonedDateTime.of(2015, 07, 01, 00, 00, 00, 000000, ZoneOffset.UTC);
ZonedDateTime date3 = ZonedDateTime.of(2015, 07, 01, 00, 00, 01, 000000, ZoneOffset.UTC);
System.out.println(Duration.between(date1, date2)); // PT1S
System.out.println(Duration.between(date2, date3)); // PT1S

1
你的电脑时钟真的足够准确,以至于这实际上很重要吗? - Andy Turner
4
同时,使用前导零来表示时间/日期组件并不是一个好主意。一个带有前导零的整数常量会被解释为八进制,因此你不能写成 08、09 等等,而写成 012345 也与 12345 不同。 - Andy Turner
6
闰秒通过UTC-SLS平滑技术对您隐藏。基本上,在闰秒周围,“Java秒”要么比实际的UTC秒短一点,要么比实际的UTC秒长一点。由于Duration是以Java时间测量的,因此您永远不会看到闰秒。 - dhke
1
一般来说不可能支持这个,因为我们不能预测未来或遥远过去所有的闰秒。这是无法预测的。你可以编译和维护一个已知闰秒列表(目前只有36个),然后自己添加。 - ZhongYu
1
@dhke 这里有一个官方确认事实的消息,即JDK不包含任何闰秒信息:https://github.com/threeten/threeten/issues/197 是的,由IANA托管的原始tzdb包含这些信息,但当Oracle或OpenJDK将tzdb提取/评估为自己的格式时,它们的分发中并没有包含这些信息。 - Meno Hochschild
显示剩余5条评论
3个回答

5

这是一个有趣的答案。但我已经在使用threetenbp(该项目在Java 7中,无法迁移到java8),而Threetenbp-extra似乎不活跃且没有支持 :( - Zycho

2
使用我的库Time4J(v3.x适用于Java 7 - 或者在Android上使用Time4A)将为您提供完整的解决方案,包括对不同时区的格式化和解析的广泛支持。 示例:
// Time4J - transformation from old world
Moment m1 = TemporalType.JAVA_UTIL_DATE.translate(d1);
Moment m2 = TemporalType.JAVA_UTIL_DATE.translate(d2);
System.out.println("Time4J - from j.u.Date: " + SI.SECONDS.between(m1, m2)); // 601

// Time4J - programmatical construction of timestamps (like in your example)
m1 = PlainTimestamp.of(2012, 6, 30, 23, 50, 0).atUTC();
m2 = PlainTimestamp.of(2012, 7, 1, 0, 0, 0).atUTC();
System.out.println("Time4J - from utc-timestamps: " + SI.SECONDS.between(m1, m2)); // 601

// Time4J - parsing zoned inputs
ChronoFormatter<Moment> cf =
    ChronoFormatter.ofMomentPattern(
      "d. MMM uuuu HH:mm:ss[XXX]", // optional offset
      PatternType.CLDR, 
      Locale.ENGLISH, 
      EUROPE.PARIS); // only used if the offset is missing in input
m1 = cf.parse("1. Jul 2015 01:50:00");
m2 = cf.parse("1. Jul 2015 09:00:00+09:00");
System.out.println("Time4J - from offsets or zones: " + SI.SECONDS.between(m1, m2)); // 601

// the leap second itself can also be parsed
Moment ls = cf.parse("1. Jul 2015 08:59:60+09:00"); // offset of Tokyo
System.out.println("leap second in 2015: " + ls);
// 2015-06-30T23:59:60Z

更值得一提的是,Time4J支持时钟意识到闰秒,并且可以从IANA-TZDB传输闰秒数据。请参见我在DZone上的文章中的示例。请注意,Time4J可以完全替代ThreetenBP,但也与Java-8互操作(只需将版本更改为v4.x),并提供了许多超出本问题范围的其他功能。
侧记:大多数人显然选择ThreetenBP是为了使未来迁移到Java-8更容易(只需更改一些导入语句)。但是,您的情况问题在于:
  • Java标准(任何版本)本身不知道闰秒(甚至没有必要的数据)。
  • 建议使用的其他第三方库Threeten-Extra无法在Java-7上工作。它需要Java-8。
迁移到Java-8后,Threeten-Extra似乎提供了解决方案。然而,我现在已经进行了自己的测试,并建议不要使用该库,请参见以下代码:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
Date d1 = sdf.parse("2012-06-30T23:50:00Z");
Date d2 = sdf.parse("2012-07-01T00:00:00Z");
System.out.println(
  "Old world in Posix - ignorant of leap seconds: " 
  + (d2.getTime() - d1.getTime()) / 1000);
// 600 (OK, SI-seconds without counting leap second, a limitation of POSIX)

Instant instant1 = d1.toInstant();
Instant instant2 = d2.toInstant();
System.out.println(
  "Java 8 without Threeten-Extra: " 
  + (instant2.getEpochSecond() - instant1.getEpochSecond()));
// 600 (obviously using more or less fuzzy "rubber seconds", not SI-seconds)
// internally a simple 1:1-mapping of POSIX is applied within 'toInstant()'

UtcInstant utc1 = UtcInstant.of(instant1);
UtcInstant utc2 = UtcInstant.of(instant2);
System.out.println(
  "Threeten-Extra-impl of UTC-SLS: " 
  + utc1.durationUntil(utc2).getSeconds()); // pitfall, see next output!
// 600 (??? - where is the leap second, should be 601 because of original POSIX-input?!)
System.out.println(
  "Threeten-Extra-impl of UTC-SLS: " 
  + utc1.durationUntil(utc2)); // <= printing the duration object
// PT10M0.600600601S (OK, here the UTC-SLS-specific fraction of second appears
// Reason: Smoothing starts 1000s before leap second)

// only offset "Z=UTC+00:00" can be parsed
utc1 = UtcInstant.parse("2012-06-30T23:50:00Z");
utc2 = UtcInstant.parse("2012-07-01T00:00:00Z");
System.out.println(
  "Threeten-Extra-impl of UTC-SLS: " + utc1.durationUntil(utc2).getSeconds());
// 601 (expected, the only way where seconds are really SI-seconds)

撇开格式和解析中的一些明显限制不谈,主要问题似乎是时间尺度定义的混淆处理。这里秒的定义取决于上下文。例如,如果您只考虑使用UTC-SLS进行转换,则结果“PT10M0.600600601S”是可以接受的,但如果您考虑从POSIX通过UTC-SLS到UTC的整个转换,则不可以接受。正如前面所说,POSIX时间在闰秒之前已经被很好地定义了,因此在闰秒期间POSIX的任何模糊都没有借口。

请记住,除了java.time世界外,真正的没有人会谈论UTC-SLS,它是Markus Kuhn的一个(已经过期的)建议,用于解决NTP时间服务器或操作系统内核的内部实现。相反,其他人和企业,如Google,介绍了自己不同的闰秒扩散实现。我不认为UTC-SLS有未来。
由于java.util.Date与UTC-SLS无关(它比较古老),但对于普通时间戳也有明确定义(遵循UTC定义的秒,但不处理闰秒的限制),因此我们在这里看到了外部IT世界(不想知道闰秒或UTC-SLS)和Threeten-Extra之间的互操作性问题,这可能会导致计算持续时间时出现意外差异。

-2

使用Quartz在闰秒方面并没有增加任何内容...或者如果有的话,您需要提供更多上下文来说明它带来了什么... - Guillaume

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接