You can use the class java.time.temporal.TemporalAdjusters
, that has the method lastInMonth
, passing as parameter a java.time.DayOfWeek
corresponding to Friday. Ex:
// alguma data em janeiro (1 de janeiro de 2019)
LocalDate data = LocalDate.of(2019, 1, 1);
// ajustar para a última sexta-feira do mês
LocalDate ultimaSexta = data.with(TemporalAdjusters.lastInMonth(DayOfWeek.FRIDAY));
System.out.println(ultimaSexta); // 2019-01-25
An important point is that the API classes java.time
sane immutable. This means that methods that modify some information (such as the method with
above) always return another instance, so you should not forget to store the returned value in some variable. If you want, you can even use the same:
// data passa a ser a última sexta-feira do mês
data = data.with(TemporalAdjusters.lastInMonth(DayOfWeek.FRIDAY));
But notice that LocalDate
requires having the day, month and year. As in your CSV there is only the month and the year, it is not possible to create a LocalDate
directly (unless you artificially put some value to the day).
An alternative is to do the Parsing of its string in "mm/yyyy" format for a java.time.YearMonth
, which is a class that has only the month and the year. And to transform a String
in YearMonth
, we use a java.time.format.DateTimeFormatter
:
import static java.time.DayOfWeek.FRIDAY;
import static java.time.temporal.TemporalAdjusters.lastInMonth;
// String no formato mm/yyyy
String s = "01/2019"; // janeiro de 2019
// definir o formato para mês/ano (MM/yyyy)
DateTimeFormatter parser = DateTimeFormatter.ofPattern("MM/yyyy");
YearMonth mesAno = YearMonth.parse(s, parser);
// setar um dia arbitrário (1) e depois ajustar para a última sexta-feira do mês
LocalDate ultimaSexta = mesAno.atDay(1).with(lastInMonth(FRIDAY));
System.out.println(ultimaSexta); // 2019-01-25
First I create the DateTimeFormatter
indicating the format of String
(in the case, "month/year"). Attention to use M
uppercase and y
because it makes a difference (see in documentation that m
tiny and Y
capital represent different fields, and if you use them erroneously, won’t work).
Then I use the method YearMonth.parse
to create the YearMonth
corresponding. This class only has the month and year, without any information about the day. In this case, the instance obtained corresponds to January 2019.
Then I die an arbitrary day (using the method atDay
), to create a LocalDate
. In case, I used the day 1
, because it is a more guaranteed value that every month have (if I use the day 31
, for example, not every month have and that can make a mistake).
Having the LocalDate
, I can use with(lastInMonth(FRIDAY))
(notice that I used import static
to make the code a little more succinct and readable). The result is the last Friday of the month.
It may seem redundant to set the day to 1 and only then adjust for the last Friday of the month, but if I use lastInMonth
directly on YearMonth
, makes a mistake:
mesAno.with(lastInMonth(FRIDAY));
This code throws an exception:
java.time.temporal.Unsupportedtemporaltypeexception: Unsupported field: Dayofmonth
This is because lastInMonth
changes the day of the month, but the class YearMonth
has no information about the day (only about the month and year, hence the error of "Unsupported field" - "Unsupported field"). It is therefore necessary to turn it into a LocalDate
before.
Alternative
If you don’t want to create a YearMonth
, it is possible to create a LocalDate
directly. For this, just set an arbitrary day (as we did above with day 1) and use a java.time.format.DateTimeFormatterBuilder
:
DateTimeFormatter parser = new DateTimeFormatterBuilder()
// formato "mês/ano"
.appendPattern("MM/yyyy")
// definir o valor default para o dia do mês = 1
.parseDefaulting(ChronoField.DAY_OF_MONTH, 1)
// cria o DateTimeFormatter
.toFormatter();
// faz o parse diretamente para LocalDate (o dia é setado para 1)
LocalDate data = LocalDate.parse("01/2019", parser);
// ajusta para a última sexta-feira do mês
LocalDate ultimaSexta = data.with(lastInMonth(FRIDAY));
System.out.println(ultimaSexta); // 2019-01-25
First we define the format (the same used in the previous example: month/year -> MM/yyyy
). Then we use the method parseDefaulting
, which serves to define the value a field will have if it is not present.
To define which field should have the value default, I used a java.time.temporal.ChronoField
, which is a Enum which has several predefined constants for the most common fields of dates and times. How I want to set a value default to the day of the month, used the field DAY_OF_MONTH
. Then I define the value that this field will have: in this case, it is 1
.
So just use the method LocalDate.parse
and directly obtain a LocalDate
. The month and year will have the values that are in String
, while the day will be 1
. After that, just adjust to the last Friday of the month, in the same way that is done in the previous examples, with with(lastInMonth(FRIDAY))
.
Too complete! Thank you very much!
– Thiago Cunha