ISO 8601 Анализ интервала времени в Java

ISO 8601 определяет синтаксис представления временного интервала.

Существует четыре способа выражения временного интервала:

  • Начало и конец, например "2007-03-01T13: 00: 00Z/2008-05-11T15: 30: 00Z"
  • Начало и продолжительность, такие как "2007-03-01T13: 00: 00Z/P1Y2M10DT2H30M"
  • Продолжительность и конец, например "P1Y2M10DT2H30M/2008-05-11T15: 30: 00Z"
  • Только продолжительность, например "P1Y2M10DT2H30M", с дополнительной информацией о контексте

Если какие-либо элементы отсутствуют в конечном значении, они считаются такими же, как для начального значения, включая часовой пояс. Эта особенность стандарта позволяет получить краткие представления временных интервалов. Например, дата двухчасового собрания, включая время начала и окончания, может быть просто показана как "2007-12-14T13: 30/15: 30", где "/15: 30" означает "/2007-12- 14T15: 30" (на ту же дату, что и начало), или даты начала и окончания месячного расчетного периода как "2008-02-15/03-14", где "/03-14" подразумевает "/2008-03 -14" (в том же году, что и начало).

Кроме того, повторяющиеся интервалы формируются путем добавления "R [n]/" в начало интерпольного выражения, где R используется как сама буква, а [n] заменяется числом повторений. Оставляя значение для [n], означает неограниченное количество повторений. Итак, чтобы повторить интервал "P1Y2M10DT2H30M" пять раз, начиная с "2008-03-01T13: 00: 00Z", используйте "R5/2008-03-01T13: 00: 00Z/P1Y2M10DT2H30M".

Я ищу хороший Java-анализатор (если возможно, совместимый с библиотекой Joda-Time), чтобы проанализировать этот синтаксис. Любые указатели на хорошую библиотеку?

Ответ 1

java.time

Структура java.time, встроенная в Java 8 и более поздняя версия, имеет метод Duration.parse для синтаксического анализа длительность отформатированного ISO 8601:

java.time.Duration d = java.time.Duration.parse("PT1H2M34S");
System.out.println("Duration in seconds: " + d.get(java.time.temporal.ChronoUnit.SECONDS));

Печать Duration in seconds: 3754

Ответ 2

Для тех, кто в проекте, который может быть ограничен использованием сторонних библиотек (причины лицензирования или что-то еще), сама Java предоставляет хотя бы часть этой возможности, поскольку Java 1.6 (или ранее?), используя javax.xml.datatype.DatatypeFactory.newDuration(String) method и Duration. Метод DatatypeFactory.newDuration(String) будет анализировать строку в формате "PnYnMnDTnHnMnS". Эти классы предназначены для манипуляций с XML, но поскольку XML использует временные обозначения ISO 8601, они также служат удобными утилитами для синтаксического разбора.

Пример:

import javax.xml.datatype.*;

Duration dur = DatatypeFactory.newInstance().newDuration("PT5H12M36S");
int hours = dur.getHours(); // Should return 5

Я лично не использовал формат продолжительности, кроме 4-го, который вы перечисляете, поэтому я не могу ручаться за то, успешно ли он разбирает их или нет.

Ответ 3

Я так понимаю, вы уже пробовали Joda-Time? Подача строк примера из вашего вопроса через Interval.parse(Object) показывает, что он может обрабатывать "начало и конец", "начало и продолжительность" и "продолжительность и конец", но не подразумеваемые поля и повторение.

2007-03-01T13:00:00Z/2008-05-11T15:30:00Z => from 2007-03-01T13:00:00.000Z to 2008-05-11T15:30:00.000Z
2007-03-01T13:00:00Z/P1Y2M10DT2H30M       => from 2007-03-01T13:00:00.000Z to 2008-05-11T15:30:00.000Z
P1Y2M10DT2H30M/2008-05-11T15:30:00Z       => from 2007-03-01T13:00:00.000Z to 2008-05-11T15:30:00.000Z
2007-12-14T13:30/15:30                    => java.lang.IllegalArgumentException: Invalid format: "15:30" is malformed at ":30"
R5/2008-03-01T13:00:00Z/P1Y2M10DT2H30M    => java.lang.IllegalArgumentException: Invalid format: "R5"

