Informazioni sull'orario di interruzione della data di Java


121

Ho un oggetto Java Date contenente informazioni su data e ora. Voglio scrivere un metodo che tagli le informazioni sull'ora, tronca le ore-minuti-secondi, quindi mi rimane solo la data.

Input di esempio:

2008-01-01 13:15:00

Uscita prevista:

2008-01-01 00:00:00

Hai un suggerimento? Ho provato a fare qualcosa del genere:

(timestamp / (24 * 60 * 60 * 1000)) * (24 * 60 * 60 * 1000)

ma ho avuto problemi con il fuso orario.

Risposte:


178

Il modo consigliato per manipolare data / ora è usare un Calendaroggetto:

Calendar cal = Calendar.getInstance(); // locale-specific
cal.setTime(dateObject);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
long time = cal.getTimeInMillis();

2
@cletus, è possibile rimuovere l'ora dall'oggetto Date. Voglio un appuntamento puro come il 01-01-2008 senza porzione di tempo.
Prem

2
Non direi che java.util.Calendar è raccomandato. Sun / Oracle ha soppiantato quella classe in Java 8 con il nuovo pacchetto java.time. Usa quello o Joda-Time.
Basil Bourque

3
Il calendario non è più consigliato. Vedi stackoverflow.com/a/28868089/40064 se stai usando Java 8.
Wim Deblauwe

2
Cordiali saluti, le vecchie classi data-ora fastidiose come java.util.Calendarora sono legacy , soppiantate dalle classi java.time . Vedi Tutorial di Oracle .
Basil Bourque

148

Hai esaminato il metodo di troncamento DateUtils in Apache Commons Lang?

Date truncatedDate = DateUtils.truncate(new Date(), Calendar.DATE);

rimuoverà l'elemento tempo.


9
Link aggiornato di nuovo (dai Apache!
Smetti di spostare i

6
A parte questo approccio, il fuso orario in cui viene eseguita l'operazione di troncamento è sempre il fuso orario del PC locale. Quindi, se lavori con il calendario e le date nel fuso orario UTC o in qualsiasi fuso orario diverso dal fuso orario locale, potrebbe troncare la data non come previsto.
Cloud

come ha sottolineato @Cloud, il fatto che non vedo l'istanza di Calendar qui (non ho alcun controllo su di essa) mi rende un po 'nervoso :)
Jaroslav Záruba

Questo metodo è sorprendentemente lungo e ha avuto almeno due correzioni di bug.
Pino

42

Hai guardato Joda ? È un modo molto più semplice e intuitivo per lavorare con date e orari. Ad esempio puoi convertire banalmente tra (diciamo) oggetti LocalDateTime e LocalDate .

