Convertir java.util.Date en java.time.LocalDate

494

Quelle est la meilleure façon de convertir un java.util.Dateobjet vers le nouveau JDK 8 / JSR-310 java.time.LocalDate?

Date input = new Date();
LocalDate date = ???
JodaStephen
la source

Réponses:

828

Réponse courte

Date input = new Date();
LocalDate date = input.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();

Explication

Malgré son nom, java.util.Datereprésente un instant sur la ligne du temps, pas une "date". Les données réelles stockées dans l'objet sont un longdécompte des millisecondes depuis le 1970-01-01T00: 00Z (minuit au début de 1970 GMT / UTC).

La classe équivalente à java.util.DateJSR-310 est Instant, il existe donc une méthode pratique toInstant()pour fournir la conversion:

Date input = new Date();
Instant instant = input.toInstant();

Une java.util.Dateinstance n'a pas de concept de fuseau horaire. Cela peut sembler étrange si vous appelez toString()un java.util.Date, car le toStringest relatif à un fuseau horaire. Cependant, cette méthode utilise en fait le fuseau horaire par défaut de Java à la volée pour fournir la chaîne. Le fuseau horaire ne fait pas partie de l'état réel de java.util.Date.

Un Instantne contient pas non plus d'informations sur le fuseau horaire. Ainsi, pour convertir une Instantdate en date locale, il est nécessaire de spécifier un fuseau horaire. Il peut s'agir du fuseau par défaut - ZoneId.systemDefault()- ou il peut s'agir d'un fuseau horaire contrôlé par votre application, tel qu'un fuseau horaire issu des préférences de l'utilisateur. Utilisez la atZone()méthode pour appliquer le fuseau horaire:

Date input = new Date();
Instant instant = input.toInstant();
ZonedDateTime zdt = instant.atZone(ZoneId.systemDefault());

Un ZonedDateTimecontient un état composé de la date et de l'heure locales, du fuseau horaire et du décalage par rapport à GMT / UTC. En tant que telle, la date - LocalDate- peut être facilement extraite en utilisant toLocalDate():

Date input = new Date();
Instant instant = input.toInstant();
ZonedDateTime zdt = instant.atZone(ZoneId.systemDefault());
LocalDate date = zdt.toLocalDate();

Réponse de Java 9

Dans Java SE 9, une nouvelle méthode a été ajoutée qui simplifie légèrement cette tâche:

Date input = new Date();
LocalDate date = LocalDate.ofInstant(input.toInstant(), ZoneId.systemDefault());

Cette nouvelle alternative est plus directe, créant moins de déchets et devrait donc être plus performante.

JodaStephen
la source
14
J'avais LocalDate.from(Instant.ofEpochMilli(date.getTime()))je pense que c'est équivalent au vôtre, mais plus direct.
Marko Topolnik
9
@MarkoTopolnik qui compile mais ne s'exécute pas. Un Instant ne contient pas de fuseau horaire donc il n'y a aucun moyen d'obtenir le LocalDate.
JodaStephen
11
@assylias Utilisez simplement sqlDate.toLocalDate ()!
JodaStephen
25
@JodaStephen Daten'a pas de concept de fuseau horaire, Instantne contient pas non plus d'informations sur le fuseau horaire. L' LocalDateAPI dit "Une date sans fuseau horaire". Alors pourquoi convertir de Dateà Instanten LocalDatebesoins atZone(ZoneId.systemDefault())?
Gustavo
8
@Gustavo: LocalDateet LocalDateTimene pas "stocker ou représenter un fuseau horaire" (ref: javadocs). Bien qu'ils ne les stockent pas, les classes représentent une Localdate et / ou une heure, donc la conversion en date / heure locale implique un fuseau horaire.
Cuga
158

La meilleure façon est:

Date date = ...;
Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDate()

Avantages de cette version:

  • fonctionne quelle que soit l'entrée est une instance de java.util.Dateou c'est une sous-classe de java.sql.Date(contrairement à la manière de @ JodaStephen). Ceci est courant avec les données d'origine JDBC. java.sql.Date.toInstant()lève toujours une exception.

  • c'est la même chose pour JDK8 et JDK7 avec backport JSR-310

J'utilise personnellement une classe utilitaire (mais elle n'est pas compatible avec le backport):

/**
 * Utilities for conversion between the old and new JDK date types 
 * (between {@code java.util.Date} and {@code java.time.*}).
 * 
 * <p>
 * All methods are null-safe.
 */
public class DateConvertUtils {

