cálculo do último dia do mês

144

Estou tendo problemas com o cálculo de quando o próximo último dia do mês é para uma notificação que está programada para ser enviada.

Aqui está o meu código:

RecurrenceFrequency recurrenceFrequency = notification.getRecurrenceFrequency();
Calendar nextNotifTime = Calendar.getInstance();

Esta é a linha que causa problemas que acredito:

nextNotifTime.add(recurrenceFrequency.getRecurrencePeriod(), 
                  recurrenceFrequency.getRecurrenceOffset());

Como posso usar o calendário para definir corretamente o último dia do próximo mês para a notificação?

OakvilleWork
fonte
1
Defina-o para o primeiro dia do próximo mês e depois reverta um dia.
Bill
Um SSCCE facilitaria a resposta a essa pergunta. Que problema você está realmente vendo?
DNA

Respostas:

347
Calendar.getInstance().getActualMaximum(Calendar.DAY_OF_MONTH);

Isso retorna o máximo real para o mês atual. Por exemplo, agora é fevereiro do ano bissexto, retornando 29 como int.

AlexR
fonte
obrigado, eu preciso obter o máximo para o próximo mês, no entanto, ainda é necessário que ele possa ser o primeiro dia do mês, pois essa parte funciona agora. Obrigado novamente por seu tempo e esforço
OakvilleWork
4
Não é um problema para obter ActualMaximumpara cada data. Basta rolar a instância do calendário para a data necessária antes de ligar getActualMaximum. A primeira data pode ser obtida porgetActualMinimum()
AlexR
ok, muito obrigado Alex, por isso, se eu desejar rolar a instância para um mês à frente e descobrir o getActualMaximum desse mês, como eu faria isso? vivas
OakvilleWork
seria este Alex? nextNotifTime.roll (Calendar.MONTH, true); nextNotifTime.getActualMaximum (Calendar.DAY_OF_MONTH);
precisa
Também defino a hora da data pronta após isso: nextNotification.setReadyDateTime(nextNotifTime.getTime()); portanto, não devo fazer isso nextNotifTime.roll(Calendar.MONTH, true); nextNotifTime.getActualMaximum(Calendar.DAY_OF_MONTH); nextNotifTime.setTime(Calendar.DAY_OF_MONTH); :?
precisa
61

java.time.temporal.TemporalAdjusters.lastDayOfMonth ()

Usando a java.timebiblioteca integrada no Java 8, você pode usar a TemporalAdjusterinterface. Encontramos uma implementação pronta para uso na TemporalAdjustersclasse de utilitário: lastDayOfMonth.

import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;

LocalDate now = LocalDate.now(); //2015-11-23
LocalDate lastDay = now.with(TemporalAdjusters.lastDayOfMonth()); //2015-11-30

Se você precisar adicionar informações de horário, poderá usar qualquer disponível LocalDatepara LocalDateTimeconversão, como

lastDay.atStartOfDay(); //2015-11-30T00:00
Przemek
fonte
Boa resposta. Mas essa última parte é fraca, sugerindo o uso de LocalDateTime. Essa classe não possui nenhum conceito de fuso horário ou deslocamento do UTC. Portanto, não é possível determinar quando um determinado dia em um determinado local realmente começa. Essa classe funciona apenas para dias genéricos simples de 24 horas, não para dias do mundo real. Para os dias de reais, especificar um fuso horário: ZonedDateTime zdt = now.atStartOfDay( ZoneId.of( "Asia/Tokyo" ) ) ;. Alguns dias em alguns locais podem começar em um horário diferente, como 01:00, devido a anomalias como horário de verão (DST).
Basil Bourque
35

E para obter o último dia como objeto Data:

Calendar cal = Calendar.getInstance();
cal.set(Calendar.DATE, cal.getActualMaximum(Calendar.DATE));

Date lastDayOfMonth = cal.getTime();
yetAnotherSE
fonte
22

Você pode definir o calendário para o primeiro mês que vem e subtrair um dia.

Calendar nextNotifTime = Calendar.getInstance();
nextNotifTime.add(Calendar.MONTH, 1);
nextNotifTime.set(Calendar.DATE, 1);
nextNotifTime.add(Calendar.DATE, -1);

Depois de executar esse código, nextNotifTime será definido como o último dia do mês atual. Lembre-se de que hoje é o último dia do mês, o efeito líquido desse código é que o objeto Calendário permaneça inalterado.

Mike Deck
fonte
17

A seguir, sempre haverá resultados adequados:

    Calendar cal = Calendar.getInstance();
    cal.set(Calendar.MONTH, ANY_MONTH);
    cal.set(Calendar.YEAR, ANY_YEAR);
    cal.set(Calendar.DAY_OF_MONTH, 1);// This is necessary to get proper results
    cal.set(Calendar.DATE, cal.getActualMaximum(Calendar.DATE));
    cal.getTime();
Safvan Kothawala
fonte
Por que é necessário? Poderia dizer-me :)
Frank fang
1
Sim, apenas os testes falharam por esse exato motivo acima. O problema é que, a menos que DAY_OF_MONTH esteja definido, o padrão será atual.
ppearcy
7

Usar a java.timebiblioteca mais recente aqui é a melhor solução:

LocalDate date = LocalDate.now();
LocalDate endOfMonth = date.with(TemporalAdjusters.lastDayOfMonth());

Como alternativa, você pode fazer:

LocalDate endOfMonth = date.withDayOfMonth(date.lengthOfMonth());
satnam
fonte
2

Veja o getActualMaximum(int field)método do objeto Calendário.

Se você definir o objeto Calendário como o mês para o qual está buscando a última data, getActualMaximum(Calendar.DAY_OF_MONTH)o último dia será exibido.

Bruno
fonte
2
        SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
        Date date = sdf.parse("11/02/2016");

        Calendar calendar = Calendar.getInstance();  
        calendar.setTime(date);  

        System.out.println("First Day Of Month : " + calendar.getActualMinimum(Calendar.DAY_OF_MONTH));  
        System.out.println("Last Day of Month  : " + calendar.getActualMaximum(Calendar.DAY_OF_MONTH)); 
Kishore Kumar
fonte
1

Implementação de extensão de data Kotlin usando java.util.Calendar

fun Date.toEndOfMonth(): Date {
    return Calendar.getInstance().apply {
        time = this@toEndOfMonth
    }.toEndOfMonth().time
}

fun Calendar.toEndOfMonth(): Calendar {
    set(Calendar.DAY_OF_MONTH, getActualMaximum(Calendar.DAY_OF_MONTH))
    return this
}

Você pode chamar a toEndOfMonthfunção em cada objeto Date, comoDate().toEndOfMonth()

gokhan
fonte
4
Essas classes terríveis foram substituídas há anos pelas modernas classes java.time com a adoção do JSR 310. Sugerir essas classes herdadas em 2019 é um péssimo conselho.
Basil Bourque
Não é um mau conselho. O java.time moderno está disponível para Android apenas para API 26+. Ainda dependemos do java.util. * Para oferecer suporte a dispositivos antigos (não muito).
Rafael Chagas