ad esempio (per illustrare l'API)

LocalDate date = new LocalDateTime(milliseconds).toLocalDate()

Inoltre risolve alcuni problemi di sicurezza dei thread con i formattatori di data / ora ed è fortemente raccomandato per lavorare con qualsiasi problema di data / ora in Java.


12
Credo di sì. Si consiglia di utilizzare una libreria migliore piuttosto che combattere con le classi java.util non funzionanti (come evidenziato dalla domanda)
Brian Agnew

10
Ovviamente si riferisce, Joda è LA libreria per la manipolazione di data, ora e calendario in Java. Sarebbe un abbandono del dovere non raccomandarlo.
Joel

1
La questione della sicurezza dei thread è molto interessante, ho trovato questo post che ricerca un errore Oracle / Hibernate "IllegalArgumentException, sun.util.calendar.ZoneInfo.getOffset (ZoneInfo), oracle.jdbc.driver.DateCommonBinder.zoneOffset (OraclePreparedStatement)". Questo parla di un altro problema Oracle che appare solo con un carico pesante forums.oracle.com/forums/thread.jspa?threadID=325576 È davvero difficile persino creare un oggetto con millisecondi non validi nel codice normale. Per me, penso che abbatterò il mio thread usando Joda, piuttosto che lasciare che Oracle usi la roba forse sospetta non joda.
Mark Bennett

4
Vorrei poter votare in negativo tutti coloro che hanno votato in alto. Chi sono tutte queste persone che fanno +1 su una risposta che non tenta nemmeno di rispondere alla domanda? Una risposta usando Joda sarebbe più che gradita, ma non è questo ...
Ryan

1
@ Ryan In che modo questa risposta non è pertinente? La domanda chiedeva la data senza l'ora del giorno. Questa risposta presenta una classe il cui unico scopo è rappresentare una data senza un'ora del giorno.
Basil Bourque

35

Solo un rapido aggiornamento alla luce delle classi java.time ora integrate in Java 8 e versioni successive.

LocalDateTimeha un truncatedTometodo che affronta efficacemente ciò di cui stai parlando qui:

LocalDateTime.now().truncatedTo(ChronoUnit.MINUTES)

Questo esprimerà l'ora corrente solo in minuti:

2015-03-05T11:47

È possibile utilizzare un ChronoUnit(o un TemporalUnit) inferiore a DAYS per eseguire il troncamento (poiché il troncamento viene applicato solo alla parte dell'ora di LocalDateTime, non alla parte della data).


3
Puoi usare solo ChronoUnits fino a DAYS - le unità più alte genereranno un'eccezione.
assylias

26
Date date = new Date();
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
date = cal.getTime();

22

Con Joda puoi facilmente ottenere la data prevista.

A partire dalla versione 2.7 (forse da qualche versione precedente maggiore di 2.2), come osserva un commentatore, toDateMidnightè stata deprecata a favore o con un nome appropriato withTimeAtStartOfDay(), rendendo conveniente

DateTime.now().withTimeAtStartOfDay()

possibile.

Vantaggio aggiunto di un'API più bella.

Con le versioni precedenti, puoi farlo

new DateTime(new Date()).toDateMidnight().toDate()

6
I metodi e le classi relativi alla mezzanotte in Joda-Time sono ora deprecati. Gli sviluppatori consigliano di non utilizzarli. Usa invece il metodo withTimeAtStartOfDay.
Basil Bourque

Grazie per avermelo fatto notare. Aggiornata la risposta per tenerne conto.
h7r

Puoi anche usarlo toLocalDateper ottenere un oggetto che non ha nemmeno una componente temporale.
Charles Wood

Cordiali saluti, il progetto Joda-Time è ora in modalità di manutenzione , con il team che consiglia la migrazione alle classi java.time . Vedi Tutorial di Oracle .
Basil Bourque

8

Ho fatto il troncamento con la nuova API java8. Ho affrontato una cosa strana ma in generale è troncata ...

Instant instant = date.toInstant();
instant = instant.truncatedTo(ChronoUnit.DAYS);
date = Date.from(instant);

parole chiave: java.time, Java 8, LocalDateTime.truncatedTo ()
Alexander Taylor

1
Questa risposta produce solo date per il contesto del fuso orario UTC.
Basil Bourque

6

Usa DateUtils da Apache, con truncate, in questo modo:

DateUtils.truncate(Calendar.getInstance().getTime(), Calendar.DATE);

Questo verrà troncato nel fuso orario locale, non nell'UTC rappresentato dalla data (getTime).
epox

6

Per timestamp:

timestamp -= timestamp % (24 * 60 * 60 * 1000)

3
C'è un piccolo errore in questo codice - la parentesi sinistra non è posizionata correttamente, deve essere timestamp - = timestamp% (24 * 60 * 60 * 1000). Questo taglierà la parte del tempo. E sono sicuro che in questo modo è più sufficiente che creare un oggetto Caledar e giocare con i suoi metodi o persino utilizzare qualsiasi altra libreria di terze parti
Stan

2
No, questa NON è la soluzione corretta, ATTENZIONE. È lo stesso dell'arrotondamento del valore della data al valore floor, ovvero potresti ottenere il 28 marzo per il timestamp del 29 marzo (nella tua locale). Quindi le soluzioni basate su Calendar (DateUtils di Apache usa anche Calendar) sono l'unico modo
Drew

Anche questo non funzionerà quando l'ora cambia a causa dell'ora legale.
Alexandru Severin

5

Da java.util.Date JavaDocs:

La classe Date rappresenta un istante di tempo specifico, con precisione al millisecondo

e dal java.sql.Date JavaDocs:

Per conformarsi alla definizione di SQL DATE, i valori dei millisecondi racchiusi da un'istanza java.sql.Date devono essere 'normalizzati' impostando le ore, i minuti, i secondi e i millisecondi su zero nel particolare fuso orario a cui è associata l'istanza .

Quindi, l'approccio migliore è usare java.sql.Date se non hai bisogno della parte di tempo

java.util.Date utilDate = new java.util.Date();
java.sql.Date sqlDate = new java.sql.Date(System.currentTimeMillis());

e l'output è:

java.util.Date : Thu Apr 26 16:22:53 PST 2012
java.sql.Date  : 2012-04-26

4

tl; dr

LocalDateTime.parse(                            // Lacking an offset or time zone, parse as a `LocalDateTime`. *Not* a specific moment in time.
    "2008-01-01 13:15:00".replace( " " , "T" )  // Alter input string to comply with ISO 8601 standard format.
)
.toLocalDate()                                  // Extract a date-only value.
.atStartOfDay(                                  // Do not assume the day starts at 00:00:00. Let class determine start-of-day.
    ZoneId.of( "Europe/Paris" )                 // Determining a specific start-of-day requires a time zone.
)                                               // Result is a `ZonedDateTime` object. At this point we have a specific moment in time, a point on the timeline.
.toString()                                     // Generate a String in standard ISO 8601 format, wisely extended to append the name of the time zone in square brackets.

2008-01-01T00: 00 + 01: 00 [Europe / Paris]

Per generare una stringa nel formato desiderato, passare un file DateTimeFormatter.

LocalDateTime.parse(                            // Lacking an offset or time zone, parse as a `LocalDateTime`. *Not* a specific moment in time.
    "2008-01-01 13:15:00".replace( " " , "T" )  // Alter input string to comply with ISO 8601 standard format.
)
.toLocalDate()                                  // Extract a date-only value.
.atStartOfDay(                                  // Do not assume the day starts at 00:00:00. Let class determine start-of-day.
    ZoneId.of( "Europe/Paris" )                 // Determining a specific start-of-day requires a time zone.
)                                               // Result is a `ZonedDateTime` object. At this point we have a specific moment in time, a point on the timeline.
.format(                                        // Generate a String representing the object’s value.
    DateTimeFormatter.ISO_LOCAL_DATE_TIME       // Built-in predefined formatter close to what you want. 
)
.replace( "T" , " " )                           // Replace the standard’s use of a 'T' in the middle with your desired SPACE character.

2008-01-01 00:00:00

Dettagli

Altre risposte sono corrette, ma usano vecchie classi data-ora ora superate dal framework java.time.

java.time

Il framework java.time è integrato in Java 8 e versioni successive. Gran parte della funzionalità java.time è stata trasferita su Java 6 e 7 ( ThreeTen-Backport ) e ulteriormente adattata ad Android ( ThreeTenABP ).

Per prima cosa modifica la stringa di input in modo che sia conforme alla versione canonica del formato ISO 8601. I formati ISO 8601 standard vengono utilizzati per impostazione predefinita nelle classi java.time per analizzare / generare stringhe che rappresentano valori di data e ora. Dobbiamo sostituire quello SPAZIO nel mezzo con un file T.

String input = "2008-01-01 13:15:00".replace( " " , "T" );  // → 2008-01-01T13:15:00

Ora possiamo analizzarlo come un LocalDateTime, dove "Locale" significa nessuna località specifica. L'input è privo di informazioni sulla differenza di fuso orario o UTC .

LocalDateTime ldt = LocalDateTime.parse( input );

ldt.toString ()… 2008-01-01T13: 15: 00

Se non ti interessa l'ora del giorno né il fuso orario, converti in un file LocalDate.

LocalDate ld = ldt.toLocalDate();

ld.toString ()… 2008-01-01

Primo momento della giornata

Se invece vuoi che l'ora del giorno sia impostata sul primo momento della giornata, usa una ZonedDateTimeclasse, quindi converti in un LocalDateoggetto per chiamare il suo atStartOfDaymetodo. Tieni presente che il primo momento potrebbe non essere l'ora a 00:00:00causa dell'ora legale o forse di altre anomalie.

Il fuso orario è fondamentale perché per un dato momento la data varia nel mondo in base alla zona. Ad esempio, pochi istanti dopo la mezzanotte a Parigi è un nuovo giorno per i parigini, ma è ancora "ieri" a Montréal per i canadesi.

ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ldt.atZone( zoneId );
LocalDate ldFromZdt = zdt.toLocalDate();
ZonedDateTime zdtStartOfDay = ldFromZdt.atStartOfDay( zoneId );

zdtStartOfDay.toString ()… 2008-01-01T00: 00: 00-05: 00 [America / Montreal]

UTC

Per vedere quel momento attraverso la lente del fuso orario UTC , estrai un Instantoggetto. Sia l' ZonedDateTimee Instantrappresenterà nello stesso momento sulla timeline, ma appaiono come due diversi tempi di orologio a muro .

An Instantè la classe building block di base in java.time, sempre in UTC per definizione. Utilizza questa classe frequentemente, poiché in genere dovresti eseguire la logica aziendale, l'archiviazione dei dati e lo scambio di dati in UTC.

Instant instant = zdtStartOfDay.toInstant();

instant.toString ()… 2008-01-01T05: 00: 00Z

Vediamo le 5 del mattino anziché lo scoccare della mezzanotte. Nel formato standard, alla Zfine è l'abbreviazione Zulue significa "UTC".


Informazioni su java.time

Il framework java.time è integrato in Java 8 e versioni successive. Queste classi soppiantare la vecchia fastidiosi legacy classi data-time come java.util.Date, Calendar, e SimpleDateFormat.

Il progetto Joda-Time , ora in modalità di manutenzione , consiglia la migrazione alle classi java.time .

Per saperne di più, vedere il tutorial Oracle . E cerca Stack Overflow per molti esempi e spiegazioni. La specifica è JSR 310 .

Puoi scambiare oggetti java.time direttamente con il tuo database. Utilizza un driver JDBC compatibile con JDBC 4.2 o successivo. Non c'è bisogno di stringhe, non c'è bisogno di java.sql.*classi.

Dove ottenere le classi java.time?

  • Java SE 8 , Java SE 9 e versioni successive
    • Built-in.
    • Parte dell'API Java standard con un'implementazione in bundle.
    • Java 9 aggiunge alcune funzionalità e correzioni minori.
  • Java SE 6 e Java SE 7
    • Gran parte delle funzionalità java.time sono portate indietro su Java 6 e 7 in ThreeTen-Backport .
  • androide
    • Versioni successive delle implementazioni di bundle Android delle classi java.time.
    • Per precedenza Android (<26), il ThreeTenABP progetto si adatta ThreeTen-Backport (di cui sopra). Vedi Come usare ThreeTenABP… .

Il progetto ThreeTen-Extra estende java.time con classi aggiuntive. Questo progetto è un banco di prova per possibili future aggiunte a java.time. Si possono trovare alcune classi utili, come per esempio Interval, YearWeek, YearQuartere altro .



2

La domanda è contraddittoria. Chiede una data senza ora del giorno ma visualizza un esempio con l'ora di 00:00:00.

Joda-Time

AGGIORNAMENTO: Il progetto Joda-Time è ora in modalità di manutenzione , con il team che consiglia la migrazione alle classi java.time . Vedi la mia altra risposta per la soluzione java.time.

Se invece vuoi che l'ora del giorno sia impostata sul primo momento della giornata, usa un DateTimeoggetto nella libreria Joda-Time e chiama il suo withTimeAtStartOfDaymetodo. Tieni presente che il primo momento potrebbe non essere l'ora a 00:00:00causa dell'ora legale o forse di altre anomalie.


Questa è la risposta definitiva (supponendo che si possa e si voglia usare Joda Time)
Clint Eastwood

2

Solo clear()campi ridondanti.

Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.clear(Calendar.MINUTE);
calendar.clear(Calendar.SECOND);
calendar.clear(Calendar.MILLISECOND);
Date truncatedDate = calendar.getTime();

Da Java 8 un'opzione migliore è utilizzare il truncatedTometodo LocalDateTime, ad esempio:

LocalDateTime.now().truncatedTo(ChronoUnit.DAYS)

Questo non funziona, perché calendar.clear(Calendar.HOUR_OF_DAY);non cancella le ore. La soluzione di @cletus funziona bene.
Arcao

Scusa, hai ragione, l'ho corretto, per ore dovresti impostare il valore. In generale è quasi allo stesso modo di come troncare la data ...
m190

1

Mi ha davvero infastidito che il nuovo costruttore di calendari "migliorato" non prenda un int per millisecondi come il vecchio "orribile" Date. Poi mi sono davvero arrabbiato e ho scritto questo:

long stuffYou = startTime.getTimeInMillis() % 1000;
startTime.setTimeInMillis(startTime.getTimeInMillis() - stuffYou);

Non ho usato la parola "roba" in quel momento, ma poi ho scoperto la felicità di questo:

startTime.set(Calendar.MILLISECOND, 0);

Ma sono ancora abbastanza arrabbiato per questo.


1

Ho risolto il problema in questo modo (nell'ottava zona orientale (ora di Pechino)):

private Date getTruncatedDate(Date d) {
    if (d == null) {
        return null;
    }
    long h = 60 * 60 * 1000L;
    long dateStamp = d.getTime() - (d.getTime() + 8 * h) % (24 * h);
    return new Date(dateStamp);
}

Prima di tutto, dovresti essere chiaro cosa è il timestamp. Il timestamp è il totale dei millisecondi dall'01 gennaio alle 00:00:00 1970 GMT (come UTC) o dal giovedì 01 gennaio alle 08:00:00 CST 1970 ad oggi.

Ricorda: il timestamp è indipendente dal fuso orario.

Quindi ottieni lo stesso risultato con la seguente dichiarazione in diversi fusi orari:

System.out.println(new Date().getTime());

E

System.out.println(new Date(0));

stampa diverse informazioni sull'ora in diversi fusi orari: se imposti il ​​fuso orario del tuo PC come UTC, ottieni

Thu Jan 01 00:00:00 UTC 1970

Ma se imposti il ​​fuso orario come (UTC +8: 00) Pechino, Chongqing, Hong Kong, Urumq, ottieni:

Thu Jan 01 08:00:00 CST 1970

Java ottiene il timestamp, quindi visualizza le informazioni su data e ora in base al fuso orario.

Per l'introduzione di come Java visualizza le informazioni di data e ora in diversi fusi orari, è facile come trancare le informazioni sull'ora. Dovresti ottenere il timestamp e tenere in considerazione il fuso orario quando vengono tagliate le informazioni sull'ora. Quindi puoi creare un nuovo oggetto Date con il timestamp (possiamo chiamarlo data stamp), java compenserà il fuso orario quando visualizza le informazioni sulla data.

Come nella zona otto orientale (ora di Pechino), l'ora di Pechino è anteriore di 8 ore rispetto al GMT, quindi dovresti sottrarre più ore 8 quando esegui l'operazione modulo. Vale a dire, dovresti prima ottenere l'ora GMT, quindi Java aggiungerà 8 ore quando l'ora di visualizzazione in base all'impostazione del fuso orario del tuo PC.


Il problema del fuso orario è oscuro e mi lascia perplesso per molto tempo. Ora lo metto in chiaro. La speranza aiuta.


2018-01-04 Il metodo seguente funziona anche.

private Date getTruncatedDate2(Date d) {
    Calendar cal = Calendar.getInstance(); // locale-specific
    cal.setTime(d);
    cal.set(Calendar.HOUR_OF_DAY, 0);
    cal.set(Calendar.MINUTE, 0);
    cal.set(Calendar.SECOND, 0);
    cal.set(Calendar.MILLISECOND, 0);


return cal.getTime();

}


Cordiali saluti, questa risposta utilizza fastidiose vecchie classi data-ora soppiantate anni fa dalle classi java.time .
Basil Bourque

1

Puoi farlo per evitare problemi di fuso orario:

public static Date truncDate(Date date) {
    Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
    cal.setTime(date);
    cal.set(Calendar.HOUR_OF_DAY, 0);
    cal.set(Calendar.MINUTE, 0);
    cal.set(Calendar.SECOND, 0);
    cal.set(Calendar.MILLISECOND, 0);
    return cal.getTime();
}

Sebbene l' Dateoggetto Java sia un valore di timestamp, ma durante il troncamento, verrà convertito nel fuso orario locale, quindi otterrai un valore sorprendente se ti aspetti un valore dal fuso orario UTC.


I problemi Calendare le Dateclassi sono diventati legacy anni fa con le classi java.time che implementavano JSR 310 in Java 8 e versioni successive. Suggerirne l'uso nel 2018 è un consiglio scadente.
Basil Bourque

0

Potrebbe essere una risposta in ritardo, ma ecco un modo per farlo in una riga senza utilizzare alcuna libreria:

new SimpleDateFormat("yyyy-MM-dd").parse(new SimpleDateFormat("yyyy-MM-dd").format(YOUR_TIMESTAMP))

0

Con Joda-Time dalla versione 2.0 puoi usare LocalDate.toDate().

Semplicemente

// someDatetime is whatever java.util.Date you have.
Date someDay = new LocalDate(someDatetime).toDate();

[A] La tua risposta è ridondante. Già pubblicato da Brian Agnew . [B] Il tuo codice non è corretto, poiché ignora il fuso orario, proprio il problema sollevato nella domanda.
Basil Bourque

Potrebbe essere ridondante, ma ho solo messo un esempio più semplice qui perché ho riconosciuto che una risposta non dovrebbe essere nei commenti. Sì, java.util.Date ignora TimeZone ma la domanda originale era con a Java Date objectquindi ho usato java.util.Date. Inoltre non spiega come il suo fuso orario sia problematico con java.util.Date (quando? Dove?) Sebbene non possa fare a meno di usare altro che Date.
lamusique

0

Per tutte le risposte che utilizzano Calendar, dovresti usarlo invece in questo modo

public static Date truncateDate(Date date) {
    Calendar c = Calendar.getInstance();
    c.setTime(date);
    c.set(Calendar.HOUR_OF_DAY, c.getActualMinimum(Calendar.HOUR_OF_DAY));
    c.set(Calendar.MINUTE, c.getActualMinimum(Calendar.MINUTE));
    c.set(Calendar.SECOND, c.getActualMinimum(Calendar.SECOND));
    c.set(Calendar.MILLISECOND, c.getActualMinimum(Calendar.MILLISECOND));
    return c.getTime();
}

Ma io preferisco questo:

public static Date truncateDate(Date date) {
    return new java.sql.Date(date.getTime());
}

La java.sql.Dateclasse finge di rappresentare un valore di sola data, ma in realtà ha un'ora del giorno regolata per il fuso orario UTC. Quindi non è proprio una soluzione adeguata alla Domanda.
Basil Bourque

0

Ecco un modo semplice per ottenere la data senza tempo se stai usando Java 8+: usa il tipo java.time.LocalDate invece di Date.

LocalDate now = LocalDate.now();
 System.out.println(now.toString());

Il risultato:

2019-05-30

https://docs.oracle.com/javase/8/docs/api/java/time/LocalDate.html


1
Usando LocalDateè una buona idea per la maggior parte degli scopi, ma non è chiaro come questo funzionerà con l'ingresso dalla questione, 2008-01-01 13:15:00.
Ole VV
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.