Find the day before today's date and after today's date

115 Views Asked by At

I have created a financial app and I want the app to show the financial month to the user. The financial months represents usually the day when the user gets the salary or the biggest income. That day could be the first of the month, the last day of the month, or simply the 15th every month. This value is configurable in the settings.

I tried to run two methods that are called in my ViewModel, getFirstDayOfMonth and getLastDayOfMonth

To understand better the context, here are some examples, we will take as a reference today's date. 9th September 2020. The Input value is the value I read from settings, which the user can select from, is a number from 1 to 31 inclusive.

Examples: Input: 5 Output: 5th September 2020 at 00:00 for start and 4th October 2020 at 23:59:59 for end

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

Input: 31 Output: 31th August 2020 00:00 for start and 30 September 2020 at 23:59:59

The catch is that if the month does not have that day, it will get the closest one on the left, for example, if 31 is selected as the first day and the month has 30 days, 30 will be calculated as the first day, and also for the end date, if 31 is selected and we are in february and it has only 28 days, 28 it will be selected.

Until now I have this code but I feel it can be improved, also it's not working as expected.

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()
}

As an example, I wrote a test that runs today date and only changes the month from January to December. Here is the output of the current algorithm. 1st of the month is the date selected by the user which also defaults in the app.

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

Also for 31 selected

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
2

There are 2 best solutions below

0
On

There are some mistakes in your calculations which you will be able to catch quite easily once you have understood the solution given below. I have put enough comments in the code which will help you understand it quickly.

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();
    }
}

Output:

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
0
On

It seems that this LocalDateTime class has all the bells and whistles that you need built-in. Check the minusDays and plusDays methods.