Jak odjąć X dni od daty za pomocą kalendarza Java?

180

Czy ktoś zna prosty sposób używania kalendarza Java do odejmowania X dni od daty?

Nie udało mi się znaleźć żadnej funkcji, która pozwala mi bezpośrednio odjąć X dni od daty w Javie. Czy ktoś może wskazać mi właściwy kierunek?

fmsf
źródło
Do Twojej wiadomości, kłopotliwe stare klasy daty i czasu, takie jak java.util.Calendarteraz, są starsze , wyparte przez klasy java.time . Zobacz samouczek Oracle .
Basil Bourque,

Odpowiedzi:

309

Zaczerpnięte z dokumentów tutaj :

Dodaje lub odejmuje określony czas w danym polu kalendarza, zgodnie z regułami kalendarza. Na przykład, aby odjąć 5 dni od bieżącej godziny kalendarza, możesz to zrobić, dzwoniąc:

Calendar calendar = Calendar.getInstance(); // this would default to now
calendar.add(Calendar.DAY_OF_MONTH, -5).
Anson Smith
źródło
1
Po prostu zachowaj ostrożność, ponieważ nie zawsze toczy się ona tak, jak tego oczekujesz.
Carson
1
Te odpowiedzi mają wiele pozytywnych opinii, ale czy można z nich bezpiecznie korzystać? albo to jest lepsze: stackoverflow.com/a/10796111/948268
Kuldeep Jain
44
Nie użyłbyś Calendar.DAY_OF_MONTHw tym przypadku, ponieważ nie poradzi sobie z rolką między miesiącami, jak chcesz. Calendar.DAY_OF_YEARZamiast tego użyj
algorytmy
4
Powinno to być bezpieczne, najwyraźniej podczas dodawania nie ma znaczenia, czy to DAY_OF_MONTH, czy DAY_OF_YEAR stackoverflow.com/q/14065198/32453
rogerdpack
@carson jakie są niektóre problemy z tym podejściem?
tatmanblue
38

Możesz użyć tej addmetody i przekazać jej liczbę ujemną. Można również napisać prostszą metodę, która nie korzysta z Calendarklasy, taką jak poniżej

public static void addDays(Date d, int days)
{
    d.setTime( d.getTime() + (long)days*1000*60*60*24 );
}

Pobiera wartość znacznika czasu daty (milisekund od epoki) i dodaje odpowiednią liczbę milisekund. Możesz przekazać ujemną liczbę całkowitą dla parametru dni, aby wykonać odejmowanie. Byłoby to prostsze niż „właściwe” rozwiązanie kalendarza:

public static void addDays(Date d, int days)
{
    Calendar c = Calendar.getInstance();
    c.setTime(d);
    c.add(Calendar.DATE, days);
    d.setTime( c.getTime().getTime() );
}

Zauważ, że oba te rozwiązania zmieniają Dateobiekt przekazywany jako parametr zamiast zwracania całkowicie nowego Date. Każdą funkcję można łatwo zmienić, aby zrobić to w drugą stronę, jeśli jest to pożądane.

Eli Courtwright
źródło
3
Nie wierz, że .setTime i .add są statycznymi metodami Kalendarza. Powinieneś używać zmiennej instancji c.
Edward
@ Edward: masz rację, dziękuję za zwrócenie uwagi na mój błąd. Naprawiłem kod, aby działał poprawnie.
Eli Courtwright
3
Bądź ostrożny z pierwszym sposobem, na przykład w przypadku dni leapyear może się nie powieść: stackoverflow.com/a/1006388/32453
rogerdpack
Do Twojej wiadomości, kłopotliwe stare klasy daty i czasu, takie jak java.util.Calendarteraz, są starsze , wyparte przez klasy java.time . Zobacz samouczek Oracle .
Basil Bourque,
36

Odpowiedź Ansona zadziała w przypadku prostego przypadku, ale jeśli zamierzasz wykonać bardziej złożone obliczenia daty, polecam sprawdzenie Jody Time . Ułatwi ci to życie.

Do Twojej wiadomości w Joda Time możesz to zrobić

