Jak uzyskać datę bez czasu w Javie?

235

Kontynuując pytanie programu Java dotyczące przepełnienia stosu , aby uzyskać bieżącą datę bez znacznika czasu :

Jaki jest najskuteczniejszy sposób na uzyskanie obiektu Date bez czasu? Czy jest jakiś inny sposób niż te dwa?

// Method 1
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date dateWithoutTime = sdf.parse(sdf.format(new Date()));

// Method 2
Calendar cal = Calendar.getInstance();
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
dateWithoutTime = cal.getTime();

Aktualizacja :

  1. Wiedziałem o Joda-Time ; Próbuję po prostu uniknąć dodatkowej biblioteki dla tak prostego (jak sądzę) zadania. Ale na podstawie dotychczasowych odpowiedzi Joda-Time wydaje się niezwykle popularny, więc mógłbym to rozważyć.

  2. Mówiąc wydajnie , chcę uniknąć Stringtworzenia tymczasowych obiektów, z których korzysta method 1, tymczasem method 2wydaje się, że to hack zamiast rozwiązania.

Rosdi Kasim
źródło
1
Wydajny? Czy potrzebujesz większej wydajności niż to, co zapewnia np. Metoda1?
Johan Sjöberg,
2
Co rozumiesz przez „wydajny”? Data jest w zasadzie długa na maszynie, naprawdę nie można tego zrobić w mniejszej ilości pamięci. Jeśli masz na myśli „wygodny”, czas JODA jest najlepszym rozwiązaniem.
millimoose
3
+1 Dokładnie pytanie, które miałem.
Alba Mendez
1
Podoba mi się metoda 2. Utwórz metodę statyczną w klasie narzędzi i po prostu użyj jej. Takie podejście stosuję od lat.
Wilson Freitas
1
Nitpicking twojej „aktualizacji 1”: gdyby było to „takie proste zadanie”, myślę, że Sun nie przyszedłby do tak przerażającego i nieefektywnego API, a ty (i wiele innych osób) nie zadałbyś tego pytania w ogóle ;-)
Flávio Etrusco

Odpowiedzi:

108

Czy jesteś absolutnie mają do użytku java.util.Date? Chciałbym dokładnie zaleca się użycie Joda czasu lub java.timepakiet z Java 8 zamiast. W szczególności, gdy Data i kalendarz zawsze stanowią szczególny moment w czasie, bez takiego pojęcia jak „tylko data” Joda czas ma mieć typ reprezentujący ten ( LocalDate). Twój kod będzie znacznie wyraźniejszy, jeśli będziesz w stanie używać typów reprezentujących to, co faktycznie próbujesz zrobić.

Istnieje wiele, wiele innych powodów, aby używać Joda Time lub java.timezamiast wbudowanych java.utiltypów - są to na ogół znacznie lepsze interfejsy API. W java.util.Daterazie potrzeby zawsze możesz przekonwertować na / z granicy własnego kodu, np. W celu interakcji z bazą danych.

Jon Skeet
źródło
75
W aplikacjach korporacyjnych nie zawsze mamy opcję dodawania / używania innych bibliotek. Doceniam wskaźnik do Joda Time, ale tak naprawdę nie jest to odpowiedź na pierwotny problem z pobraniem daty za pomocą standardowej Java. Dzięki. Poprawienie odpowiedzi Chathuranga.
noogrub
3
@noogrub: Odpowiedź Chathugrangi nie odpowiada na pierwotne pytanie, które dotyczy tego, jak uzyskać Dateobiekt - ta odpowiedź formatuje datę na ciąg, co nie jest tym samym. Zasadniczo pytanie o datę Datejest bezsensownym pytaniem bez dodatkowych informacji: strefy czasowej i używanego systemu kalendarza.
Jon Skeet,
7
„W aplikacjach dla przedsiębiorstw nie zawsze mamy opcję dodawania / używania innych bibliotek”. Naprawdę Sugerujesz, że w ogóle nie możesz używać bibliotek stron trzecich?
Brian Agnew
3
@BrianAgnew komentarz noogrub wydaje się być związany z
pozatechnicznymi
3
Z dzisiejszej perspektywy: „ Joda-Time jest de facto standardową biblioteką daty i godziny dla Java przed Java SE 8. Użytkownicy proszeni są teraz o migrację do java.time (JSR-310)”.
Drux
66

Oto, co otrzymałem dzisiejszą datę z czasem ustawionym na 00:00:00:

