Convierta java.util.Date a java.time.LocalDate

494

¿Cuál es la mejor manera de convertir un java.util.Dateobjeto al nuevo JDK 8 / JSR-310 java.time.LocalDate?

Date input = new Date();
LocalDate date = ???
JodaStephen
fuente

Respuestas:

828

Respuesta corta

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

Explicación

A pesar de su nombre, java.util.Daterepresenta un instante en la línea de tiempo, no una "fecha". Los datos reales almacenados dentro del objeto son un longrecuento de milisegundos desde 1970-01-01T00: 00Z (medianoche al comienzo de 1970 GMT / UTC).

La clase equivalente a java.util.Dateen JSR-310 es Instant, por lo tanto, hay un método conveniente toInstant()para proporcionar la conversión:

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

Una java.util.Dateinstancia no tiene concepto de zona horaria. Esto puede parecer extraño si llama toString()a un java.util.Date, porque toStringes relativo a una zona horaria. Sin embargo, ese método en realidad usa la zona horaria predeterminada de Java sobre la marcha para proporcionar la cadena. La zona horaria no es parte del estado real de java.util.Date.

An Instanttampoco contiene ninguna información sobre la zona horaria. Por lo tanto, para convertir de una Instanta una fecha local, es necesario especificar una zona horaria. Esta podría ser la zona predeterminada ZoneId.systemDefault(), o podría ser una zona horaria que controla su aplicación, como una zona horaria de las preferencias del usuario. Use el atZone()método para aplicar la zona horaria:

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

A ZonedDateTimecontiene un estado que consiste en la fecha y hora local, la zona horaria y el desplazamiento de GMT / UTC. Como tal, la fecha - LocalDate- se puede extraer fácilmente usando toLocalDate():

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

Respuesta de Java 9

En Java SE 9, se ha agregado un nuevo método que simplifica ligeramente esta tarea:

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

Esta nueva alternativa es más directa, crea menos basura y, por lo tanto, debería funcionar mejor.

JodaStephen
fuente
14
Tenía LocalDate.from(Instant.ofEpochMilli(date.getTime()))Creo que es equivalente a la suya, pero más directa.
Marko Topolnik
99
@MarkoTopolnik que compila pero no se ejecuta. Un Instant no contiene una zona horaria, por lo que no hay forma de obtener el LocalDate.
JodaStephen
11
@assylias ¡Solo use sqlDate.toLocalDate ()!
JodaStephen
25
@JodaStephen Dateno tiene concepto de zona horaria, Instanttampoco contiene información sobre la zona horaria. La LocalDateAPI dice "Una fecha sin zona horaria". Entonces, ¿por conversión de Datea Instanta LocalDatelas necesidades atZone(ZoneId.systemDefault())?
Gustavo
8
@Gustavo: LocalDatey LocalDateTimeno "almacene ni represente una hora o zona horaria" (ref: javadocs). Si bien no lo almacenan, las clases representan una Localfecha y / o hora, por lo tanto, la conversión a fecha / hora local implica una zona horaria.
Cuga
158

La mejor manera es:

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

Ventajas de esta versión:

  • funciona independientemente de que la entrada sea una instancia java.util.Dateo sea una subclase de java.sql.Date(a diferencia de @ JodaStephen). Esto es común con los datos originados por JDBC. java.sql.Date.toInstant()siempre arroja una excepción.

  • es lo mismo para JDK8 y JDK7 con backport JSR-310

Yo personalmente uso una clase de utilidad (pero no es compatible con el 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);
    }

}

El asLocalDate()método aquí es nulo-seguro, utiliza toLocalDate(), si la entrada es java.sql.Date(puede ser anulada por el controlador JDBC para evitar problemas de zona horaria o cálculos innecesarios), de lo contrario utiliza el método mencionado anteriormente.

Oliv
fuente
12
Si esta es la mejor manera, es muy feo y detallado. Muy doloroso.
ceklock
3
Es mejor en comparación con la respuesta aceptada, explico por qué. Sí, es feo, pero por eso escribió DateConvertUtils.
Oliv
No entiendo por qué no implementaron una clase de conversión en la nueva API.
ceklock
@ceklock, que hicieron poner en práctica, no una clase de conversión, pero un par de métodos de conversión, al igual que Date.toInstant().
Ole VV
2
¿Existe una biblioteca Kotlin que los empaquete como funciones de extensión para que un desarrollador pueda hacer lo siguiente? Fecha x = ...; x.asLocalDate ();
Mark Ashworth
20
LocalDate localDate = LocalDate.parse( new SimpleDateFormat("yyyy-MM-dd").format(date) );
ceklock
fuente
77
Aquí, la SimpleDateFormatinstancia se limita al hilo actual. Se utiliza de forma segura para subprocesos. Ahora, SimpleDateFormattiene fama de ser 'caro para crear una instancia' (en la cuenta de todas las estructuras de datos internas que necesita) pero no se puede compartir una como 'Singleton' (sin sincronizar el acceso a ella), porque es de hecho no es thread- seguro. (Una ThreadLocalsolución puede funcionar si el código 'contaminante' Threaden esto es responsable del ciclo de vida del hilo ... pero eso rara vez sucede). Torpe. Evitar SimpleDateFormates la razón para usar javax.time.
David Bullock el
66
Este enfoque tiene muchos gastos generales: el 'costoso' SimpleDateFormat(que se tira), la cadena intermedia (que se tira) y el costo del análisis. Es una solución, pero no se recomienda.
David Bullock
2
@ceklock pero luego tienes el problema de que ya no es seguro
subprocesos
44
@ceklock, SimpleDateFormat solo puede procesar una fecha a la vez; si la usa al mismo tiempo, producirá resultados desordenados. Entonces sí, es importante. No cree una sola instancia de SimpleDateFormat en una variable global.
flup
19