DateTime dt = new DateTime();
DateTime fiveDaysEarlier = dt.minusDays(5);
Mike Deck
źródło
1
@ShahzadImam, sprawdź DateTimeFormat . Umożliwi to konwersję instancji DateTime na ciągi znaków przy użyciu dowolnych formatów. Jest bardzo podobny do klasy java.text.DateFormat.
Mike Deck
1
Począwszy od wersji Java 8 rozważ użycie java.time docs.oracle.com/javase/8/docs/api/java/time/…
toidiu
Do Twojej dyspozycji jest projekt Joda-Time trybie konserwacji , a zespół doradza migrację do klas java.time . Zobacz samouczek Oracle .
Basil Bourque,
19

tl; dr

LocalDate.now().minusDays( 10 )

Lepiej określić strefę czasową.

LocalDate.now( ZoneId.of( "America/Montreal" ) ).minusDays( 10 )

Detale

Stare klasy daty i czasu dołączone do wczesnych wersji Javy, takie jak java.util.Date/ .Calendar, okazały się kłopotliwe, mylące i wadliwe. Unikaj ich.

java.time

Java 8 i nowsze wersje zastępują te stare klasy za pomocą nowego środowiska java.time. Zobacz samouczek . Zdefiniowany przez JSR 310 , zainspirowany Joda-Time i rozszerzony przez projekt ThreeTen-Extra . Projekt ThreeTen-Backport przenosi klasy na Java 6 i 7; projekt ThreeTenABP na Androida.

Pytanie jest niejasne, niejasne, czy wymaga tylko daty, czy daty.

LocalDate

Tylko na randkę, bez pory dnia, skorzystaj z LocalDateklasy. Pamiętaj, że strefa czasowa ma kluczowe znaczenie dla ustalenia daty, takiej jak „dzisiaj”.

LocalDate today = LocalDate.now( ZoneId.of( "America/Montreal" ) );
LocalDate tenDaysAgo = today.minusDays( 10 );

ZonedDateTime

Jeśli miałeś na myśli datę i godzinę, skorzystaj z Instantzajęć, aby uzyskać chwilę na osi czasu w UTC . Stamtąd dostosuj się do strefy czasowej, aby uzyskaćZonedDateTime obiekt.

Instant now = Instant.now();  // UTC.
ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );
ZonedDateTime tenDaysAgo = zdt.minusDays( 10 );

Tabela typów daty i godziny w Javie, zarówno nowoczesnych, jak i starszych.


O java.time

Środowisko java.time jest wbudowane w Javę 8 i nowsze wersje . Klasy te zastępują kłopotliwe, starsze, klasowe daty i godziny, takie jakjava.util.Date , Calendar, i SimpleDateFormat.

The Joda-Time , teraz w trybie konserwacji , zaleca migrację doklas java.time .

Aby dowiedzieć się więcej, zobacz samouczek Oracle . I przeszukaj stos przepełnienia dla wielu przykładów i wyjaśnień. Specyfikacja to JSR 310 .

Możesz wymienić java.time obiekty bezpośrednio ze swoją bazą danych. Użyj sterownika JDBC zgodnego z JDBC 4.2 lub nowszym. Nie potrzeba ciągów, nie ma potrzeby java.sql.*klas.

Gdzie można uzyskać klasy java.time?

Projekt ThreeTen-Extra rozszerza java.time o dodatkowe klasy. Ten projekt jest poligonem doświadczalnym dla ewentualnych przyszłych dodatków do java.time. Można znaleźć kilka przydatnych klas tutaj takie jak Interval, YearWeek, YearQuarter, i więcej .

Basil Bourque
źródło
Jest to znacznie lepsza poprawa w porównaniu z innymi opcjami (jest również znacznie nowsza i wykorzystuje nowe metody). Jeśli odwiedzasz teraz ten problem, to jest właściwa droga.
Shourya Bansal
5

Zamiast pisać własne, addDaysjak sugeruje Eli, wolałbym korzystać DateUtilsz Apache . Jest to przydatne, zwłaszcza gdy musisz użyć go w wielu miejscach w swoim projekcie.

Interfejs API mówi:

addDays(Date date, int amount)

Dodaje liczbę dni do daty zwracając nowy obiekt.

Zauważ, że zwraca nowy Dateobiekt i nie wprowadza zmian w samym poprzednim.

Risav Karna
źródło
2

Można to łatwo zrobić w następujący sposób

