Informations sur l'heure limite Java Date


121

J'ai un objet Java Date contenant des informations de date et d'heure. Je veux écrire une méthode qui coupe les informations de temps, tronque les heures-minutes-secondes, donc je n'ai plus que la date.

Exemple d'entrée:

2008-01-01 13:15:00

Production attendue:

2008-01-01 00:00:00

Avez-vous un conseil? J'ai essayé de faire quelque chose comme ça:

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

mais j'ai rencontré des problèmes avec le fuseau horaire.

Réponses:


178

La manière recommandée de manipuler la date / l'heure est d'utiliser un Calendarobjet:

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, Est-il possible de supprimer l'heure de l'objet Date. Je veux une date pure comme le 01/01/2008 sans temps partiel.
Prem

2
Je ne dirais pas que java.util.Calendar est recommandé. Sun / Oracle a remplacé cette classe dans Java 8 par le nouveau package java.time. Utilisez cela ou Joda-Time.
Basil Bourque

3
Le calendrier n'est plus recommandé. Voir stackoverflow.com/a/28868089/40064 si vous utilisez Java 8.
Wim Deblauwe

2
FYI, les anciennes classes de date-heure gênantes telles que java.util.Calendarmaintenant héritées , supplantées par les classes java.time . Voir le didacticiel d'Oracle .
Basil Bourque

148

Avez-vous examiné la méthode tronquée DateUtils dans Apache Commons Lang?

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

supprimera l'élément de temps.


9
Lien mis à jour à nouveau (allez sur Apache! Arrêtez de déplacer vos documents!)
Ogre Psalm33

6
Moins de cette approche que le fuseau horaire dans quelle opération tronquée est effectuée est toujours le fuseau horaire du PC local. Donc, si vous travaillez avec un calendrier et des dates dans le fuseau horaire UTC ou dans tout autre fuseau horaire que le fuseau horaire local, cela pourrait tronquer la date non comme prévu.
Nuage du

