Executando verificação de segurança...
1

Muito bom! Alguns detalhes para complementar:

Os métodos withYear, withMonth, etc, na verdade não modificam a data. As classes do java.time são imutáveis, então estes métodos sempre retornam outra instância com o valor modificado.

Por isso se vc fizer:

LocalDate data = LocalDate.now();
data.withYear(2000);
System.out.println(data);

Não vai mudar o ano para 2000, vai continuar imprimindo a data atual. Isso porque withYear retornou outra instância de LocalDate, que não foi atribuída a nenhuma variável, e portanto "se perdeu".

Para obter a data com ano alterado, deve-se usar o valor retornado:

LocalDate data = LocalDate.now();
LocalDate outra = data.withYear(2000);
System.out.println(outra);

O mesmo vale para os métodos plusXXX e minusXXX, eles sempre retornam outra instância com o resultado.

Outro ponto é que não precisaria ficar chamando now toda hora, então em vez disso:

LocalDate hoje = LocalDate.now();
LocalDate amanha = LocalDate.now().plusDays(1);

Poderia ser isso:

LocalDate hoje = LocalDate.now();
LocalDate amanha = hoje.plusDays(1);

Na maioria dos casos o resultado será o mesmo, mas tem um corner case: o código pode rodar muito próximo da meia-noite, então o primeiro now retorna um dia e o segundo retorna outro. O resultado é que amanha acabará com uma data dois dias à frente de hoje.


Quanto a este exemplo:

LocalDate hoje = LocalDate.now();
YearMonth yearMonth = YearMonth.from(hoje);
System.out.println(yearMonth.getMonth() + " " + yearMonth.getYear());

Se a ideia era apenas obter o mês e ano, não precisaria usar YearMonth, poderia obter diretamente:

LocalDate hoje = LocalDate.now();
System.out.println(hoje.getMonth() + " " + hoje.getYear());

O uso de YearMonth é quando vc precisa apenas desses dois campos (por exemplo, para data de expiração de cartão de crédito, que possui somente ano e mês).


Para formatação, eu tenho preferido usar uuuu em vez de yyyy para o ano. O motivo disto é que yyyy não funciona em caso de datas antes de Cristo. Para a maioria dos casos não faz diferença, pois ambos funcionam, mas nos casos em que faz diferença, o uuuu deve ser usado. Mais detalhes nesta resposta (em inglês).

Quanto a 30 de fevereiro, de fato não dá para criar usando LocalDate.of, mas e se tentarmos fazer o parsing?

DateTimeFormatter parser = DateTimeFormatter.ofPattern("dd/MM/uuuu");
LocalDate data = LocalDate.parse("30/02/2020", parser);
System.out.println(data); // 2020-02-29

Tentei fazer o parsing de 30 de fevereiro, e a data foi ajustada para o dia 29. Basicamente, é feito um "arredondamento" para o último dia válido do mês (lembrando que 2020 é ano bissexto: se não fosse, o ajuste seria feito para o dia 28).

Se a ideia é não aceitar datas inválidas e não fazer tal ajuste, basta mudar o ResolverStyle:

DateTimeFormatter parser = DateTimeFormatter.ofPattern("dd/MM/uuuu")
    .withResolverStyle(ResolverStyle.STRICT);
LocalDate data = LocalDate.parse("30/02/2020", parser);

Agora dá erro, porque a data é inválida:

java.time.format.DateTimeParseException: Text '30/02/2020' could not be parsed: Invalid date 'FEBRUARY 30'

Basicamente, existem 3 modos diferentes de tratar datas inválidas:

O modo LENIENT permite datas inválidas e faz ajustes automáticos. Por exemplo, 31/06/2017 é ajustado para 01/07/2017. Além disso, este modo aceita valores fora dos limites definidos para cada campo, como o dia 32, mês 15, etc. Por exemplo, 32/15/2017 é ajustado para 01/04/2018.

O modo SMART também faz alguns ajustes quando a data é inválida, então 31/06/2017 é interpretado como 30/06/2017. A diferença para LENIENT é que este modo não aceita valores fora dos limites dos campos (mês 15, dia 32, etc), então 32/15/2017 dá erro (lança um DateTimeParseException). É o modo default quando você cria um DateTimeFormatter.

O modo STRICT é o mais restrito: não aceita valores fora dos limites e nem faz ajustes quando a data é inválida, portanto 31/06/2017 e 32/15/2017 dão erro (lançam um DateTimeParseException).


Sobre o exemplo de Duration, só tem um pequeno detalhe na hora de mostrar os dados. Considere este exemplo:

// duração de 10 horas, 35 minutos e 20 segundos
Duration difference = Duration.ofHours(10).plusMinutes(35).plusSeconds(20);
System.out.printf("%s horas, %s minutos, %s segundos",
    difference.toHours(), difference.toMinutes(), difference.getSeconds());

A saída deveria ser "10 horas, 35 minutos e 20 segundos", mas na verdade foi:

10 horas, 635 minutos, 38120 segundos

Isso porque toMinutes retorna a quantidade total de minutos (o mesmo vale para getSeconds). Se quer quebrar em partes, pode usar os métodos toXXXPart, disponíveis a partir do Java 9:

Duration difference = Duration.ofHours(10).plusMinutes(35).plusSeconds(20);

// atenção: toXXXPart só funciona a partir do Java 9
System.out.printf("%s horas, %s minutos, %s segundos",
        difference.toHoursPart(), difference.toMinutesPart(), difference.toSecondsPart());

// para Java 8, tem que fazer na mão
long secs = difference.getSeconds();
long hours = secs / 3600;
secs %= 3600;
long mins = secs / 60;
secs %= 60;
System.out.printf("%s horas, %s minutos, %s segundos", hours, mins, secs);

Leitura complementar:

Carregando publicação patrocinada...