ostatni dzień obliczenia miesiąca

144

Mam problemy z obliczeniem, kiedy następny ostatni dzień miesiąca przypada na powiadomienie, które ma zostać wysłane.

Oto mój kod:

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

To jest linia powodująca problemy, które moim zdaniem:

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

Jak mogę użyć Kalendarza, aby poprawnie ustawić ostatni dzień następnego miesiąca dla powiadomienia?

OakvilleWork
źródło
1
Ustaw go na pierwszy dzień następnego miesiąca, a następnie cofnij o jeden dzień.
Bill
SSCCE będzie łatwiej odpowiedzieć na to pytanie. Jaki problem faktycznie widzisz?
DNA

Odpowiedzi:

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

Zwraca rzeczywiste maksimum dla bieżącego miesiąca. Na przykład teraz jest luty roku przestępnego, więc zwraca 29 jako int.

AlexR
źródło
dzięki, muszę uzyskać maksimum na następny miesiąc, ale również musi działać, że może to być również pierwszy dzień miesiąca, ponieważ ta część działa teraz. jeszcze raz dziękuję za poświęcony czas i wysiłek
OakvilleWork
4
Nie ma problemu z umówieniem się ActualMaximumna każdą randkę. Po prostu przenieś instancję kalendarza do wymaganej daty przed zadzwonieniem getActualMaximum. Pierwszą datę można uzyskać przezgetActualMinimum()
AlexR
ok, wielkie dzięki Alex, więc jeśli chcę przesunąć instancję na miesiąc do przodu i dowiedzieć się, jaka jest wartość getActualMaximum w tym miesiącu, jak mam to zrobić? pozdrawiam
OakvilleWork
czy to będzie to Alex? nextNotifTime.roll (Calendar.MONTH, true); nextNotifTime.getActualMaximum (Calendar.DAY_OF_MONTH);
OakvilleWork
także ustawić czas po tej dacie gotowe: nextNotification.setReadyDateTime(nextNotifTime.getTime()); w związku z tym nie powinien to zrobić w zamian: nextNotifTime.roll(Calendar.MONTH, true); nextNotifTime.getActualMaximum(Calendar.DAY_OF_MONTH); nextNotifTime.setTime(Calendar.DAY_OF_MONTH); ?
OakvilleWork
61

java.time.temporal.TemporalAdjusters.lastDayOfMonth ()

Korzystając z java.timebiblioteki wbudowanej w Javę 8, możesz korzystać z TemporalAdjusterinterfejsu. Znaleźć implementacja gotowy do użycia w TemporalAdjustersklasie użytkowej: 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

Jeśli chcesz dodać informacje o czasie, możesz użyć dowolnego dostępnego LocalDatedo LocalDateTimekonwersji, takiego jak

lastDay.atStartOfDay(); //2015-11-30T00:00
Przemek
źródło
Dobra odpowiedź. Ale ta ostatnia część jest słaba, co sugeruje użycie LocalDateTime. W tej klasie brakuje jakiejkolwiek koncepcji strefy czasowej lub przesunięcia względem czasu UTC. Nie może więc określić, kiedy konkretny dzień w określonym miejscu faktycznie się zaczyna. Ta klasa działa tylko w zwykłych ogólnych 24-godzinnych dniach, a nie w rzeczywistych dniach. Dla prawdziwych dni, należy określić strefę czasową: ZonedDateTime zdt = now.atStartOfDay( ZoneId.of( "Asia/Tokyo" ) ) ;. Niektóre dni w niektórych miejscach mogą rozpoczynać się o innej godzinie, na przykład o godzinie 01:00, z powodu anomalii, takich jak czas letni (DST).
Basil Bourque
35

Aby otrzymać ostatni dzień jako obiekt Date:

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

Date lastDayOfMonth = cal.getTime();
yetAnotherSE
źródło
22

Możesz ustawić kalendarz na pierwszy dzień następnego miesiąca, a następnie odjąć dzień.

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

Po uruchomieniu tego kodu nextNotifTime zostanie ustawiony na ostatni dzień bieżącego miesiąca. Pamiętaj, że jeśli dzisiaj jest ostatni dzień miesiąca, efekt netto tego kodu jest taki, że obiekt Calendar pozostaje niezmieniony.

Mike Deck
źródło
17

Poniższe zawsze zapewniają prawidłowe wyniki:

    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
źródło
Dlaczego jest to konieczne? Czy możesz mi powiedzieć :)
Frank Fang
1
Tak, po prostu testy zakończyły się niepowodzeniem z tego dokładnego powodu powyżej. Problem polega na tym, że jeśli DAY_OF_MONTH nie jest ustawiony, domyślnie będzie bieżący.
ppearcy
7

Korzystanie z najnowszej java.timebiblioteki tutaj jest najlepszym rozwiązaniem:

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

Alternatywnie możesz:

LocalDate endOfMonth = date.withDayOfMonth(date.lengthOfMonth());
satnam
źródło
2

Spójrz na getActualMaximum(int field)metodę obiektu Calendar.

Jeśli ustawisz obiekt kalendarza na miesiąc, dla którego szukasz ostatniej daty, getActualMaximum(Calendar.DAY_OF_MONTH)poda ci ostatni dzień.

Bruno
źródło
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
źródło
1

Wdrożenie rozszerzenia daty Kotlin przy użyciu 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
}

Możesz wywołać toEndOfMonthfunkcję dla każdego obiektu Date, takiego jakDate().toEndOfMonth()

gokhan
źródło
4
Te okropne klasy zostały wyparte lata temu przez nowoczesne klasy java.time wraz z przyjęciem JSR 310. Sugerowanie tych klas starszych w 2019 roku jest kiepską radą.
Basil Bourque
To nie jest zła rada. Nowoczesne java.time jest dostępne dla Androida tylko dla API 26+. Nadal polegamy na java.util. *, Gdy obsługujemy (nie tak) stare urządzenia.
Rafael Chagas