17

I need to get the first date of the current quarter as a java.util.Date object and the last date of the current quarter as a java.util.Date object.

I'm using following methods to get this month first date and this month last date.

private   Date getThisMonthFirstDate(){
    Calendar calendar = new GregorianCalendar();
    calendar.set(Calendar.HOUR_OF_DAY, 0);
    calendar.set(Calendar.MINUTE, 0);
    calendar.set(Calendar.SECOND, 0);
    calendar.set(Calendar.MILLISECOND, 0);
    calendar.set(Calendar.DAY_OF_MONTH, 1);
    return calendar.getTime();
}

private   Date getThisMonthLastDate(){
    Calendar calandar = new GregorianCalendar();
    calendar.set(Calendar.HOUR_OF_DAY, 0);
    calendar.set(Calendar.MINUTE, 0);
    calendar.set(Calendar.SECOND, 0);
    calendar.set(Calendar.MILLISECOND, 0);
    calendar.set(Calendar.DAY_OF_MONTH,1);
    calendar.add(Calendar.MONTH, 1);
    calendar.add(Calendar.DATE, -1);
   return calendar.getTime();
}

Is there a way to modify that function to achieve this or could anyone point out a better way?

Assume that Q1 = Jan Feb Mar, Q2 = Apr, May, Jun, etc.

7
  • If you are using zero=January months, like Date does, the start of the quarter is just (year, 3*quarter, 1). Commented Apr 8, 2016 at 7:15
  • 1
    currentMonth - (currentMonth % 3) gives you the first month of the current quarter. That said, you should use the new Java 8 time API or, if not on Java 8, joda-time. Calendar and Date are awful.
    – JB Nizet
    Commented Apr 8, 2016 at 7:16
  • Note that Date represents an instant in time, not a "day" (a 24-ish hour period in your local timezone). As such, the "end" of a quarter is best represented as the start of the next quarter, exclusive. So a quarter is defined by the timestamp range start of this quarter (inclusive) to start of next quarter (exclusive). Commented Apr 8, 2016 at 7:18
  • 2
    Java 8 does it pretty easily, i.e. this
    – dambros
    Commented Apr 8, 2016 at 7:18
  • 1
    I am a bit confused about your getThisMonthLastDate() method since by executing calander.add(Calendar.DAY_OF_MONTH, -1); you skip the complete last day of the month. Maybe decreasing by one second or millisecond would be better.
    – Alexander
    Commented Apr 8, 2016 at 7:22

8 Answers 8

47

Pure Java-8+ solution:

LocalDate localDate = LocalDate.now();
LocalDate firstDayOfQuarter = localDate.with(localDate.getMonth().firstMonthOfQuarter())
    .with(TemporalAdjusters.firstDayOfMonth());

LocalDate lastDayOfQuarter = firstDayOfQuarter.plusMonths(2)
    .with(TemporalAdjusters.lastDayOfMonth());
1
  • 18
    firstDayOfQuarter can be even more enhanced LocalDate firstDayOfQuarter = date.with(IsoFields.DAY_OF_QUARTER, 1L);
    – JaXt0r
    Commented Jan 30, 2019 at 10:32
17

Here is solution in Java 7 or older (otherwise, I suggest to check other answers):

private static Date getFirstDayOfQuarter(Date date) {
    Calendar cal = Calendar.getInstance();
    cal.setTime(date);
    cal.set(Calendar.DAY_OF_MONTH, 1);
    cal.set(Calendar.MONTH, cal.get(Calendar.MONTH)/3 * 3);
    return cal.getTime();
}

private static Date getLastDayOfQuarter(Date date) {
    Calendar cal = Calendar.getInstance();
    cal.setTime(date);
    cal.set(Calendar.DAY_OF_MONTH, 1);
    cal.set(Calendar.MONTH, cal.get(Calendar.MONTH)/3 * 3 + 2);
    cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
    return cal.getTime();
}

Note 1: Months in java.util.Calendar are represented as integers starting from 0 (Jan) through 11 (Dec)
Note 2: division of integers in Java results in floor value (another integer value). So 2/3 = 0, 4/3 = 1 and so forth. So, cal.get(Calendar.MONTH)/3 * 3 calculates zero-based index of quarter: 0(Q1), 1(Q2), 2(Q3), 3(Q4).
Example: Feb in Calendar is 1. So, 1 / 3 * 3 = 0.
If Feb date is supplied, then start of the quarter is 1st of Jan (because we got 0) and the end of quarter is last day of month 0 + 2 = 2 (Mar)

4
  • Thanks for the answer, I'll try this and get back. Commented Apr 8, 2016 at 9:01
  • 1
    Since this answer is working for my test cases I'll mark this as the correct answer. If anyone found a error please report here :) Commented Apr 8, 2016 at 12:03
  • Using this in a Groovy script, I wound up having to use Math.floor and type cast it to an int. Otherwise, works well, thanks! Commented Aug 7, 2017 at 21:02
  • 1
    @Asankasanjaya there was an error in my code. When 31/05/2020 was passed to getLastDayOfQuarter then it would return 01/06/2020 instead of 30/06/2020. I've edit my answer with a fix. Before changing calendar instance to new month, it was required to set it to 1 day of month. Otherwise, 31/05 would become 31/06 which isn't valid and java will automatically convert it to 01/06 which was the case Commented May 5, 2020 at 10:48
