cálculo del último día del mes

144

Tengo problemas con el cálculo de cuándo el próximo último día del mes es para una notificación que está programada para ser enviada.

Aquí está mi código:

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

Esta es la línea que causa problemas que creo:

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

¿Cómo puedo usar el Calendario para configurar correctamente el último día del próximo mes para la notificación?

OakvilleWork
fuente
1
Ajústelo al primer día del mes siguiente y luego retroceda un día.
Bill
Un SSCCE facilitaría responder esta pregunta. ¿Qué problema estás viendo realmente?
ADN

Respuestas:

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

Esto devuelve el máximo real para el mes actual. Por ejemplo, ahora es febrero del año bisiesto, por lo que devuelve 29 como int.

AlexR
fuente
gracias, necesito obtener el máximo para el próximo mes, sin embargo, también tiene que funcionar para que pueda ser el primer día del mes, ya que esa parte funciona ahora. gracias de nuevo por su tiempo y esfuerzo
OakvilleWork
44
No es un problema ActualMaximumpara cada fecha. Simplemente pase la instancia del calendario a la fecha requerida antes de llamar getActualMaximum. La primera fecha se puede obtener porgetActualMinimum()
AlexR
ok muchas gracias Alex, así que si deseo pasar la instancia a un mes por delante y averiguar el getActualMaximum para ese mes, ¿cómo lo haría? aplausos
OakvilleWork
¿sería este Alex? nextNotifTime.roll (Calendar.MONTH, verdadero); nextNotifTime.getActualMaximum (Calendar.DAY_OF_MONTH);
OakvilleWork
También configuré la fecha y hora lista después de esto: nextNotification.setReadyDateTime(nextNotifTime.getTime()); por lo tanto, ¿no debería hacer esto en su lugar nextNotifTime.roll(Calendar.MONTH, true); nextNotifTime.getActualMaximum(Calendar.DAY_OF_MONTH); nextNotifTime.setTime(Calendar.DAY_OF_MONTH); :?
OakvilleWork
61

java.time.temporal.TemporalAdjusters.lastDayOfMonth ()

Usando la java.timebiblioteca integrada en Java 8, puede usar la TemporalAdjusterinterfaz. Nos encontramos con una aplicación lista para su uso en la TemporalAdjustersclase de utilidad: 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

Si necesita agregar información de tiempo, puede usar cualquier disponible LocalDatepara la LocalDateTimeconversión como

lastDay.atStartOfDay(); //2015-11-30T00:00
Przemek
fuente
Buena respuesta. Pero esa última parte es débil, lo que sugiere el uso de LocalDateTime. Esa clase carece de cualquier concepto de zona horaria o desplazamiento desde UTC. Por lo tanto, no puede determinar cuándo comienza un día en particular en un lugar en particular. Esa clase solo funciona para días genéricos de 24 horas, no para días del mundo real. Para los días reales, especifique una zona horaria: ZonedDateTime zdt = now.atStartOfDay( ZoneId.of( "Asia/Tokyo" ) ) ;. Algunos días en algunos lugares pueden comenzar a una hora diferente, como 01:00, debido a anomalías como el horario de verano (DST).
Basil Bourque
35

Y para obtener el último día como objeto Fecha:

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

Date lastDayOfMonth = cal.getTime();
otro más
fuente
22

Puede configurar el calendario al primero del próximo mes y luego restar un día.

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

Después de ejecutar este código, nextNotifTime se establecerá en el último día del mes actual. Tenga en cuenta que si hoy es el último día del mes, el efecto neto de este código es que el objeto Calendario permanece sin cambios.

Mike Deck
fuente
17

Lo siguiente siempre dará resultados adecuados:

    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
fuente
¿Por qué es necesario? ¿Podría decirme :)
Frank Fang
1
Sí, las pruebas fallaron por este motivo exacto. El problema es que, a menos que se establezca DAY_OF_MONTH, el valor predeterminado será el actual.
ppearcy
7

Usar la última java.timebiblioteca aquí es la mejor solución:

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

Alternativamente, puedes hacer:

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

Mira el getActualMaximum(int field)método del objeto Calendario.

Si configura su objeto Calendario para estar en el mes para el que está buscando la última fecha, getActualMaximum(Calendar.DAY_OF_MONTH)le dará el último día.

Bruno
fuente
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
fuente
1

Implementación de extensión de fecha 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
}

Puede llamar a la toEndOfMonthfunción en cada objeto Fecha comoDate().toEndOfMonth()

gokhan
fuente
44
Estas clases terribles fueron suplantadas hace años por las modernas clases java.time con la adopción de JSR 310. Sugerir estas clases heredadas en 2019 es un mal consejo.
Basil Bourque
No es un mal consejo. El java.time moderno está disponible para Android solo para API 26+. Todavía dependemos de java.util. * Cuando se admiten dispositivos antiguos (no tan).
Rafael Chagas