Разбор строк времени, таких как 1 час 30 минут

Кто-нибудь знает о библиотеке Java, которая может анализировать строки времени, такие как «30 минут», «2 часа 15 минут» или «2 дня 15 часов 30 минут», как миллисекунды (или какой-то объект Duration). Может ли Joda-Time сделать что-то подобное?

(У меня есть уродливый длинный метод для поддержки, который выполняет такой синтаксический анализ, и я хотел бы избавиться от него / заменить его чем-то, что работает лучше.)


person Jonik    schedule 19.06.2011    source источник
comment
Я видел, как Xd Yh Zm или Xd Yh Zmin упоминается как нотация JIRA, но я не знаю, широко ли распространен этот термин.   -  person Jonik    schedule 19.06.2011


Ответы (6)


Вам, вероятно, придется немного изменить это для вашего собственного формата, но попробуйте что-то в этом роде:

PeriodFormatter formatter = new PeriodFormatterBuilder()
    .appendDays().appendSuffix("d ")
    .appendHours().appendSuffix("h ")
    .appendMinutes().appendSuffix("min")
    .toFormatter();

Period p = formatter.parsePeriod("2d 5h 30min");

обратите внимание, что есть appendSuffix, который принимает параметр variants, если вам нужно сделать его более гибким.

Обновление: с тех пор Joda Time добавила Period.toStandardDuration(), и оттуда вы можете использовать getStandardSeconds(), чтобы получить прошедшее время в секундах в виде long.

Если вы используете более старую версию без этих методов, вы все равно можете самостоятельно вычислить отметку времени, приняв стандартные 24 часа в сутки, 60 минут в час и т. д. (В этом случае воспользуйтесь преимуществами констант в классе DateTimeConstants для избегайте необходимости в магических числах.)

person Brad Mace    schedule 19.06.2011
comment
Спасибо! С небольшими изменениями я получаю это, чтобы делать в основном то, что я хочу, и это немного лучше, чем 150-строчный монстр регулярного выражения / конечного автомата, с которым я столкнулся. (Кстати, для преобразования Period в миллисекунды DateTimeConstants в Joda содержит полезные константы, такие как MILLIS_PER_DAY.) - person Jonik; 19.06.2011
comment
Один из способов использования java.time для обработки точного формата, упомянутого в вопросе, см. в этом ответе. Хотя мне интересно, есть ли что-нибудь похожее на PeriodFormatterBuilder в java.time для аналогичного, более простого решения, как в этом ответе. - person Jonik; 12.04.2021

Анализ длительности теперь включен в Java 8. Используйте стандартный формат ISO 8601 с Duration.parse.

Duration d = Duration.parse("PT1H30M")

Вы можете преобразовать эту длительность к общей длине в миллисекундах. Учтите, что Duration имеет разрешение в наносекундах, поэтому возможна потеря данных с наносекунд на миллисекунды.

long milliseconds = d.toMillis();

Формат немного отличается от того, что вы описываете, но его можно легко перевести из одного в другой.

person Andrejs    schedule 17.03.2014
comment
Это также добавляет преимущества поддержки стандарта ISO8601 в течение длительного времени. Строка ISO должна соответствовать форме P[yY][mM][dD][T[hH][mM][s[.s]S]]. Он использует стандартные классы Java в Java 8, а также есть бэкпорты для Java 6 и 7, см.: threeten.org/threetenbp. - person theINtoy; 25.08.2016
comment
Я не уверен, что он поддерживает весь стандарт ISO8601, в частности строку с неделями: например, P2W не поддается разбору, но я считаю, что он действителен в соответствии со стандартом. Документы для Duration говорят: Получает продолжительность из текстовой строки, такой как PnDTnHnMn.nS. - person Michael Graff; 17.10.2017
comment
Как это помогает, когда вопрос был о «например, 30 минут, 2 часа 15 минут или 2 дня 15 часов 30 минут»? - person Michel Jung; 06.06.2019

Я хотел сделать день, час и минуту необязательными, и, похоже, это работает. Обратите внимание, что вызовы appendSuffix() не имеют пробела после символа.

Использование Джоды 2.3.