DateFormat formatter = new SimpleDateFormat("dd/MM/yyyy");

Date today = new Date();

Date todayWithZeroTime = formatter.parse(formatter.format(today));
Shobbi
źródło
28
W jaki sposób różni się to od już zaproponowanej „metody 1” w pierwotnym pytaniu?
Chris
Nie dotyczy strefy czasowej, w której ma to miejsce (pozostawiono domyślnej strefie czasowej JVM).
Darrell Teague,
58

Możesz użyć DateUtils.truncate z biblioteki Apache Commons.

Przykład:

DateUtils.truncate(new Date(), java.util.Calendar.DAY_OF_MONTH)
JoGo
źródło
3
Jeśli chcesz dodać nową bibliotekę, niech będzie to Joda zamiast Apache Commons.
Dibbeke,
6
+1 @Dibbeke Różnica polega nie tylko na dodaniu jednej biblioteki zamiast innej. Czasami nie chce się uczyć całej nowej biblioteki tylko dla banalnego zadania skracania czasu od daty. Lub nie chce mieć kodu, który łączy Data / Kalendarz z datami Jody, używając czasami pierwszej, a czasem drugiej. Lub nie stać mnie na usprawiedliwienie zastąpienia całego dobrze działającego kodu opartego na dacie / kalendarzu inną biblioteką w tak trywialnym celu. Sugerując, że Joda jest z pewnością dobra, ponieważ jest wyraźnie lepsza i po prostu Właściwa Rzecz, czasem zaczyna się prawdziwe życie.
SantiBailors
Geniusz! Pomógł mi bardzo
Thiesen
37

Czy jest jakiś inny sposób niż te dwa?

Tak jest.

LocalDate.now( 
    ZoneId.of( "Pacific/Auckland" ) 
)

java.time

Java 8 i nowsze wersje są dostarczane z nowym wbudowanym pakietem java.time . Zobacz samouczek . Duża część funkcji java.time została przeniesiona z powrotem do Java 6 i 7 w ThreeTen-Backport, a następnie dostosowana do Androida w ThreeTenABP .

Podobnie jak Joda-Time , java.time oferuje LocalDateklasę reprezentującą wartość tylko daty bez pory dnia i strefy czasowej.

Pamiętaj, że strefa czasowa ma kluczowe znaczenie dla ustalenia konkretnej daty. Na przykład o północy w Paryżu data w Montrealu wciąż jest „wczoraj”.

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

Domyślnie java.time używa standardu ISO 8601 do generowania ciągu reprezentującego wartość daty lub wartości daty i godziny. (Kolejne podobieństwo do Joda-Time.) Więc po prostu wywołaj, toString()aby wygenerować tekst podobny 2015-05-21.

String output = today.toString() ; 

O java.time

Środowisko java.time jest wbudowane w Javę 8 i nowsze wersje . Klasy te kłopotliwe zastąpić stary starszych klas Date-Time, takich jak java.util.Date, Calendar, i SimpleDateFormat.

Projekt Joda-Time , teraz w trybie konserwacji , zaleca migrację do klas 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 .

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
1
Dziękujemy za podanie strefy czasowej . Nie przyszło mi do głowy, że „jaki jest dzień” nie jest absolutną koncepcją.
ToolmakerSteve
@ToolmakerSteve Uzyskiwanie daty zawsze wiąże się ze strefą czasową. Ale sztuczka polega na tym, że jeśli nie określisz strefy czasowej, bieżąca domyślna strefa czasowa maszyny JVM zostanie automatycznie zastosowana do określenia daty. Ponadto bieżąca domyślna strefa czasowa maszyny JVM może ulec zmianie w dowolnym momencie ! Każdy kod w dowolnym wątku dowolnej aplikacji w JVM może wywoływać w TimeZone.setDefaultczasie wykonywania i natychmiast wpływać na cały inny kod działający w tej JVM. Morał historii: Zawsze określaj strefę czasową.
Basil Bourque,
22

Najprostszy sposób:

