better way to determine quarter boundaries dates

Viewed 237

I have this code that works :

    private fun getCycleBoundaries(cycleNumber: Int, year: Int): Array<Date> {

        return when (cycleNumber) {
            1 -> arrayOf(DATE_FORMATER.parse("$year-01-01T00:00:00"), DATE_FORMATER.parse("$year-03-31T23:59:59"))
            2 -> arrayOf(DATE_FORMATER.parse("$year-04-01T00:00:00"), DATE_FORMATER.parse("$year-06-30T23:59:59"))
            3 -> arrayOf(DATE_FORMATER.parse("$year-07-01T00:00:00"), DATE_FORMATER.parse("$year-09-30T23:59:59"))
            4 -> arrayOf(DATE_FORMATER.parse("$year-10-01T00:00:00"), DATE_FORMATER.parse("$year-12-31T23:59:59"))
            else -> throw IllegalArgumentException("$cycleNumber is nor a valid cycle number")
        }
    }

This code takes a cycle number : 1 or 2 or 3 or 4 and a year And it returns an arrays of 2 dates (the start and the end of the quarter)

For example if i call :

 getCycleBoundaries(1, 2019)

I expect :

2019-01-01T00:00:00 and 2019-03-31T23:59:59

As said it works but i'm not happy with the code and was trying to do it better. Is there a better way to achieve this ?

Thanks

2 Answers

The IsoFields class of java.time may be a bit overlooked. It’s our friend when it comes to manipulating quarters. Sorry that I can write only Java code.

public static LocalDate[] getCycleBoundaries(int cycleNumber, int year) {
    YearMonth firstMonthOfQuarter = YearMonth.of(year, 1)
            .with(IsoFields.QUARTER_OF_YEAR, cycleNumber);
    return new LocalDate[] { firstMonthOfQuarter.atDay(1),
            firstMonthOfQuarter.plusMonths(2).atEndOfMonth() };
}

Let’s try it out:

    System.out.println(Arrays.toString(getCycleBoundaries(4, 2019)));

Output in this case:

[2019-10-01, 2019-12-31]

Tip: use half-open intervals. While users usually prefer to have a period in the calendar given as first and last day, in a computer program it’s generally more convenient to represent a period as a from date inclusive and a to date exclusive. So a quarter as the first day of the quarter and the first day of the following quarter. And if you know already that the period is a quarter, then just the first day of the quarter and nothing more. The rest can be easily calculated as needed.

Quarter & YearQuarter

Add the ThreeTen-Extra library to your project. You can access handy classes that extend the features of the modern java.time classes built into Java and defined in JSR 310.

The Quarter enum class represents generic quarters without a year. Use this class across your code rather than a mere integer 1-4. Sound so makes your code more self-documenting, provides type-safety, and ensures valid values.

Quarter q = Quarter.Q2 ;

The YearQuarter represents a quarter in a specific year.

YearQuarter yq = YearQuarter.of( 2019 , q ) ;

Get the date of the first day of that quarter.

LocalDate start = yq.atDay( 1 ) ;

Get the date of last day of quarter.

LocalDate lastDate = yq.atEndOfQuarter() ;

Or for half-open date ranges, get the first day of the next quarter.

LocalDate stop = yq.plusQuarters( 1L ).atDay( 1 ) ;

Rather than return a mere array of the dates, use the LocalDateRange class also included in ThreeTen-Extra. This class offers powerful comparison methods like contains, abuts, overlaps, and more.

LocalDateRange qDates = LocalDateRange.of( start , stop ) ;
Related