comme @Cloud l'a souligné, le fait que je ne vois pas l'instance de calendrier ici (je n'ai aucun contrôle dessus) me rend un peu nerveux :)
Jaroslav Záruba

Cette méthode est étonnamment longue et a eu au moins deux corrections de bogues.
Pino

42

Avez-vous regardé Joda ? C'est une manière beaucoup plus simple et intuitive de travailler avec les dates et les heures. Par exemple, vous pouvez convertir de manière triviale entre (par exemple) des objets LocalDateTime et LocalDate .

par exemple (pour illustrer l'API)

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

De plus, il résout certains problèmes de sécurité des threads avec les formateurs de date / heure et est fortement recommandé pour travailler avec tous les problèmes de date / heure en Java.


12
Je crois que oui. C'est une recommandation d'utiliser une meilleure bibliothèque plutôt que de se battre avec les classes java.util cassées (comme en témoigne la question)
Brian Agnew

10
Bien sûr, cela se rapporte, Joda est LA bibliothèque de manipulation de date, d'heure et de calendrier en Java. Ce serait un manquement au devoir de ne pas le recommander.
Joel

1
Le problème de la sécurité des threads est très intéressant, j'ai trouvé cet article recherchant une erreur Oracle / Hibernate "IllegalArgumentException, sun.util.calendar.ZoneInfo.getOffset (ZoneInfo), oracle.jdbc.driver.DateCommonBinder.zoneOffset (OraclePreparedStatement)". Cela parle d'un autre problème Oracle apparaissant uniquement avec une charge importante forums.oracle.com/forums/thread.jspa?threadID=325576 Il est vraiment difficile de créer même un objet avec des millisecondes non valides en code normal. Pour moi, je pense que je vais abattre mon fil de discussion en utilisant Joda, plutôt que de laisser Oracle utiliser des trucs non-joda potentiellement suspects.
Mark Bennett

4
J'aurais aimé pouvoir voter contre tous ceux qui ont voté pour cela. Qui sont toutes ces personnes qui attribuent +1 à une réponse qui n'essaye même pas de répondre à la question? Une réponse utilisant Joda serait plus que bienvenue, mais ce n'est pas ça ...
Ryan

1
@Ryan En quoi cette réponse n'est-elle pas pertinente? La question demandait la date sans heure du jour. Cette réponse présente une classe dont le seul but est de représenter une date sans heure du jour.
Basil Bourque

35

Juste une mise à jour rapide à la lumière des classes java.time désormais intégrées à Java 8 et versions ultérieures.

LocalDateTimea une truncatedTométhode qui répond efficacement à ce dont vous parlez ici:

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

Cela exprimera l'heure actuelle en minutes seulement:

2015-03-05T11:47

Vous pouvez utiliser un ChronoUnit(ou un TemporalUnit) plus petit que DAYS pour exécuter la troncature (car la troncature est appliquée uniquement à la partie heure de LocalDateTime, pas à la partie date).


3
Vous ne pouvez utiliser les ChronoUnits que jusqu'à DAYS - les unités supérieures lancent une exception.
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

Avec Joda, vous pouvez facilement obtenir la date prévue.

À partir de la version 2.7 (peut-être depuis une version antérieure supérieure à 2.2), comme le note un commentateur, toDateMidnighta été déconseillée en faveur ou bien nommée withTimeAtStartOfDay(), ce qui rend le

DateTime.now().withTimeAtStartOfDay()

possible.

Avantage ajouté d'une API bien plus agréable.

Avec les anciennes versions, vous pouvez faire

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

6
Les méthodes et classes liées à minuit dans Joda-Time sont désormais obsolètes. Les développeurs recommandent de ne pas les utiliser. Utilisez plutôt la méthode withTimeAtStartOfDay.
Basil Bourque

Merci d'avoir souligné. Mise à jour de la réponse pour en tenir compte.
h7r

Vous pouvez également utiliser toLocalDatepour obtenir un objet qui n'a même pas de composante temporelle.
Charles Wood

Pour info, le projet Joda-Time est maintenant en mode maintenance , l'équipe conseillant la migration vers les classes java.time . Voir le didacticiel d'Oracle .
Basil Bourque

8

J'ai fait la troncature avec la nouvelle API java8. J'ai fait face à une chose étrange mais en général c'est tronqué ...

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

mots-clés: java.time, Java 8, LocalDateTime.truncatedTo ()
Alexander Taylor

1
Cette réponse produit uniquement des dates pour le contexte du fuseau horaire UTC.
Basil Bourque

6

Utilisez DateUtils d'Apache, avec truncate, comme ceci:

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

Cela tronquera dans la zone horaire locale, pas dans UTC que la date (getTime) représente.
epox

6

Pour les horodatages:

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

3
Il y a une petite erreur dans ce code - le crochet gauche n'est pas placé correctement, il doit être horodaté - = horodatage% (24 * 60 * 60 * 1000). Cela coupera la partie du temps. Et je suis sûr que cette méthode est plus suffisante que de créer un objet Caledar et de jouer avec ses méthodes ou même d'utiliser toute autre bibliothèque tierce
Stan

2
Non, ce n'est PAS la bonne solution, ATTENTION. Cela revient à arrondir la valeur de la date au plancher, c'est-à-dire que vous pouvez obtenir le 28 mars pour l'horodatage du 29 mars (dans votre région). Donc, les solutions basées sur le calendrier (DateUtils d'Apache utilise également Calendar) sont le seul moyen
Drew

Cela ne fonctionnera pas non plus lorsque l'heure change en raison de l'heure d'été.
Alexandru Severin

5

À partir de java.util.Date JavaDocs:

La classe Date représente un instant spécifique dans le temps, avec une précision de la milliseconde

et à partir des JavaDocs java.sql.Date:

Pour se conformer à la définition de SQL DATE, les valeurs en millisecondes encapsulées par une instance java.sql.Date doivent être `` normalisées '' en définissant les heures, minutes, secondes et millisecondes sur zéro dans le fuseau horaire particulier auquel l'instance est associée .

Donc, la meilleure approche est d'utiliser java.sql.Date si vous n'avez pas besoin de la partie temps

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

et la sortie est:

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]

Pour générer une chaîne au format souhaité, passez un fichier 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.

01/01/2008 00:00:00

Détails

D'autres réponses sont correctes, mais utilisent d'anciennes classes de date-heure désormais dépassées par le framework java.time.

java.time

Le framework java.time est intégré à Java 8 et versions ultérieures. Une grande partie de la fonctionnalité java.time est rétroportée vers Java 6 et 7 ( ThreeTen-Backport ) et adaptée à Android ( ThreeTenABP ).

Modifiez d'abord la chaîne d'entrée pour qu'elle soit conforme à la version canonique du format ISO 8601. Les formats ISO 8601 standard sont utilisés par défaut dans les classes java.time pour analyser / générer des chaînes qui représentent des valeurs date-heure. Nous devons remplacer cet ESPACE au milieu par un T.

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

Maintenant, nous pouvons l'analyser comme un LocalDateTime, où «Local» signifie aucune localité spécifique. L'entrée ne contient aucune information de décalage par rapport à UTC ou de fuseau horaire.

LocalDateTime ldt = LocalDateTime.parse( input );

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

Si vous ne vous souciez pas de l'heure ni du fuseau horaire, convertissez-le en fichier LocalDate.

LocalDate ld = ldt.toLocalDate();

ld.toString ()… 01/01/2008