long millisInDay = 60 * 60 * 24 * 1000;
long currentTime = new Date().getTime();
long dateOnly = (currentTime / millisInDay) * millisInDay;
Date clearDate = new Date(dateOnly);
rzymski
źródło
5
Dla mnie jest to obecnie: „Sobota 19 lutego 01 : 00: 00 CET 2011”. Jeśli chcesz tego użyć, to tylko z UTC.
Chris Lercher
4
Czy wiersz 3 nie jest long dateOnly = (currentTime / millisInDay) * millisInDay;tym samym co pisanie long dateOnly = currentTime;?
Chris
5
Nie jest tak z powodu matematyki liczb całkowitych. Pomyśl o tym bardziej jak Math.floor (currentTime / millisInDay) * millisInDay. W ten sposób skutecznie ustawia czas na 00:00:00.
Nicholas Flynt
2
To rozwiązanie jest prawdopodobnie odpowiednie dla większości aplikacji, ale należy pamiętać, że założenie, że dzień ma 60 * 60 * 24 * 1000, nie zawsze jest prawdziwe. Możesz na przykład mieć kilka sekund przestępnych.
Pierre-Antoine,
1
Jest to prawdopodobnie najbardziej tępy i niezalecany sposób w stosunku do innych 1 i 2-liniowych jasnych i zwięzłych metod.
Darrell Teague,
22

Standardową odpowiedzią na te pytania jest użycie Joda Time . Interfejs API jest lepszy, a jeśli używasz formatatorów i parserów, możesz uniknąć nieintuicyjnego braku bezpieczeństwa wątków SimpleDateFormat.

Korzystanie z Jody oznacza, że ​​możesz po prostu:

LocalDate d = new LocalDate();

Aktualizacja :: Korzystanie z java 8 można to osiągnąć za pomocą

LocalDate date = LocalDate.now();
Brian Agnew
źródło
Nowy pakiet java.time w Javie 8 oferuje również LocalDateklasę podobną do Joda-Time.
Basil Bourque,
1
Idealnie byłoby przekazać DateTimeZonedo tego konstruktora LocalDate. Określenie bieżącej daty zależy od strefy czasowej, ponieważ Paryż rozpoczyna nową datę wcześniej niż Montréal. Pominięcie strefy czasowej powoduje zastosowanie domyślnej strefy czasowej maszyny JVM. Zwykle lepiej jest określać niż polegać na domyślnych.
Basil Bourque,
8

Jest to prosty sposób:

Calendar cal = Calendar.getInstance();
SimpleDateFormat dateOnly = new SimpleDateFormat("MM/dd/yyyy");
System.out.println(dateOnly.format(cal.getTime()));
Dolina górska
źródło
4
Ta odpowiedź nie zawiera odpowiedzi na pytanie. Uzyskanie sformatowanego ciągu nie było celem.
Basil Bourque
7

Nie ma sensu mówić o dacie bez znacznika czasu w odniesieniu do procedur Date w standardowym środowisku wykonawczym Java, ponieważ zasadniczo odwzorowuje ona konkretną milisekundę, a nie datę. Wspomniana milisekunda jest z natury związana z porą dnia, co czyni ją podatną na problemy strefy czasowej, takie jak czas letni i inne zmiany kalendarza. Zobacz, dlaczego odejmowanie tych dwóch razy (w 1927 r.) Daje dziwny wynik? na ciekawy przykład.

Jeśli chcesz pracować z datami zamiast milisekund, musisz użyć czegoś innego. W Javie 8 istnieje nowy zestaw metod zapewniających dokładnie to, o co prosisz. W przypadku języka Java 7 i wcześniejszych należy użyć http://www.joda.org/joda-time/

Thorbjørn Ravn Andersen
źródło
3
Uwaga: takie podejście, mówiąc „data o północy”, nie radzi sobie dobrze z czasem letnim i wieloma strefami czasowymi.
Thorbjørn Ravn Andersen
Potwierdzam to, zwiększałem daty o jeden dzień w mojej aplikacji i dowiedziałem się o tym z raportem o błędach od użytkowników w strefie czasowej z DST (mój kraj nie używa DST). W dniu rozpoczęcia DST moja aplikacja dodaje 11 godzin zamiast 12. Być może użycie południa jako godziny dla daty „brak czasu” jest lepsze?
Jose_GD
1
@Jose_GD To w najlepszym razie hack. Youtube.com/watch?v=-5wpm-gesOY może Cię zainteresować .
Thorbjørn Ravn Andersen
@ Thorbjorn, masz rację, po prostu chciałem uniknąć JodaTime, ponieważ dodanie biblioteki dla jednej funkcji przesady dźwięku jest dla mnie. Wiedziałem o tym filmie, naprawdę zabawne.
Jose_GD
1
@Jose_GD Chodziło o to, że istnieje rzeczywisty przypadek użycia, w którym twoje podejście zostanie uznane za błąd. Jeśli znajdziesz, może być więcej ... Nie wiem, jak Joda sobie z tym radzi, ale możesz rzucić okiem. Zauważ też, że Java 8 wprowadza nową bibliotekę daty i godziny (opartą na doświadczeniach z Jodą), którą możesz chcieć zajrzeć.
Thorbjørn Ravn Andersen
4