Si está utilizando Java 8, la respuesta de @ JodaStephen es obviamente la mejor. Sin embargo, si está trabajando con el puerto JSR-310 , desafortunadamente tiene que hacer algo como esto:

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
fuente
44
No es cierto, la respuesta de @ JodaStephen todavía funciona. Solo necesita otra forma de convertir java.util.Date a Instant. Para esto puede usar org.threeten.bp.DateTimeUtils.toInstant: threeten.org/threetenbp/apidocs/org/threeten/bp/…
Christian Ciach
3
DateTimeUtils no estaba disponible cuando estaba usando el backport, pero tiene razón en que está disponible para cualquiera que use ThreeTen Backport 1.0 o posterior. Gracias por mencionarlo.
dhalsim2
14
LocalDate ld = new java.sql.Date( new java.util.Date().getTime() ).toLocalDate();
Gustavo
fuente
1
Y es la realización más simple: LocalDate.of (getYear () + 1900, getMonth () + 1, getDate ())
Grigory Kislin
Sin embargo, las líneas que dicen en desuso: |
TheRealChx101
8

Puedes convertir en una línea:

public static LocalDate getLocalDateFromDate(Date date){
   return LocalDate.from(Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()));
}
Sahil Chhabra
fuente
Recibo un error como este usando este método: java.time.DateTimeException: No se puede obtener LocalDate de TemporalAccessor: 2018-01-31T11: 54: 27.964Z de tipo java.time.Instant
Luigi Rubino
@LuigiRubino gracias por señalar. Por favor vea la respuesta actualizada. Olvidé agregar Zone antes.
Sahil Chhabra
8

primero, es fácil convertir una fecha en un instante

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

Luego, puede convertir el Instant a cualquier api de fecha en jdk 8 usando el método ofInstant ():

LocalDateTime date = LocalDateTime.ofInstant(timestamp, ZoneId.systemDefault()); 
Shedom Wei
fuente
incluye localDate, localDateTime, localTime
Shedom Wei
¿Qué significa especificar ZoneId aquí? Si obtengo Date -> Instant de API y puedo pensarlo como milisegundos desde 1970 en UTC. Cuando quiero obtener solo LocalDate correspondiente (aaaa-mm-dd) desde la perspectiva de la API no convertida a ninguna zona horaria, ¿no debería usar ZoneOffset.UTC para no tener este desplazamiento instantáneo a mi zona horaria local? ¿No es su ejemplo con ZoneId.systemDefault () el cambio de fecha del servidor de formularios? Ex. instant representa el 20-10-2018 a las 0:00 y luego pasó de UTC a América / Los_Angeles ¿Obtuve la Fecha local 2018-10-19 en lugar de 2018-10-20?
Michał Ziobro
Se rompe si tiene import java.sql.Dateen su archivo: el toInstant()método de java.sql.Datesiempre tira.
Oliv
4
Date input = new Date();
LocalDateTime  conv=LocalDateTime.ofInstant(input.toInstant(), ZoneId.systemDefault());
LocalDate convDate=conv.toLocalDate();

La Dateinstancia también contiene tiempo junto con la fecha, mientras LocalDateque no. Por lo tanto, primero puede convertirlo LocalDateTimeusando su método, ofInstant()luego, si lo desea sin tiempo , convierta la instancia en LocalDate.

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

este formato es 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();
    }
宏杰 李
fuente
0

He tenido problemas con la implementación de @ JodaStephen en JBoss EAP 6. Entonces, reescribí la conversión siguiendo el Tutorial Java de Oracle en 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
fuente
-2

¿Qué hay de malo en esta 1 línea simple?

new LocalDateTime(new Date().getTime()).toLocalDate();
Lawrence
fuente
Java se queja de que no puede invocar toLocalDate () en el tipo primitivo de largo. Usé un objeto Date real; tal vez ahí está el problema?
TheGeeko61
-8

Resolví esta pregunta con la solución a continuación

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

En este caso, localDate imprime su fecha en este formato "aaaa-MM-dd"

estevamdf
fuente
1
La pregunta es buscar una solución usando las java.timeclases en JDK8, no con Joda Time.
pioto