Premier moment de la journée

Si à la place vous voulez que l'heure du jour soit définie sur le premier moment de la journée, utilisez une ZonedDateTimeclasse, puis convertissez-la en LocalDateobjet pour appeler sa atStartOfDayméthode. Sachez que le premier moment peut ne pas être l'heure à 00:00:00cause de l'heure d'été ou peut-être d'autres anomalies.

Le fuseau horaire est crucial car à tout moment, la date varie dans le monde par zone. Par exemple, quelques instants après minuit à Paris est un nouveau jour pour les Parisiens mais c'est toujours «hier» à Montréal pour les Canadiens.

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 [Amérique / Montréal]

UTC

Pour voir ce moment à travers l'objectif du fuseau horaire UTC , extrayez un Instantobjet. Les deux ZonedDateTimeet Instantreprésenteront le même moment sur la chronologie mais apparaîtront comme deux heures d'horloge murale différentes .

An Instantest la classe élémentaire de base de java.time, toujours en UTC par définition. Utilisez cette classe fréquemment, car vous devriez généralement faire votre logique métier, votre stockage de données et votre échange de données en UTC.

Instant instant = zdtStartOfDay.toInstant();

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

Nous voyons 5 heures du matin plutôt que le coup de minuit. Dans le format standard, le Zà la fin est l'abréviation Zuluet signifie «UTC».


À propos java.time

Le framework java.time est intégré à Java 8 et versions ultérieures. Ces classes supplantent les anciens gênants hérités des classes date-heure tels que java.util.Date, Calendar, et SimpleDateFormat.

Le projet Joda-Time , désormais en mode maintenance , conseille la migration vers les classes java.time .

Pour en savoir plus, consultez le didacticiel Oracle . Et recherchez Stack Overflow pour de nombreux exemples et explications. La spécification est JSR 310 .

Vous pouvez échanger des objets java.time directement avec votre base de données. Utilisez un pilote JDBC compatible avec JDBC 4.2 ou version ultérieure. Pas besoin de chaînes, pas besoin de java.sql.*classes.

Où obtenir les classes java.time?

  • Java SE 8 , Java SE 9 et versions ultérieures
    • Intégré.
    • Partie de l'API Java standard avec une implémentation groupée.
    • Java 9 ajoute quelques fonctionnalités et corrections mineures.
  • Java SE 6 et Java SE 7
    • Une grande partie de la fonctionnalité java.time est rétroportée vers Java 6 et 7 dans ThreeTen-Backport .
  • Android
    • Versions ultérieures des implémentations de bundle Android des classes java.time.
    • Pour les versions antérieures d'Android (<26), le projet ThreeTenABP adapte ThreeTen-Backport (mentionné ci-dessus). Voir Comment utiliser ThreeTenABP… .

Le projet ThreeTen-Extra étend java.time avec des classes supplémentaires. Ce projet est un terrain d'essai pour d'éventuels futurs ajouts à java.time. Vous trouverez peut - être des classes utiles ici, comme Interval, YearWeek, YearQuarteret plus .



2

La question est contradictoire. Il demande une date sans heure de la journée mais affiche un exemple avec une heure de 00:00:00.

Joda-Time

MISE À JOUR: Le projet Joda-Time est maintenant en mode maintenance , l'équipe conseillant la migration vers les classes java.time . Voir mon autre réponse pour la solution java.time.

Si à la place vous souhaitez que l'heure du jour soit définie sur le premier moment de la journée, utilisez un DateTimeobjet de la bibliothèque Joda-Time et appelez sa withTimeAtStartOfDayméthode. Sachez que le premier moment peut ne pas être l'heure à 00:00:00cause de l'heure d'été ou peut-être d'autres anomalies.


C'est la réponse définitive (en supposant que l'on puisse et veut utiliser Joda Time)
Clint Eastwood

2

Juste clear()des champs redondants.

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();

À partir de Java 8, une meilleure option consiste à utiliser la truncatedTométhode de LocalDateTime, par exemple:

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

Cela ne fonctionne pas, car calendar.clear(Calendar.HOUR_OF_DAY);les heures ne sont pas effacées. La solution de @cletus fonctionne bien.
Arcao

Désolé, vous avez raison, je l'ai corrigé, pendant des heures, vous devriez définir la valeur. En général, presque de la même façon comment tronquer la date ...
m190

1

Cela m'a vraiment ennuyé que le nouveau constructeur de calendrier "amélioré" ne prenne pas un int pendant des millisecondes comme le "terrible" vieux Date. Je me suis alors vraiment fâché et j'ai écrit ceci:

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

Je n'ai pas utilisé le mot "trucs" à l'époque, mais j'ai ensuite découvert le bonheur de ceci:

