Добавить поплавок в Java Calendar

В настоящее время я разрабатываю некоторые функции, которые должны либо вычесть, либо добавить время к экземпляру класса Calendar. Время, когда мне нужно добавить / sub, находится в файле свойств и может быть любым из этих форматов:

30,sec

90,sec

1.5,min

2,day

2.333,day

Предположим дополнение для простоты. Я бы прочитал эти значения в массиве String:

String[] propertyValues = "30,sec".split(",");

Я бы прочитал второе значение в этой паре, разделенной запятыми, и сопоставил бы его с соответствующим int в классе Calendar (так, например, «sec» становится Calendar.SECOND, «min» становится Calendar.MINUTE):

int calendarMajorModifier = mapToCalendarClassIntValues(propertyValues[1]);

Чтобы затем выполнить фактическую операцию, я бы сделал это так же просто, как:

cal.add(calendarMajorModifier, Integer.parseInt(propertyValues[0]));

Это работает, и это не слишком сложно. Теперь проблема заключается в плавающих значениях (например, 2.333, день для eaxmple) - как бы вы с этим справились?

String[] propertyValues = "2.333,day".split(",");

Как вы понимаете, код становится довольно сложным (на самом деле я его еще не написал, поэтому не обращайте внимания на синтаксические ошибки)

float timeComponent = Float.parseFloat(propertyValues[0]);
if (calendarMajorModifier == Calendar.DATE) {
    int dayValue = Integer.parseFloat(timeComponent);
    cal.add(calendarMajorModifier, dayValue);
    timeComponent = (timeComponent - dayValue) * 24; //Need to convert a fraction of a day to hours
    if (timeComponent != 0) {
        calendarMajorModifier = Calendar.HOUR;
    }
}
if (calendarMajorModifier == Calendar.HOUR) {
    int hourValue = Integer.parseFloat(timeComponent);
    cal.add(calendarMajorModifier, hourValue);
    timeComponent = (timeComponent - hourValue) * 60; //Need to convert a fraction of an hour to minutes
    if (timeComponent != 0) {
        calendarMajorModifier = Calendar.MINUTE;
    }
}
... etc

Конечно, я понимаю, что может быть возможность рефакторинга, но все же кажется очень грубым решением.

Я использую класс Calendar для выполнения операций, но технически это может быть любой класс. Пока я могу конвертировать между ними (т.е. получая длинное значение и используя его), так как функция потребности возвращает класс Calendar. В идеале класс также должен быть родным для Java, чтобы избежать проблем с лицензированием третьих лиц :).

Боковое примечание: я предложил изменить формат на что-то вроде yy: MM: ww: dd: hh: mm: ss, чтобы избежать плавающих значений, но это не сработало. Я также предложил что-то вроде 2, дня, 5, часа, но опять же, в идеале это должно быть указано выше.

Я предлагаю вам использовать Java Time API. Calendar устарел. Кроме того, чтобы иметь дело с десятичными значениями произвольной точности, я предлагаю взглянуть на класс BigDecimal.

MC Emperor 01.10.2018 16:50

Это может быть List<Adjustment>, и каждый из них применяется к cal (или какому-либо другому типу объекта). Запись свойства 2,day имеет только 1 элемент в списке, в то время как 2.333,day имеет два элемента в списке. И отделите синтаксический анализ значения свойства от применения корректировок.

Andrew S 01.10.2018 16:50

Вам нужно будет кодировать переходы самостоятельно (хотя подумайте о рабочей библиотеке java.time вместо календаря) - на самом деле нет «дроби» ни в одной из наших единиц времени - когда мы говорим «полтора дня», что на самом деле мы имеем в виду «1 день, 12 часов». Это преобразование обычно не представлено какой-либо библиотекой datetime из-за того, насколько оно на самом деле произвольно.

M. Prokhorov 01.10.2018 16:51

Я посмотрю на класс Time и посмотрю, поможет ли это упростить задачу - в конце концов, на выходе все равно должен быть класс Calendar, независимо от того, что устарело или нет. И действительно, Прохоров, формат мне тоже не очень нравится, так как вы наталкиваетесь на эти глупые подсчеты, к сожалению, окончательного решения у меня нет :)

Martin 01.10.2018 17:25

@Martin, по сути, вы отделяете целую часть от имеющейся у вас единицы. Затем вы берете следующую наименьшую единицу и целую часть этой единицы и так далее (вам также нужно будет создать здесь упорядоченную структуру единиц). В результате этого анализа вы должны получить от 2.333 days что-то точно близкое к 2 days 7 hours 59 minutes 31 seconds 2 centiseconds (учитывая, что входной сигнал - 2.333, а это не точная треть дня). Чтобы сделать это менее строгим, вы можете, например, округлить минуты до часов, если вы получили ввод в днях и т. д.

M. Prokhorov 01.10.2018 17:52

В идеале вы могли бы позволить своим пользователям вводить периоды времени с таким количеством полей, которое им необходимо, чтобы значения всех полей были целыми числами. Но я полагаю, что это исключено.

M. Prokhorov 01.10.2018 17:53
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
6
368
1

Ответы 1

Я бы преобразовал значение в наименьшую единицу и добавил:

float timeComponent = Float.parseFloat(propertyValues[0]);
int unitFactor = mapUnitToFactor(propertyValues[1]);
cal.add(Calendar.SECOND, (int)(timeComponent * unitFactor));

а mapUnitToFactor будет примерно таким:

int mapUnitToFactor(String unit)
{
    if ("sec".equals(unit))
        return 1;
    if ("min".equals(unit))
        return 60;
    if ("hour".equals(unit))
        return 3600;
    if ("day".equals(unit))
        return 24*3600;
    throw new InvalidParameterException("Unknown unit: " + unit);
}

Так, например, 2,333 дня превратятся в 201571 секунду.

Спасибо за ответ. Я действительно рассматривал аналогичную функцию, поскольку она относительно чистая, и наполовину реализовывала ее, пока не заговорили о том, что также имеет значение «1, месяц» в качестве допустимого значения свойства, что, к сожалению, разрушит эту реализацию. Но я могу убедить их, что плавающие значения для месяца трудно вычислить, и поэтому разрешены только целочисленные значения для месяца.

Martin 01.10.2018 17:29

Не в каждом дне 86400 секунд. Чтобы максимально точно соответствовать источнику, лучше всего делать точки как можно более грубыми.

M. Prokhorov 01.10.2018 17:46

@Martin Ага, месяц с плавающей запятой не имеет смысла. Чтобы реализовать это (независимо от того, как), вам нужно будет указать фиксированный размер для интерпретации доли месяца. Но это просто катастрофа, ожидающая своего часа. И, как указал @MProkhorov, это верно даже в течение нескольких дней (и, следовательно, недель), хотя в зависимости от того, насколько точным вы хотите, чтобы это было, дополнительная секунда может быть не такой большой проблемой (иначе вы не должны в любом случае используя поплавки).

Max Vollmer 01.10.2018 18:09

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