Zdecydowanie nie jest to najbardziej poprawny sposób, ale jeśli potrzebujesz szybkiego rozwiązania, aby uzyskać datę bez czasu i nie chcesz korzystać z biblioteki innej firmy, powinno to zrobić

    Date db = db.substring(0, 10) + db.substring(23,28);

Potrzebowałem tylko daty do celów wizualnych i nie mogłem Joda, więc podłożyłem.

Bamz
źródło
4
// 09/28/2015
System.out.println(new SimpleDateFormat("MM/dd/yyyy").format(Calendar.getInstance().getTime()));

// Mon Sep 28
System.out.println( new Date().toString().substring(0, 10) );

// 2015-09-28
System.out.println(new java.sql.Date(System.currentTimeMillis()));

// 2015-09-28
// java 8
System.out.println( LocalDate.now(ZoneId.of("Europe/Paris")) ); // rest zones id in ZoneId class
blueberry0xff
źródło
3

O ile wiem, nie ma łatwiejszego sposobu na osiągnięcie tego, jeśli używasz tylko standardowego JDK.

Możesz oczywiście umieścić tę logikę w metodzie 2 w funkcji statycznej w klasie pomocniczej, tak jak zrobiono to tutaj w metodzie toBeginningOfTheDay

Następnie możesz skrócić drugą metodę do:

Calendar cal = Calendar.getInstance();
Calendars.toBeginningOfTheDay(cal);
dateWithoutTime = cal.getTime();

Lub, jeśli naprawdę tak często potrzebujesz bieżącego dnia w tym formacie, możesz po prostu zawinąć go w inną statyczną metodę pomocniczą, czyniąc go w ten sposób liniowym.

Enduriel
źródło
3

Jeśli chcesz tylko zobaczyć datę „RRRR-MM-DD” bez innych bałaganu, np. „Cz 21 maja 12:08:18 EDT 2015”, po prostu użyj java.sql.Date. Ten przykład pobiera bieżącą datę:

new java.sql.Date(System.currentTimeMillis());

Również java.sql.Datejest podklasą java.util.Date.

BigMac66
źródło
3

Jeśli potrzebujesz tylko bieżącej daty, bez czasu, inną opcją jest:

DateTime.now().withTimeAtStartOfDay()
użytkownik3037726
źródło
1
Pytanie nie dotyczyło pory dnia. Twój wynik to obiekt daty i godziny, który rzeczywiście ma porę dnia, która 00:00:00zwykle jest czasem (może się różnić w zależności od strefy czasowej dla anomalii, takich jak czas letni ). Tak więc, jak zauważają inne Odpowiedzi, LocalDateklasa jest bardziej odpowiednia, od Joda-Time (zakładając, że odwołujesz się do Joda-Time - na co powinieneś wyraźnie zwrócić uwagę przy cytowaniu klas niepowiązanych z Javą).
Basil Bourque,
3

Użyj LocalDate.now()i przekonwertuj na Dateponiższe:

Date.from(LocalDate.now().atStartOfDay(ZoneId.systemDefault()).toInstant());
Sahil Chhabra
źródło
1
Bieżąca domyślna strefa czasowa JVM może się zmienić w dowolnym momencie podczas działania. Może wywoływać dowolny kod w dowolnym wątku dowolnej aplikacji w JVM TimeZone.setDefault. Twój kod wywołuje się ZoneId.systemDefaultdwa razy, pierwszy jest domyślnie ukryty, LocalDate.now()a drugi jawnie za pomocą atStartOfDay. Pomiędzy tymi dwoma momentami w czasie wykonywania bieżąca domyślna strefa może się zmienić. Sugeruję uchwycenie strefy w zmiennej i przejście do obu metod.
Basil Bourque
2

Jeśli potrzebujesz części daty tylko w celu uzyskania echa, to

Date d = new Date(); 
String dateWithoutTime = d.toString().substring(0, 10);
manish_s
źródło
1
Nie sądzę, że jest to przyjazne i18n
Mark Lapasa
2

A co z tym?

