I'm trying to get Months from a certain quarter. Using the code below, I successfully get the names of the months in the current quarter from the LocalDate.now() instance.
How would I get a quarter's months from just a quarter String (e.g. "Q1")?
int monthInt = Month.from(LocalDate.now()).firstMonthOfQuarter().getValue();
for (int j = 1; j <= 3; j++) { //for each month in quarter
System.out.println(Month.of(monthInt).name()); //January, February, March
monthInt++;
}
We can find out how the JDK calculates the quarter by looking at the declaration of getFrom of IsoFields.QUARTER_OF_YEAR:
public long getFrom(TemporalAccessor temporal) {
if (isSupportedBy(temporal) == false) {
throw new UnsupportedTemporalTypeException("Unsupported field: QuarterOfYear");
}
long moy = temporal.getLong(MONTH_OF_YEAR);
return ((moy + 2) / 3);
}
Notice how it uses the formula quarter = (moy + 2) / 3. Therefore, to find the starting month of a quarter, we just need to rearrange it in terms of moy - moy = quarter * 3 - 2.
You can write a method like this:
private static List<String> monthNamesFromQuarter(int quarter) {
// you can do the validation of quarter yourself
int start = quarter * 3 - 2;
return IntStream.range(start, start + 3)
.mapToObj(Month::of)
.map(Month::name)
.collect(Collectors.toList());
}
Use org.threeten.extra.YearQuarter class, along with Quarter, ZoneId, LocalDate, and Month.
YearQuarter // Represent an entire quarter of a specific year.
.now( ZoneId.of( "Asia/Tokyo" ) ) // Determine the current quarter as seen via the wall-clock time used by the people of a particular region (a time zone).
.with( // Move to another quarter.
Quarter.valueOf( "Q1" ) // Or, `Quarter.of( 1 )` if starting with an integer number rather than a `String` object.
) // Returns another `YearQuarter` object, rather than modifying the original.
.atDay( 1 ) // Returns a `LocalDate` object.
.getMonth() // Returns a `Month` enum object.
.getDisplayName( // Automatically localize the name of the month.
TextStyle.FULL , // How long or abbreviated do you want the translation.
Locale.US // Or Locale.CANADA_FRENCH and so on.
) // Returns a `String` object.
January
YearQuarter in ThreeTen-ExtraThe ThreeTen-Extra library has a class you might find useful for this work: YearQuarter.
Get the current quarter. We need a time zone to determine the current date, and therefore the current quarter. For any given moment, the date varies around the globe by time zone.
ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
YearQuarter currentYearQuarter = YearQuarter.now( z ) ;
But you want to determine a quarter by parsing a string.
If you have a string styled similar to ISO 8601 (the standard does not actually specify quarters) YYYY-Qq then YearQuarter can directly parse.
String input = "2020-Q1" ;
YearQuarter yearQuarter = YearQuarter.parse( input ) ;
If you have only the quarter part without the year, use Quarter enum. If your input string is Q1 and such, use valueOf to retrieve the matching enum object.
String input = "Q1" ;
Quarter quarter = Quarter.valueOf( input ) ;
If you have a number instead of a string, that is, 1 or 2 or 3 or 4, then use static method Quarter.of. By the way, in your own code you should be passing around these Quarter objects rather than a mere integer or string, to make your code more self-documenting, to provide type-safety, and to ensure valid values.
int input = 1 ; // Domain: 1, 2, 3, 4.
Quarter quarter = Quarter.of( input ) ;
Apply that Quarter instance to our current YearQuarter instance to get another YearQuarter instance. These classes use the immutable objects pattern, so we are not modifying existing instance, we are generating new instances.
YearQuarter yearQuarter = currentYearQuarter.with( quarter ) ;
yearQuarter.toString(): 2019-Q1
Get first date (LocalDate), and year-month (YearMonth), and Month enum object, from that year-quarter.
LocalDate firstDate = yearQuarter.atDay( 1 ) ;
YearMonth yearMonth1 = YearMonth.from( firstDate ) ;
YearMonth yearMonth2 = yearMonth1.plusMonths( 1 ) ;
YearMonth yearMonth3 = yearMonth1.plusMonths( 2 ) ;
Generate a string containing the automatically localized name of month.
Locale locale = Locale.US ; // Or Locale.CANADA_FRENCH and so on.
String output1 = yearMonth1.getMonth().getDisplayName( TextStyle.FULL , locale ) ;
January
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.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With