PeriodParser parser = new PeriodFormatterBuilder()
        .appendDays().appendSuffix("d").appendSeparatorIfFieldsAfter(" ")
        .appendHours().appendSuffix("h").appendSeparatorIfFieldsAfter(" ")
        .appendMinutes().appendSuffix("min")
        .toParser();

Приведенный выше код проходит эти тесты.

@Test
public void testConvert() {
    DurationConverter c = new DurationConverter();

    Duration d;
    Duration expected;

    d = c.convert("1d");
    expected = Duration.ZERO
            .withDurationAdded(Duration.standardDays(1),1);
    assertEquals(d, expected);

    d = c.convert("1d 1h 1min");
    expected = Duration.ZERO
            .withDurationAdded(Duration.standardDays(1),1)
            .withDurationAdded(Duration.standardHours(1),1)
            .withDurationAdded(Duration.standardMinutes(1),1);
    assertEquals(d, expected);


    d = c.convert("1h 1min");
    expected = Duration.ZERO
            .withDurationAdded(Duration.standardHours(1),1)
            .withDurationAdded(Duration.standardMinutes(1),1);
    assertEquals(d, expected);

    d = c.convert("1h");
    expected = Duration.ZERO
            .withDurationAdded(Duration.standardHours(1),1);
    assertEquals(d, expected);

    d = c.convert("1min");
    expected = Duration.ZERO
            .withDurationAdded(Duration.standardMinutes(1),1);
    assertEquals(d, expected);

}
person Victor    schedule 07.02.2014
comment
PeriodParser в первую очередь подразумевается как внутренний интерфейс. PeriodFormatter — это API, ориентированный на пользователя. - person Matt Ball; 15.04.2014
comment
где определяется DurationConverter? - person Pierre D; 28.09.2015
comment
Хотя я изначально думал, что это полезно, он неправильно обрабатывает недопустимый ввод. Или, по крайней мере, может, но это будет реализация DurationConverter, которую мы не видим. - person Tom Quarendon; 27.10.2017

К вашему сведению, только что написал это в течение часа +, использует только java.time.*, довольно прост для понимания и настройки для любых нужд;

Эта версия работает с такими строками, как; 3d12h, 2y, 9m10d и т. д.

import java.time.Duration;
import java.time.Instant;
import java.time.Period;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.Locale;
private static final Pattern periodPattern = Pattern.compile("([0-9]+)([hdwmy])");

public static Long parsePeriod(String period){
    if(period == null) return null;
    period = period.toLowerCase(Locale.ENGLISH);
    Matcher matcher = periodPattern.matcher(period);
    Instant instant=Instant.EPOCH;
    while(matcher.find()){
        int num = Integer.parseInt(matcher.group(1));
        String typ = matcher.group(2);
        switch (typ) {
            case "h":
                instant=instant.plus(Duration.ofHours(num));
                break;
            case "d":
                instant=instant.plus(Duration.ofDays(num));
                break;
            case "w":
                instant=instant.plus(Period.ofWeeks(num));
                break;
            case "m":
                instant=instant.plus(Period.ofMonths(num));
                break;
            case "y":
                instant=instant.plus(Period.ofYears(num));
                break;
        }
    }
    return instant.toEpochMilli();
}

person bekce    schedule 31.05.2019
comment
Да, хороший ответ - person MrNadimi; 29.06.2021

Нет, Joda по умолчанию принимает только продолжительность, мгновенные интервалы и объекты. Для последнего он принимает такие вещи, как даты или формат SOAP ISO. Вы можете добавить сюда свой собственный преобразователь для класса Duration, и, по общему признанию, это скроет весь ваш уродливый код.

person MJB    schedule 19.06.2011

Я понимаю, что этой ветке уже несколько лет, но она продолжает всплывать в верхней части поиска Google, поэтому я подумал, что мы поделимся ею.

Нам также понадобилась эта возможность в паре проектов, поэтому мы создали для нее небольшую библиотеку: https://github.com/blueanvil/kotlin-duration-string

Он написан в/для Kotlin, но вы также можете использовать его из кода Java (см. JavaTest.java):

Duration duration = toDuration("24d 3h 45m");
...
toHumanReadableString(duration);
person Cosmin Marginean    schedule 02.08.2020