startTime.set(Calendar.MILLISECOND, 0);

Mais je suis toujours assez contrarié à ce sujet.


1

J'ai résolu le problème comme ceci (dans la zone des huit est (heure de Beijing)):

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);
}

Tout d'abord, vous devez savoir clairement ce qu'est l'horodatage. L'horodatage correspond au nombre total de millisecondes entre le 01 janvier 00:00:00 1970 et GMT (identique à UTC), ou le jeudi 01 janvier 08:00:00 CST 1970 jusqu'à maintenant.

N'oubliez pas: l'horodatage est indépendant du fuseau horaire.

Vous obtenez donc le même résultat avec l'instruction suivante dans des fuseaux horaires différents:

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

Et

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

imprime différentes informations d'heure dans différents fuseaux horaires: si vous définissez le fuseau horaire de votre ordinateur sur UTC, vous obtenez

Thu Jan 01 00:00:00 UTC 1970

Mais si vous définissez le fuseau horaire sur (UTC +8: 00) Beijing, Chongqing, HongKong, Urumq, vous obtenez:

Thu Jan 01 08:00:00 CST 1970

Java obtient l'horodatage, puis affiche les informations de date et d'heure en fonction du fuseau horaire.

Pour savoir comment Java affiche les informations de date et d'heure dans différents fuseaux horaires, il est facile de transcrire les informations d'heure. Vous devez obtenir l'horodatage et prendre en compte le fuseau horaire lorsque vous coupez les informations horaires. Ensuite, vous pouvez créer un nouvel objet Date avec l'horodatage coupé (nous pouvons l'appeler horodatage), java compensera le fuseau horaire lors de l'affichage des informations de date.

Comme dans la zone Est des huit (heure de Beijing), l'heure de Beijing est antérieure de 8 heures à l'heure GMT, vous devez donc soustraire plus de 8 heures lorsque vous effectuez l'opération modulo. C'est-à-dire que vous devez d'abord obtenir l'heure GMT, puis Java ajoutera 8 heures lors de l'affichage de l'heure en fonction du paramètre de fuseau horaire de votre PC.


La question du fuseau horaire est obscure et me laisse également perplexe pendant longtemps. Maintenant, je le précise. L'espoir aide.


2018-01-04 La méthode ci-dessous fonctionne également.

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();

}


Pour info, cette réponse utilise d'anciennes classes de date-heure gênantes, supplantées il y a des années par les classes java.time .
Basil Bourque

1

Vous pouvez le faire pour éviter les problèmes de fuseau horaire:

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();
}

Bien que l' Dateobjet Java soit une valeur d'horodatage, mais pendant la troncature, il sera converti en fuseau horaire local, vous obtiendrez donc une valeur surprenante si vous attendez une valeur du fuseau horaire UTC.


Les classes gênantes Calendaret Datesont devenues héritées il y a des années avec les classes java.time implémentant JSR 310 dans Java 8 et versions ultérieures. Suggérer leur utilisation en 2018 est un mauvais conseil.
Basil Bourque

0

Peut-être une réponse tardive, mais voici un moyen de le faire en une seule ligne sans utiliser de bibliothèques:

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

0

Avec Joda-Time depuis la version 2.0, vous pouvez utiliserLocalDate.toDate() .

Simplement

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

[A] Votre réponse est redondante. Déjà posté par Brian Agnew . [B] Votre code est incorrect, car il ignore le fuseau horaire, le problème même soulevé dans la question.
Basil Bourque

Cela pourrait être redondant, mais je viens de mettre un exemple plus simple ici car j'ai reconnu qu'une réponse ne devrait pas être dans les commentaires. Oui, java.util.Date ignore TimeZone mais la question d'origine était avec a Java Date objectdonc j'ai utilisé java.util.Date. De plus, cela n'explique pas en quoi son fuseau horaire est problématique avec java.util.Date (quand? Où?) Bien qu'il ne puisse s'empêcher d'utiliser autre que Date.
lamusique

0

Pour toutes les réponses utilisant le calendrier, vous devriez l'utiliser comme ceci à la place

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();
}

Mais je préfère ceci:

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

La java.sql.Dateclasse prétend représenter une valeur de date uniquement, mais a en fait une heure ajustée pour le fuseau horaire UTC. Donc pas vraiment une solution adaptée à la question.
Basil Bourque

0

Voici un moyen simple d'obtenir une date sans heure si vous utilisez Java 8+: utilisez le type java.time.LocalDate au lieu de Date.

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

Le résultat:

2019-05-30

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


1
L' utilisation LocalDateest une bonne idée pour la plupart des cas, mais on ne sait pas comment cela va fonctionner avec l'entrée de la question, 2008-01-01 13:15:00.
Ole VV
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.