7

As others mentioned, you are using outmoded classes. Sun/Oracle decided to supplant the old date-time classes with the java.time framework, a vast improvement.

Avoid the old date-time classes. They really are poorly designed, confusing, and troublesome.

java.time

The java.time framework is built into Java 8. For Java 6 & 7, use the back-port, ThreeTen-Backport. For Android, the adaptation of that back-port, ThreeTenABP.

The java.time framework is inspired by the highly successful Joda-Time library, defined by JSR 310, and extended by the ThreeTen-Extra project. See Oracle Tutorial.

LocalDate

For a date-only value, without time-of-day and without time zone, use LocalDate built-in as part of java.time. To get the current date requires a time zone as the date varies around the world (a new day dawns earlier in the east).

ZoneId zoneId = ZoneId.of ( "America/Montreal" );
LocalDate today = LocalDate.now ( zoneId );

Quarter & YearQuarter

The ThreeTen-Extra project mentioned above serves as the proving ground for possible future features to be added to java.time. To use this library, add its jar file to your project (or use Maven etc.). If you really don’t want to add a library, see an alternate solution Answer by dheeran.

This library currently includes the Quarter and YearQuarter classes that you might find particularly useful. Safer to pass around objects of these types in your code rather than use strings and numbers to represent your quarters.

YearQuarter currentQuarter = YearQuarter.now ( zoneId );

The YearQuarter has many useful methods, including asking for its dates.

LocalDate start = currentQuarter.atDay ( 1 );
LocalDate stop = currentQuarter.atEndOfQuarter ();

Dump to console.

System.out.println ( "today: " + today + " currentQuarter: " + currentQuarter + " start: " + start + " stop: " + stop );

today: 2016-04-08 currentQuarter: 2016-Q2 start: 2016-04-01 stop: 2016-06-30

If you really do not want to add the ThreeTen-Extra library as I recommended, see the Answer by Pavel for another solution using only the built-in java.time classes. But if you are doing much work with quarters, I am sure you’ll find ThreeTen-Extra to be well worth the trouble of adding a library to your project.

Conversion

If you must use java.util.Date to work with old code not yet updated to java.time, you can convert back and forth. Find new methods added to the old classes for conversion.

Unfortunately, the old classes lack any clean way to represent a date-only value. So there is no perfectly clean way to go to/from a LocalDate.

java.sql.Date

The java.sql.Date class pretends to be a date-only but actually contains a time-of-day adjusted to 00:00:00.0. This class awkwardly inherits from java.util.Date but the doc clearly warns against using that fact; you are supposed to treat the two as separate unrelated classes.

java.sql.Date sqlDate_quarterStart = java.sql.Date.valueOf( start );

java.util.Date

A java.util.Date represents both a date and time-of-day, effectively in UTC. We can first adjust our LocalDate into the first moment of a day to get a date with time-of-day, a ZonedDateTime. From there we can ask for an Instant which is a moment on the timeline in UTC. The old .Date class has a static from( Instant ) conversion method.

ZonedDateTime zdt = start.atStartOfDay( zoneId ); // First moment of the day on a certain date.
Instant instant = zdt.toInstant(); // Moment on the timeline in UTC.
java.util.Date utilDate = java.util.Date.from( instant );

About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

Where to obtain the java.time classes?

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

5

Keep it simple

I think the following is most elegant because it doesn't have any magical numbers (like 92), TemporalAdjusters or non-obvious calculations, and can easily be adapted to get the start and end of any quarter (not just current quarter).

import static java.time.temporal.IsoFields.QUARTER_OF_YEAR;

LocalDate date = LocalDate.now(); // can be any other date

int year = date.getYear();
int quarter = date.get(QUARTER_OF_YEAR);
        
LocalDate start = YearMonth.of(year, 1)                    // January of given year
                           .with(QUARTER_OF_YEAR, quarter) // becomes first month of given quarter
                           .atDay(1);                      // becomes first day of given quarter

LocalDate end = YearMonth.of(year, 3)                      // March of given year
                         .with(QUARTER_OF_YEAR, quarter)   // becomes 3rd (last) month of given quarter
                         .atEndOfMonth();                  // becomes last day of given quarter

If you don't have a date, but just a year and quarter (e.g. Q2 2020), this becomes:

LocalDate start = YearMonth.of(2020, 1).with(QUARTER_OF_YEAR, 2).atDay(1);
LocalDate end = YearMonth.of(2020, 3).with(QUARTER_OF_YEAR, 2).atEndOfMonth();
3

It's pretty simple with LocalDate