    /**
     * Calls {@link #asLocalDate(Date, ZoneId)} with the system default time zone.
     */
    public static LocalDate asLocalDate(java.util.Date date) {
        return asLocalDate(date, ZoneId.systemDefault());
    }

    /**
     * Creates {@link LocalDate} from {@code java.util.Date} or it's subclasses. Null-safe.
     */
    public static LocalDate asLocalDate(java.util.Date date, ZoneId zone) {
        if (date == null)
            return null;

        if (date instanceof java.sql.Date)
            return ((java.sql.Date) date).toLocalDate();
        else
            return Instant.ofEpochMilli(date.getTime()).atZone(zone).toLocalDate();
    }

    /**
     * Calls {@link #asLocalDateTime(Date, ZoneId)} with the system default time zone.
     */
    public static LocalDateTime asLocalDateTime(java.util.Date date) {
        return asLocalDateTime(date, ZoneId.systemDefault());
    }

    /**
     * Creates {@link LocalDateTime} from {@code java.util.Date} or it's subclasses. Null-safe.
     */
    public static LocalDateTime asLocalDateTime(java.util.Date date, ZoneId zone) {
        if (date == null)
            return null;

        if (date instanceof java.sql.Timestamp)
            return ((java.sql.Timestamp) date).toLocalDateTime();
        else
            return Instant.ofEpochMilli(date.getTime()).atZone(zone).toLocalDateTime();
    }

    /**
     * Calls {@link #asUtilDate(Object, ZoneId)} with the system default time zone.
     */
    public static java.util.Date asUtilDate(Object date) {
        return asUtilDate(date, ZoneId.systemDefault());
    }

    /**
     * Creates a {@link java.util.Date} from various date objects. Is null-safe. Currently supports:<ul>
     * <li>{@link java.util.Date}
     * <li>{@link java.sql.Date}
     * <li>{@link java.sql.Timestamp}
     * <li>{@link java.time.LocalDate}
     * <li>{@link java.time.LocalDateTime}
     * <li>{@link java.time.ZonedDateTime}
     * <li>{@link java.time.Instant}
     * </ul>
     * 
     * @param zone Time zone, used only if the input object is LocalDate or LocalDateTime.
     * 
     * @return {@link java.util.Date} (exactly this class, not a subclass, such as java.sql.Date)
     */
    public static java.util.Date asUtilDate(Object date, ZoneId zone) {
        if (date == null)
            return null;

        if (date instanceof java.sql.Date || date instanceof java.sql.Timestamp)
            return new java.util.Date(((java.util.Date) date).getTime());
        if (date instanceof java.util.Date)
            return (java.util.Date) date;
        if (date instanceof LocalDate)
            return java.util.Date.from(((LocalDate) date).atStartOfDay(zone).toInstant());
        if (date instanceof LocalDateTime)
            return java.util.Date.from(((LocalDateTime) date).atZone(zone).toInstant());
        if (date instanceof ZonedDateTime)
            return java.util.Date.from(((ZonedDateTime) date).toInstant());
        if (date instanceof Instant)
            return java.util.Date.from((Instant) date);

        throw new UnsupportedOperationException("Don't know hot to convert " + date.getClass().getName() + " to java.util.Date");
    }

    /**
     * Creates an {@link Instant} from {@code java.util.Date} or it's subclasses. Null-safe.
     */
    public static Instant asInstant(Date date) {
        if (date == null)
            return null;
        else
            return Instant.ofEpochMilli(date.getTime());
    }

    /**
     * Calls {@link #asZonedDateTime(Date, ZoneId)} with the system default time zone.
     */
    public static ZonedDateTime asZonedDateTime(Date date) {
        return asZonedDateTime(date, ZoneId.systemDefault());
    }

    /**
     * Creates {@link ZonedDateTime} from {@code java.util.Date} or it's subclasses. Null-safe.
     */
    public static ZonedDateTime asZonedDateTime(Date date, ZoneId zone) {
        if (date == null)
            return null;
        else
            return asInstant(date).atZone(zone);
    }

}

La asLocalDate()méthode ici est null-safe, utilise toLocalDate(), si l'entrée est java.sql.Date(elle peut être remplacée par le pilote JDBC pour éviter les problèmes de fuseau horaire ou les calculs inutiles), sinon utilise la méthode susmentionnée.