public static Date formatStrictDate(int year, int month, int dayOfMonth) {
    Calendar calendar = Calendar.getInstance();
    calendar.set(year, month, dayOfMonth, 0, 0, 0);
    calendar.set(Calendar.MILLISECOND, 0);
    return calendar.getTime();
}
vitiello.antonio
źródło
1

Sprawdź czas Veyder . Jest to prosta i wydajna alternatywa zarówno dla java.util, jak i Joda-time. Ma intuicyjny interfejs API i klasy, które reprezentują same daty, bez znaczników czasu.

samotny
źródło
1

Najprostszy sposób, który w pełni wykorzystuje ogromną bazę danych TimeZone Java i jest poprawny:

long currentTime = new Date().getTime();
long dateOnly = currentTime + TimeZone.getDefault().getOffset(currentTime);
TomWolk
źródło
1

Oto czyste rozwiązanie bez konwersji na ciąg znaków i wstecz, a także nie oblicza czasu kilka razy, gdy resetujesz każdy składnik czasu do zera. Wykorzystuje również% (moduł) zamiast dzielenia, a następnie mnożenia, aby uniknąć podwójnej operacji.

Nie wymaga żadnych zależności od stron trzecich i SZANUJE PRZESTRZEŃ CZASOWĄ przekazanego obiektu kalendarza. Ta funkcja zwraca moment o godzinie 12 w strefie czasowej daty (kalendarza), którą podajesz.

public static Calendar date_only(Calendar datetime) {
    final long LENGTH_OF_DAY = 24*60*60*1000;
    long millis = datetime.getTimeInMillis();
    long offset = datetime.getTimeZone().getOffset(millis);
    millis = millis - ((millis + offset) % LENGTH_OF_DAY);
    datetime.setTimeInMillis(millis);
    return datetime;
}
Brent Larsen
źródło
Nie jestem bardzo przekonany, czy będzie szanował letni czas letni (DST)?
Ole VV,
0

Wolisz nie korzystać z bibliotek stron trzecich w jak największym stopniu. Wiem, że o tym wcześniej wspominano, ale tutaj jest ładny, czysty sposób:

  /*
    Return values:
    -1:    Date1 < Date2
     0:    Date1 == Date2
     1:    Date1 > Date2

    -2:    Error
*/
public int compareDates(Date date1, Date date2)
{
    SimpleDateFormat sdf = new SimpleDateFormat("ddMMyyyy");

    try
    {
        date1 = sdf.parse(sdf.format(date1));
        date2 = sdf.parse(sdf.format(date2));
    }
    catch (ParseException e) {
        e.printStackTrace();
        return -2;
    }

    Calendar cal1 = new GregorianCalendar();
    Calendar cal2 = new GregorianCalendar();

    cal1.setTime(date1);
    cal2.setTime(date2);

    if(cal1.equals(cal2))
    {
        return 0;
    }
    else if(cal1.after(cal2))
    {
        return 1;
    }
    else if(cal1.before(cal2))
    {
        return -1;
    }

    return -2;
}

Cóż, nie używanie GregorianCalendar może być opcją!

hehe
źródło
0

Właśnie zrobiłem to dla mojej aplikacji:

public static Date getDatePart(Date dateTime) {
    TimeZone tz = TimeZone.getDefault();
    long rawOffset=tz.getRawOffset();
    long dst=(tz.inDaylightTime(dateTime)?tz.getDSTSavings():0);
    long dt=dateTime.getTime()+rawOffset+dst; // add offseet and dst to dateTime
    long modDt=dt % (60*60*24*1000) ;

    return new Date( dt
                    - modDt // substract the rest of the division by a day in milliseconds
                    - rawOffset // substract the time offset (Paris = GMT +1h for example)
                    - dst // If dayLight, substract hours (Paris = +1h in dayLight)
    );
}

Android API poziom 1, brak biblioteki zewnętrznej. Przestrzega światła dziennego i domyślnej strefy czasowej. Bez manipulacji ciągiem, więc myślę, że ten sposób jest bardziej wydajny niż procesor, ale nie wykonałem żadnych testów.

Elloco
źródło
0

Możesz użyć czasu Joda.

private Date dateWitoutTime(Date date){
 return new LocalDate(date).toDate()
}

i dzwonisz z:

Date date = new Date();
System.out.println("Without Time = " + dateWitoutTime(date) + "/n  With time = " + date);
Jesús Sánchez
źródło