LocalDate inputDate = LocalDate.parse("2018-09-04");
LocalDate firstDayOfQuarter = inputDate.withMonth(inputDate.get(IsoFields.QUARTER_OF_YEAR) * 3 - 2).with(TemporalAdjusters.firstDayOfMonth());
LocalDate lastDayOfQuarter = inputDate.withMonth(inputDate.get(IsoFields.QUARTER_OF_YEAR) * 3).with(TemporalAdjusters.lastDayOfMonth());

Cheers!

1
  • does not work when the current date is in the first quarter of the year.
    – Punith Raj
    Commented Jan 20, 2023 at 5:34
3

you can use java 8 LocaDate to get current quarter first and last day

The code below gives you.

public static LocalDate getCurrentQuarterStartDay(LocalDate date) {
        return date.with(IsoFields.DAY_OF_QUARTER, 1L);
    }

public static LocalDate getCurrentQuarterEndDate(LocalDate date) {
        return date.with(IsoFields.DAY_OF_QUARTER, 92L);
    }

Here in second method the 92 has partially lenient

The day-of-quarter has values from 1 to 90 in Q1 of a standard year, from 1 to 91in Q1 of a leap year, from 1 to 91 in Q2 and from 1 to 92 in Q3 and Q4

see ISOFields documentation for details here

1
  • 1
    I don't think this works, using 92L doesn't give the last day of the quarter for Q1 and Q2 but 1st (or 2nd) of April and 1st of July: " If the quarter has less than 92 days, then day 92, and potentially day 91, is in the following quarter."
    – ahelix
    Commented Sep 29, 2020 at 21:56
0

You can use joda:

Date start = date.withMonthOfYear(((date.getMonthOfYear() / 3) + 1) * 3 - 2)
            .withDayOfMonth(1)
            .withTimeAtStartOfDay()
            .toDate();
Date end = date.withMonthOfYear(((date.getMonthOfYear() / 3) + 1) * 3)
            .withDayOfMonth(Month.of(((date.getMonthOfYear() / 3) + 1) * 3).maxLength())
            .withTimeAtStartOfDay()
            .toDate();

When DateTime date = new DateTime(2016, 8, 12, 1, 1, 1);

I get:

Fri Jul 01 00:00:00 CEST 2016
Fri Sep 30 00:00:00 CEST 2016
6
  • Unfortunately I'm using java.util.date for all my other functions, so I think its better to go with that :) Commented Apr 8, 2016 at 9:02
  • Ok I see. You should definitely consider switching to joda.java.util.Date api is just horrible and I've never worked in a project which uses it directly. Joda became some kind of standard.
    – g-t
    Commented Apr 8, 2016 at 9:07
  • Thanks for mentioning that :). For future works I'll use Joda time instead of java.util.Date :) Commented Apr 8, 2016 at 9:08
  • If using Java 8, use java.time. It is inspired from Joda time.
    – adarshr
    Commented Apr 8, 2016 at 19:08
  • @Asankasanjaya Some quick searching on Stack Overflow will show that the java.util.Date/.Calendar classes are a terrible mess and should be avoided. Even Sun/Oracle gave up on them, agreeing to supplant them with the java.time classes. Commented Apr 9, 2016 at 16:11
0

Using the answer from Volodymyr Masliy using java.util.Date, there is some bug that came up in my tests If you enter "31/05/2000" it returns July 1st of that year, which is obviously not what we're looking for.

I used java.time.LocalDate as suggested by Basil Bourque and dheeran and the test case works ok.

My method is as follows:

public static Date lastDayOfQuarter(Date date) {
    LocalDate inputDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
    LocalDate lastDayOfQuarter = inputDate.withMonth(inputDate.get(IsoFields.QUARTER_OF_YEAR) * 3).with(TemporalAdjusters.lastDayOfMonth());
    return Date.from(LastDayOfQuarter.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
}

My UnitTests using ZohhakRunner are as follows:

@TestWith({
    "01/03/2000, 31/03/2000",
    "05/04/1982, 30/06/1982",
    "31/05/1982, 30/06/1982",
    "09/07/1990, 30/09/1990",
    "11/11/2019, 31/12/2019",
    "31/03/2016, 31/03/2016"})
public void lastDayOfQuarter_useInputDate_returnExpectedEndOfQuarterDate(String inputDateAsString, String endOfQuarterAsString) throws ParseException{
    Date inputDate = new SimpleDateFormat("dd/MM/yyyy").parse(inputDateAsString);
    Date expectedDate = new SimpleDateFormat("dd/MM/yyyy").parse(endOfQuarterAsString);

    Date result = DateUtils.lastDayOfQuarter(inputDate);
    assertEquals(result, expectedDate);
}
2
  • there's typo: return Date.from(...) -> the object lastDayOfQuarter should be lowercase.
    – Tony B
    Commented Dec 18, 2019 at 20:37
  • @DaithiG Thanks for pointing it out. I've edit my answer to fix mentioned scenario Commented May 5, 2020 at 10:46

Not the answer you're looking for? Browse other questions tagged or ask your own question.