Calendar calendar = Calendar.getInstance();
        // from current time
        long curTimeInMills = new Date().getTime();
        long timeInMills = curTimeInMills - 5 * (24*60*60*1000);    // `enter code here`subtract like 5 days
        calendar.setTimeInMillis(timeInMills);
        System.out.println(calendar.getTime());

        // from specific time like (08 05 2015)
        calendar.set(Calendar.DAY_OF_MONTH, 8);
        calendar.set(Calendar.MONTH, (5-1));
        calendar.set(Calendar.YEAR, 2015);
        timeInMills = calendar.getTimeInMillis() - 5 * (24*60*60*1000);
        calendar.setTimeInMillis(timeInMills);
        System.out.println(calendar.getTime());
rab
źródło
Ta odpowiedź jest nieodpowiednia, ponieważ używa okropnych starych klas daty i czasu, które są teraz starsze, wyparte przez klasy java.time. Ponadto ten kod ignoruje kluczową kwestię strefy czasowej, naiwnie zakładając 24-godzinne dni. Innym problemem jest użycie klasy data-czas do rozwiązania problemu tylko z datą. Korzystanie LocalDatejest znacznie prostsze i bardziej odpowiednie.
Basil Bourque,
1

Ktoś polecił Joda Time, więc - korzystałem z tej klasy CalendarDate http://calendardate.sourceforge.net

Jest to nieco konkurencyjny projekt do Jody Time, ale o wiele bardziej podstawowy na zaledwie 2 lekcjach. Jest bardzo poręczny i działał świetnie dla tego, czego potrzebowałem, ponieważ nie chciałem używać pakietu większego niż mój projekt. W przeciwieństwie do odpowiedników w Javie, najmniejszą jednostką jest dzień, więc jest to naprawdę data (nie sprowadzająca się do milisekund lub czegoś takiego). Po utworzeniu daty wszystko, co musisz zrobić, to odjąć coś w rodzaju myDay.addDays (-5), aby cofnąć się o 5 dni. Możesz go użyć, aby znaleźć dzień tygodnia i podobne rzeczy. Inny przykład:

CalendarDate someDay = new CalendarDate(2011, 10, 27);
CalendarDate someLaterDay = today.addDays(77);

I:

//print 4 previous days of the week and today
String dayLabel = "";
CalendarDate today = new CalendarDate(TimeZone.getDefault());
CalendarDateFormat cdf = new CalendarDateFormat("EEE");//day of the week like "Mon"
CalendarDate currDay = today.addDays(-4);
while(!currDay.isAfter(today)) {
    dayLabel = cdf.format(currDay);
    if (currDay.equals(today))
        dayLabel = "Today";//print "Today" instead of the weekday name
    System.out.println(dayLabel);
    currDay = currDay.addDays(1);//go to next day
}
mikato
źródło
1

Uważam, że czysty i przyjemny sposób na odejmowanie lub dodawanie dowolnej jednostki czasu (miesiące, dni, godziny, minuty, sekundy, ...) można osiągnąć za pomocą klasy java.time.Instant .

Przykład odejmowania 5 dni od bieżącej godziny i uzyskiwania wyniku jako Data:

new Date(Instant.now().minus(5, ChronoUnit.DAYS).toEpochMilli());

Kolejny przykład odejmowania 1 godziny i dodawania 15 minut:

Date.from(Instant.now().minus(Duration.ofHours(1)).plus(Duration.ofMinutes(15)));

Jeśli potrzebujesz większej dokładności, Instancja mierzy do nanosekund. Metody manipulowania częścią nanosekundową:

minusNano()
plusNano()
getNano()

Pamiętaj też, że data nie jest tak dokładna jak błyskawiczna.

Yordan Boev
źródło
1
Lub, w zależności od gustu, nieco krócej:Date.from(Instant.now().minus(5, ChronoUnit.DAYS))
Ole VV,
1
Miły! Masz rację. Naprawdę zaktualizowałem odpowiedź, aby użyć tego, a także pokazuję użycie klasy java.time.Duration, która w tym przypadku jest również bardzo silna.
Yordan Boev,
0

Drugie rozwiązanie Eli Courtwright jest złe, powinno być:

Calendar c = Calendar.getInstance();
c.setTime(date);
c.add(Calendar.DATE, -days);
date.setTime(c.getTime().getTime());
użytkownik178973
źródło
2
Nazwa funkcji w rozwiązaniu Heli to addDays; jego rozwiązanie jest poprawne. Jeśli chcesz odjąć dni od daty, podaj wartość ujemną dla days.
Thomas Upton
Cóż, to jest coś, co programista musi określić podczas tworzenia funkcji, prawda? : P
user178973,