Oliv
la source
12
Si c'est la meilleure façon, c'est très laid et verbeux. Si douloureux.
ceklock
3
C'est mieux par rapport à la réponse acceptée, j'explique pourquoi. Oui, c'est moche, mais c'est pourquoi j'ai écrit DateConvertUtils.
Oliv
Je ne comprends pas pourquoi ils n'ont pas implémenté de classe de conversion dans la nouvelle API.
ceklock
@ceklock, ils ont implémenté non pas une classe de conversion, mais quelques méthodes de conversion, comme Date.toInstant().
Ole VV
2
Existe-t-il une bibliothèque Kotlin qui les regroupe en tant que fonctions d'extension afin qu'un développeur puisse faire ce qui suit. Date x = ...; x.asLocalDate ();
Mark Ashworth
20
LocalDate localDate = LocalDate.parse( new SimpleDateFormat("yyyy-MM-dd").format(date) );
ceklock
la source
7
Ici, l' SimpleDateFormatinstance est limitée au thread actuel. Il est utilisé d'une manière thread-safe. Maintenant, SimpleDateFormatest réputé être «cher à instancier» (en raison de toutes les structures de données internes dont il a besoin) mais vous ne pouvez pas en partager un en tant que «singleton» (sans synchroniser l'accès à celui-ci), car il n'est en effet pas thread- sûr. (Une ThreadLocalsolution peut fonctionner si le code "polluant" Threaddans celui-ci était responsable du cycle de vie du thread ... mais cela arrive rarement). Gênant. L'évitement SimpleDateFormatest la raison de l'utilisation javax.time.
David Bullock
6
Cette approche a beaucoup de frais généraux: le «cher» SimpleDateFormat(qui est jeté), la chaîne intermédiaire (qui est jetée) et le coût de l'analyse. C'est une solution, mais non recommandée.
David Bullock
2
@ceklock mais le problème est que ce n'est plus thread-safe
flup
4
@ceklock, SimpleDateFormat ne peut traiter qu'une seule date à la fois, si vous l'utilisez simultanément, cela donnera des résultats altérés. Alors oui, c'est important. Ne créez pas une seule instance de SimpleDateFormat dans une variable globale.
flup
19

Si vous utilisez Java 8, la réponse de @ JodaStephen est évidemment la meilleure. Cependant, si vous travaillez avec le backport JSR-310 , vous devez malheureusement faire quelque chose comme ceci:

Date input = new Date();
Calendar cal = Calendar.getInstance();
cal.setTime(input);
LocalDate date = LocalDate.of(cal.get(Calendar.YEAR),
        cal.get(Calendar.MONTH) + 1,
        cal.get(Calendar.DAY_OF_MONTH));
dhalsim2
la source
4
Ce n'est pas vrai, la réponse de @ JodaStephen fonctionne toujours. Vous avez juste besoin d'une autre façon de convertir java.util.Date en un instantané. Pour cela, vous pouvez utiliser org.threeten.bp.DateTimeUtils.toInstant: threeten.org/threetenbp/apidocs/org/threeten/bp/…
Christian Ciach
3
DateTimeUtils n'était pas disponible lorsque j'utilisais le backport, mais vous avez raison de dire qu'il est disponible pour toute personne utilisant ThreeTen Backport 1.0 ou une version ultérieure. Merci de l'avoir signalé.
dhalsim2
14
LocalDate ld = new java.sql.Date( new java.util.Date().getTime() ).toLocalDate();
Gustavo
la source
1
Et sa réalisation la plus simple: LocalDate.of (getYear () + 1900, getMonth () + 1, getDate ())
Grigory Kislin
Les lignes qui disent dépréciées cependant: |
TheRealChx101
8

Vous pouvez convertir en une seule ligne:

public static LocalDate getLocalDateFromDate(Date date){
   return LocalDate.from(Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()));
}
Sahil Chhabra
la source
J'obtiens une erreur comme celle-ci en utilisant cette méthode: java.time.DateTimeException: Impossible d'obtenir LocalDate de TemporalAccessor: 2018-01-31T11: 54: 27.964Z de type java.time.Instant
Luigi Rubino
@LuigiRubino merci de l'avoir signalé. Veuillez consulter la réponse mise à jour. J'ai oublié d'ajouter Zone plus tôt.
Sahil Chhabra
8

tout d'abord, il est facile de convertir une date en instantané

Instant timestamp = new Date().toInstant(); 

Ensuite, vous pouvez convertir l'instantané en n'importe quelle API de date dans jdk 8 en utilisant la méthode ofInstant ():

