Подсчет количества дней на основе пользовательской единицы времени - PullRequest
2 голосов
/ 06 мая 2019

У меня есть класс с номером и timeUnit

public static class Time {
    private int number;
    private Unit unit;
}

TimeUnit - это перечисление

public enum Unit {
    days,
    months,
    years;
}

Так что возможные значения Time:

[0-Integer.MAX] DAYS
[0-Integer.MAX] MONTHS
[0-Integer.MAX] YEARS

IЯ хочу, чтобы минус Время от сегодняшней даты, но теперь уверен, как я могу этого достичь.

Вот пример,

, если TimePeriod равен 30 ДНЯМ, затем resultDate = Instant.now () - 30 ДНЕЙ.

, если TimePeriod равен 15 МЕСЯЦАМ,затем resultDate = Instant.now () - 450 ДНЕЙ

, если TimePeriod равен 2 ЛЕТАМ, тогда resultDate = Instant.now () - 730 ДНЕЙ

Ответы [ 4 ]

2 голосов
/ 07 мая 2019

Добавьте метод к вашему TimePeriod классу

@AllArgsConstructor
@Builder(toBuilder = true)
@EqualsAndHashCode
@Getter
@NoArgsConstructor
public static class TimePeriod implements Serializable {
    private static final long serialVersionUID = 1L;
    @PositiveOrZero
    private int number;
    @NonNull
    private TimeUnit timeUnit;

    public Instant getSubstractedDate() {
        int totalDays = number;
        switch (timeUnit) {
            case MONTHS: totalDays*=30; break;
            case YEARS: totalDays*=365; break;
        }
        return Instant.now().minus(number, ChronoUnit.DAYS);
    }
}

Использование java.time.LocalDate было бы еще лучше, поскольку у вас есть метод LocalDate#minusDays для использования.И это, вероятно, также имеет больше смысла в нашем случае.Использование Instant для DAYS в качестве минимальной детализации не совсем идеально.

2 голосов
/ 07 мая 2019

Вы можете сделать это так:

public enum TimeUnit {
    DAYS(1),
    MONTHS(30),
    YEARS(365);

    private final int days;

    private TimeUnit(int days) {
        this.days = days;
    }

    public <R extends Temporal> R addTo(R temporal, long amount) {
        return ChronoUnit.DAYS.addTo(temporal, Math.multiplyExact(amount, this.days));
    }
}

*) Использование multiplyExact() для сбоя в случае числового переполнения.

Тест

System.out.println(Instant.now());
System.out.println(TimeUnit.DAYS.addTo(Instant.now(), -30));
System.out.println(TimeUnit.MONTHS.addTo(Instant.now(), -15));
System.out.println(TimeUnit.YEARS.addTo(Instant.now(), -2));

Пример вывода

2019-05-06T21:04:50.781231200Z
2019-04-06T21:04:50.781231200Z
2018-02-10T21:04:50.781231200Z
2017-05-06T21:04:50.781231200Z

Пример выходных данных заменяет Instant на LocalDate

2019-05-06
2019-04-06
2018-02-10
2017-05-06
1 голос
/ 08 мая 2019

Я понял, что мне нужно считать 12 месяцев не 360 днями, а также рассчитать високосные годы. Есть идеи для этого?

Сначала вам нужно выбрать часовой пояс для этого. Зачем? Скажем, вы использовали этот код в (UTC) 2019-02-28T23: 00: 00Z и хотите вычесть 1 месяц. На данный момент в Мехико 28 февраля, поэтому результат должен упасть 28 января. Для сравнения, в Шанхае уже 1 марта, поэтому результат должен упасть 1 февраля. Разница составляет 3 дня (72 часа).

Как только вы определились с часовым поясом, проще всего использовать ZonedDateTime. Вы всегда можете конвертировать в Instant после вычитания, это тоже просто.

Например:

    ZoneId zone = ZoneId.of("Asia/Ashkhabad");
    Map<Unit, ChronoUnit> units = Map.of(Unit.days, ChronoUnit.DAYS,
            Unit.months, ChronoUnit.MONTHS, Unit.years, ChronoUnit.YEARS);

    Time timeToSubtract = new Time(15, Unit.months);

    Instant result = ZonedDateTime.now(zone)
            .minus(timeToSubtract.getNumber(), units.get(timeToSubtract.getUnit()))
            .toInstant();
    System.out.println("15 months ago was " + result);

При запуске только сейчас (2019-05-08T07: 06: 45Z) я получил этот вывод:

15 месяцев назад было 2018-02-08T07: 06: 45.353746Z

Метод ZonedDateTime.minus, который я использую, понимает ChronoUnit, но, конечно, не ваше Unit перечисление, поэтому, поскольку вы получаете последнее, нам нужно перевести (иначе ваше перечисление должно было бы реализовать TemporalUnit, но это было бы излишним). Я использую карту для перевода. Я инициализирую карту Java 9 способом. Если вы все еще на Java 8, я надеюсь, что вы заполните его другим способом.

В моем коде я предполагал, что у вашего Time класса есть обычный конструктор и геттеры.

ZonedDateTime также учитывает, что день не всегда 24 часа. Например, когда начинается весеннее летнее время (DST) и часы переводятся вперед, день составляет всего 23 часа. И затем 25 часов в день, когда часы возвращаются осенью.

0 голосов
/ 08 мая 2019

Вот что я в итоге сделал. Спасибо @Yasin Hajaj & @ OleV.V за правильные ответы. Но ответ Олев В.В. решил проблему более точно.

Добавлено ChronoUnit в Unit enum

public enum Unit {
    days(ChronoUnit.DAYS),
    months(ChronoUnit.MONTHS),
    years(ChronoUnit.YEARS);

    private final ChronoUnit chronoUnit;

    Unit(final ChronoUnit chronoUnit) {
        this.chronoUnit = chronoUnit;
    }

    public ChronoUnit toChronoUnit() {
        return chronoUnit;
    }
} 

И место для расчета дат я так называю

Time timeToSubtract = new Time(15, Unit.months);

Instant result = ZonedDateTime.now(ZoneId.of("America/New_York"))
        .minus(timeToSubtract.getNumber(), units.get(timeToSubtract.getUnit()))
        .toInstant();

System.out.println("15 months ago was " + result);

Возвращает вычтенный период, который включает високосные годы и месяцы с 30/31 днем.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...