Найти день до сегодняшней даты и после сегодняшней даты

Я создал финансовое приложение и хочу, чтобы приложение показывало пользователю финансовый месяц. Финансовые месяцы обычно представляют собой день, когда пользователь получает зарплату или самый большой доход. Этот день может быть первым днем ​​месяца, последним днем ​​месяца или просто 15-м числом каждого месяца. Это значение настраивается в настройках.

Я попытался запустить два метода, которые вызываются в моей ViewModel, getFirstDayOfMonth и getLastDayOfMonth.

Чтобы лучше понять контекст, вот несколько примеров, за основу которых мы возьмем сегодняшнюю дату. 9 сентября 2020 г. Входное значение — это значение, которое я считываю из настроек, из которых пользователь может выбрать, представляет собой число от 1 до 31 включительно.

Примеры: Ввод: 5 Выход: 5 сентября 2020 г. в 00:00 для начала и 4 октября 2020 г. в 23:59:59 для окончания.

================

Ввод: 31 Выход: 31 августа 2020 г., 00:00 для начала и 30 сентября 2020 г., 23:59:59.

Загвоздка в том, что если в месяце нет этого дня, то он получит ближайший слева, например, если в качестве первого дня выбрано 31, а в месяце 30 дней, то 30 будет считаться первым днем, а также для даты окончания, если выбрано 31, а мы находимся в феврале, и у него всего 28 дней, будет выбрано 28.

До сих пор у меня есть этот код, но я чувствую, что его можно улучшить, а также он работает не так, как ожидалось.

fun getFirstDayOfMonth(date: LocalDateTime): Long {
    var tempDate = date
    val firstDayOfMonth = lastDay?.filter { it.isDigit() }!!.toInt()

    if (firstDayOfMonth < tempDate.dayOfMonth) {
        tempDate = tempDate.withDayOfMonth(firstDayOfMonth)
    } else if (firstDayOfMonth > tempDate.dayOfMonth) {
        tempDate = tempDate.minusMonths(1)
        if (tempDate.monthValue == 12) {
//I don't know why minusMonths does not work in the same way as plusMonths, when I write .plusMonths(1) it also change the year if I am in december, with minusMonths if I am in January it does not change the year to minus one year.
            tempDate = tempDate.minusYears(1)
        }

        if (firstDayOfMonth > tempDate.with(TemporalAdjusters.lastDayOfMonth()).dayOfMonth) {
            tempDate.withDayOfMonth(tempDate.with(TemporalAdjusters.lastDayOfMonth()).dayOfMonth)
        } else {
            tempDate = tempDate.withDayOfMonth(firstDayOfMonth)
        }
    }

    return tempDate.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()
}

fun getLastDayOfMonth(date: LocalDateTime): Long {
    var tempDate = date
    val firstDayOfMonth = lastDay?.filter { it.isDigit() }!!.toInt()

    if (firstDayOfMonth > tempDate.dayOfMonth && firstDayOfMonth <= tempDate.with(
            TemporalAdjusters.lastDayOfMonth()
        ).dayOfMonth
    ) {
        tempDate = tempDate.withDayOfMonth(firstDayOfMonth).minusDays(1)
    } else {
        tempDate = tempDate.plusMonths(1)
        if (firstDayOfMonth > tempDate.with(TemporalAdjusters.lastDayOfMonth()).dayOfMonth) {
            tempDate.withDayOfMonth(tempDate.with(TemporalAdjusters.lastDayOfMonth()).dayOfMonth)
        } else {
            tempDate = tempDate.withDayOfMonth(firstDayOfMonth).minusDays(1)
        }
    }

    return tempDate.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()
}

В качестве примера я написал тест, который запускает сегодняшнюю дату и только меняет месяц с января на декабрь. Вот результат текущего алгоритма. 1-е число месяца — это дата, выбранная пользователем, которая также используется по умолчанию в приложении.

