Продолжительность с високосными секундами

Мне нужно запланировать задачу в моем коде на фиксированную дату. Для этого я использую 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

3 ответа

Для поддержки високосных секунд вам нужен файл jar расширения ThreeTen-Extra. Он имеет специальные классы для UTC и TAI, а также таблицу високосных секунд.

Чтобы вычислить длительность, используйте специальный метод durationUntil().

Использование моей библиотеки Time4J ( v3.x работает на Java 7 - или Time4A на Android) даст вам полное решение, включая расширенную поддержку форматирования и анализа в разных часовых поясах. Примеры:

// 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

Существует даже больше, а именно поддержка часов, осведомленных о високосных секундах и передаче данных о високосных секундах из 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)

Оставляя в стороне некоторые очевидные ограничения в форматировании и синтаксическом анализе, основная проблема, по-видимому, заключается в запутанной обработке определений шкалы времени. Определение секунды зависит здесь от контекста. Например, результат "PT10M0.600600601S" - это нормально, если вы рассматриваете преобразование только с использованием UTC-SLS, но не хорошо, если вы рассматриваете все преобразование из POSIX через UTC-SLS в UTC. И, как уже было сказано, время POSIX четко определено за 10 минут до високосной секунды, поэтому любая нечеткость POSIX в течение високосной секунды не является оправданием.

Имейте в виду, что действительно НИКТО вне java.time -world рассказывает о UTC-SLS, который был (официально истекшим) предложением Маркуса Куна, касающимся внутренней реализации NTP-серверов времени или ОС-ядер. Вместо этого другие люди и предприятия, такие как Google, вводят свои собственные различные варианты скачков. Я не вижу будущего для UTC-SLS.

И с тех пор java.util.Date не имеет ничего общего с UTC-SLS (намного старше), но также хорошо определено для нормальных временных меток (после определения UTC для Si-секунд с ограничением високосных секунд, которые вообще не обрабатываются), мы видим здесь совместимость проблема между внешним IT-миром (который не хочет ничего знать о високосных секундах или UTC-SLS) и Threeten-Extra, который может вызвать неожиданные различия в расчетных длительностях.

Рассмотрите возможность использования Quartz с CronTrigger

Другие вопросы по тегам