LocalDateTime date = LocalDateTime.ofInstant(timestamp, ZoneId.systemDefault()); 
Shedom Wei
la source
comprend localDate, localDateTime, localTime
Shedom Wei
Que signifie spécifier ici ZoneId. Si je reçois Date -> Instant de l'API et que je peux y penser en millisecondes à partir de 1970 en UTC. Lorsque je souhaite obtenir uniquement la date locale correspondante (aaaa-mm-jj) du point de vue de l'API non convertie en fuseau horaire, ne devrais-je pas utiliser ZoneOffset.UTC pour ne pas avoir cet décalage instantané dans mon fuseau horaire local. N'est-ce pas votre exemple avec ZoneId.systemDefault () qui change le serveur de formulaire de date. Ex. instant représente 2018-10-20 0:00 et est ensuite passé de UTC à America / Los_Angeles Je reçois la date locale 2018-10-19 au lieu de 2018-10-20?
Michał Ziobro
Il casse s'il vous arrive d'avoir import java.sql.Datedans votre dossier: la toInstant()méthode des java.sql.Datetoujours lance.
Oliv
4
Date input = new Date();
LocalDateTime  conv=LocalDateTime.ofInstant(input.toInstant(), ZoneId.systemDefault());
LocalDate convDate=conv.toLocalDate();

L' Dateinstance contient également l'heure ainsi que la date alors que ce LocalDaten'est pas le cas. Ainsi, vous pouvez d'abord le convertir en LocalDateTimeutilisant sa méthode, ofInstant()puis si vous le souhaitez sans temps, convertissez l'instance en LocalDate.

Arun Raaj
la source
1
public static LocalDate Date2LocalDate(Date date) {
        return LocalDate.parse(date.toString(), DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss zzz yyyy"))

ce format est de Date#tostring

    public String toString() {
        // "EEE MMM dd HH:mm:ss zzz yyyy";
        BaseCalendar.Date date = normalize();
        StringBuilder sb = new StringBuilder(28);
        int index = date.getDayOfWeek();
        if (index == BaseCalendar.SUNDAY) {
            index = 8;
        }
        convertToAbbr(sb, wtb[index]).append(' ');                        // EEE
        convertToAbbr(sb, wtb[date.getMonth() - 1 + 2 + 7]).append(' ');  // MMM
        CalendarUtils.sprintf0d(sb, date.getDayOfMonth(), 2).append(' '); // dd

        CalendarUtils.sprintf0d(sb, date.getHours(), 2).append(':');   // HH
        CalendarUtils.sprintf0d(sb, date.getMinutes(), 2).append(':'); // mm
        CalendarUtils.sprintf0d(sb, date.getSeconds(), 2).append(' '); // ss
        TimeZone zi = date.getZone();
        if (zi != null) {
            sb.append(zi.getDisplayName(date.isDaylightTime(), TimeZone.SHORT, Locale.US)); // zzz
        } else {
            sb.append("GMT");
        }
        sb.append(' ').append(date.getYear());  // yyyy
        return sb.toString();
    }
宏杰 李
la source
0

J'ai eu des problèmes avec l'implémentation de @ JodaStephen sur JBoss EAP 6. J'ai donc réécrit la conversion en suivant le tutoriel Java d'Oracle dans http://docs.oracle.com/javase/tutorial/datetime/iso/legacy.html .

    Date input = new Date();
    GregorianCalendar gregorianCalendar = (GregorianCalendar) Calendar.getInstance();
    gregorianCalendar.setTime(input);
    ZonedDateTime zonedDateTime = gregorianCalendar.toZonedDateTime();
    zonedDateTime.toLocalDate();
rogerio_gentil
la source
-2

Quel est le problème avec cette 1 ligne simple?

new LocalDateTime(new Date().getTime()).toLocalDate();
Lawrence
la source
Java se plaint de ne pas pouvoir invoquer toLocalDate () sur le type primitif long. J'ai utilisé un véritable objet Date; peut-être qu'il y a le hic ??
TheGeeko61
-8

J'ai résolu cette question avec la solution ci-dessous

  import org.joda.time.LocalDate;
  Date myDate = new Date();
  LocalDate localDate = LocalDate.fromDateFields(myDate);
  System.out.println("My date using Date" Nov 18 11:23:33 BRST 2016);
  System.out.println("My date using joda.time LocalTime" 2016-11-18);

Dans ce cas localDate imprimez votre date dans ce format "aaaa-MM-jj"

estevamdf
la source
1
La question cherche une solution en utilisant les java.timeclasses en JDK8, pas avec Joda Time.
pioto