Todays date 09.01.2020
Running for month January
Running for 1 st of the month
01.01.2020
31.01.2020
===========================================
Todays date 09.02.2020
Running for month February
Running for 1 st of the month
01.02.2020
29.02.2020
===========================================
Todays date 09.03.2020
Running for month March
Running for 1 st of the month
01.03.2020
31.03.2020
===========================================
Todays date 09.04.2020
Running for month April
Running for 1 st of the month
01.04.2020
30.04.2020
===========================================
Todays date 09.05.2020
Running for month May
Running for 1 st of the month
01.05.2020
31.05.2020
===========================================
Todays date 09.06.2020
Running for month June
Running for 1 st of the month
01.06.2020
30.06.2020
===========================================
Todays date 09.07.2020
Running for month July
Running for 1 st of the month
01.07.2020
31.07.2020
===========================================
Todays date 09.08.2020
Running for month August
Running for 1 st of the month
01.08.2020
31.08.2020
===========================================
Todays date 09.09.2020
Running for month September
Running for 1 st of the month
01.09.2020
30.09.2020
===========================================
Todays date 09.10.2020
Running for month October
Running for 1 st of the month
01.10.2020
31.10.2020
===========================================
Todays date 09.11.2020
Running for month November
Running for 1 st of the month
01.11.2020
30.11.2020
===========================================
Todays date 09.12.2020
Running for month December
Running for 1 st of the month
01.12.2020
31.12.2021
===========================================

Process finished with exit code 0

Также для 31 избранного

Todays date 09.01.2020
Running for month January
Running for 31 th of the month
31.12.2019
30.01.2020
===========================================
Todays date 09.02.2020
Running for month February
Running for 31 th of the month
31.01.2020
30.03.2020
===========================================
Todays date 09.03.2020
Running for month March
Running for 31 th of the month
09.02.2020
30.03.2020
===========================================
Todays date 09.04.2020
Running for month April
Running for 31 th of the month
31.03.2020
30.05.2020
===========================================
Todays date 09.05.2020
Running for month May
Running for 31 th of the month
09.04.2020
30.05.2020
===========================================
Todays date 09.06.2020
Running for month June
Running for 31 th of the month
31.05.2020
30.07.2020
===========================================
Todays date 09.07.2020
Running for month July
Running for 31 th of the month
09.06.2020
30.07.2020
===========================================
Todays date 09.08.2020
Running for month August
Running for 31 th of the month
31.07.2020
30.08.2020
===========================================
Todays date 09.09.2020
Running for month September
Running for 31 th of the month
31.08.2020
30.10.2020
===========================================
Todays date 09.10.2020
Running for month October
Running for 31 th of the month
09.09.2020
30.10.2020
===========================================
Todays date 09.11.2020
Running for month November
Running for 31 th of the month
31.10.2020
30.12.2021
===========================================
Todays date 09.12.2020
Running for month December
Running for 31 th of the month
09.11.2020
30.12.2021
===========================================

Process finished with exit code 0

person Marian Pavel    schedule 09.09.2020    source источник
comment
Вам действительно нужно время?   -  person MC Emperor    schedule 09.09.2020
comment
Не указывайте время окончания как 23:59:59 последнего дня. Будет секунда, не принадлежащая ни к одному финансовому месяцу, что неприятно и чревато ошибками. Вместо этого представьте финансовый месяц как полуоткрытый интервал от начала финансового месяца включительно до начала следующего финансового месяца исключительно . Это стандартная и рекомендуемая обработка временных интервалов.   -  person Ole V.V.    schedule 09.09.2020
comment
@ОлеВ.В. ну... пользователь добавляет эти значения в приложение и использует текущее время. Я вижу совершенно невозможно добавить новую запись в базу данных в 23:59:59   -  person Marian Pavel    schedule 09.09.2020
comment
@MarianPavel. Помогло ли решение решить проблему?   -  person Arvind Kumar Avinash    schedule 15.09.2020


Ответы (2)


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

import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
import java.time.YearMonth;
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;