Единственная другая исчерпывающая библиотека даты/времени, о которой я знаю, JSR-310, которая, похоже, не обрабатывает такие интервалы.

На данный момент, создание ваших собственных улучшений поверх Joda-Time - это, вероятно, ваш лучший выбор, извините. Существуют ли какие-либо конкретные форматы интервала ISO, которые вам нужно обрабатывать, помимо тех, которые уже поддерживаются Joda-Time?

Ответ 4

Единственная библиотека, которая способна моделировать все функции анализа интервала, которые вы хотите, на самом деле является моей библиотекой Time4J (диапазон-модуль). Примеры:

// case 1 (start/end)
System.out.println(MomentInterval.parseISO("2012-01-01T14:15Z/2014-06-20T16:00Z"));
// output: [2012-01-01T14:15:00Z/2014-06-20T16:00:00Z)

// case 1 (with some elements missing at end component and different offset)
System.out.println(MomentInterval.parseISO("2012-01-01T14:15Z/08-11T16:00+00:01"));
// output: [2012-01-01T14:15:00Z/2012-08-11T15:59:00Z)

// case 1 (with missing date and offset at end component)
System.out.println(MomentInterval.parseISO("2012-01-01T14:15Z/16:00"));
// output: [2012-01-01T14:15:00Z/2012-01-01T16:00:00Z)

// case 2 (start/duration)
System.out.println(MomentInterval.parseISO("2012-01-01T14:15Z/P2DT1H45M"));
// output: [2012-01-01T14:15:00Z/2012-01-03T16:00:00Z)

// case 3 (duration/end)
System.out.println(MomentInterval.parseISO("P2DT1H45M/2012-01-01T14:15Z"));
// output: [2011-12-30T12:30:00Z/2012-01-01T14:15:00Z)

// case 4 (duration only, in standard ISO-format)
Duration<IsoUnit> isoDuration = Duration.parsePeriod("P2DT1H45M");

// case 4 (duration only, in alternative representation)
Duration<IsoUnit> isoDuration = Duration.parsePeriod("P0000-01-01T15:00");
System.out.println(isoDuration); // output: P1M1DT15H

Некоторые замечания:

  • Другие классы интервалов существуют с аналогичными возможностями синтаксического анализа, например DateInterval или TimestampInterval в пакете net.time4j.range.

  • Только для обработки продолжительности (которая может охватывать как календарные, так и часы), см. также javadoc. Существуют также функции форматирования, см. Вложенный класс Duration.Formatter или локализованная версия net.time4j.PrettyTime (на самом деле на 86 языках).

  • Взаимодействие предлагается с Java-8 (java.time -package), но не с Joda-Time. Например: стартовый или конечный компонент MomentInterval можно легко запросить с помощью getStartAsInstant() или getEndAsInstant().

Повторяющиеся интервалы поддерживаются классом IsoRecurrence. Пример:

IsoRecurrence<MomentInterval> ir =
    IsoRecurrence.parseMomentIntervals("R5/2008-03-01T13:00:00Z/P1Y2M10DT2H30M");
ir.intervalStream().forEach(System.out::println);

Вывод:

[2008-03-01T13:00:00Z/2009-05-11T15:30:00Z)
[2009-05-11T15:30:00Z/2010-07-21T18:00:00Z)
[2010-07-21T18:00:00Z/2011-10-01T20:30:00Z)
[2011-10-01T20:30:00Z/2012-12-11T23:00:00Z)
[2012-12-11T23:00:00Z/2014-02-22T01:30:00Z)