public class Main {
    public static void main(String[] args) {
        // Test for 31
        int startDay = 31;

        System.out.println(Instant.ofEpochMilli(getFirstDayOfMonth(startDay, YearMonth.of(2020, Month.SEPTEMBER))));
        System.out.println(Instant.ofEpochMilli(getLastDayOfMonth(startDay, YearMonth.of(2020, Month.SEPTEMBER))));
        
        System.out.println(Instant.ofEpochMilli(getFirstDayOfMonth(startDay, YearMonth.of(2020, Month.FEBRUARY))));
        System.out.println(Instant.ofEpochMilli(getLastDayOfMonth(startDay, YearMonth.of(2020, Month.FEBRUARY))));
        
        // Test for 30
        startDay = 30;
        System.out.println();
        System.out.println(Instant.ofEpochMilli(getFirstDayOfMonth(startDay, YearMonth.of(2020, Month.SEPTEMBER))));
        System.out.println(Instant.ofEpochMilli(getLastDayOfMonth(startDay, YearMonth.of(2020, Month.SEPTEMBER))));
        
        System.out.println(Instant.ofEpochMilli(getFirstDayOfMonth(startDay, YearMonth.of(2020, Month.FEBRUARY))));
        System.out.println(Instant.ofEpochMilli(getLastDayOfMonth(startDay, YearMonth.of(2020, Month.FEBRUARY))));
                
        // Test for 28
        startDay = 28;
        System.out.println();
        System.out.println(Instant.ofEpochMilli(getFirstDayOfMonth(startDay, YearMonth.of(2020, Month.SEPTEMBER))));
        System.out.println(Instant.ofEpochMilli(getLastDayOfMonth(startDay, YearMonth.of(2020, Month.SEPTEMBER))));
        
        System.out.println(Instant.ofEpochMilli(getFirstDayOfMonth(startDay, YearMonth.of(2020, Month.FEBRUARY))));
        System.out.println(Instant.ofEpochMilli(getLastDayOfMonth(startDay, YearMonth.of(2020, Month.FEBRUARY))));
    }

    static long getFirstDayOfMonth(int startDay, YearMonth ym) {
        // Get last day of the month
        int lastDayOfTheMonth = ym.getMonth().length(ym.isLeapYear());

        // Start of the day and on the first day of the month
        LocalDateTime ldt = LocalDate.of(ym.getYear(), ym.getMonth(), 1)
                            .atStartOfDay();

        if (startDay > lastDayOfTheMonth) {
            ldt = ldt.minusMonths(1) // Go back to the last month
                    .with(TemporalAdjusters.lastDayOfMonth()); // Adjust to the last day of the obtained month
        }
        return ldt.toInstant(ZoneOffset.UTC).toEpochMilli();
    }

    static long getLastDayOfMonth(int startDay, YearMonth ym) {
        return Instant.ofEpochMilli(getFirstDayOfMonth(startDay, ym))// Get the point to start with
                .plus(ym.getMonth().length(ym.isLeapYear()), ChronoUnit.DAYS)// Add the no. of days of the given month
                .atOffset(ZoneOffset.UTC)// Get OffsetDateTime in order to get LocalDate
                .toLocalDate()// Convert to LocalDate
                .atTime(LocalTime.of(23, 59, 59))// At 23:59:59
                .toInstant(ZoneOffset.UTC)// Convert to Instant
                .toEpochMilli();
    }
}

Вывод:

2020-08-31T00:00:00Z
2020-09-30T23:59:59Z
2020-01-31T00:00:00Z
2020-02-29T23:59:59Z

2020-09-01T00:00:00Z
2020-10-01T23:59:59Z
2020-01-31T00:00:00Z
2020-02-29T23:59:59Z

2020-09-01T00:00:00Z
2020-10-01T23:59:59Z
2020-02-01T00:00:00Z
2020-03-01T23:59:59Z
person Arvind Kumar Avinash    schedule 09.09.2020

Кажется, что этот класс LocalDateTime имеет все необходимые встроенные функции. Проверьте методы minusDays и plusDays.

person Yves Daoust    schedule 09.09.2020