Перетворити java.util.Date в java.time.LocalDate


494

Який найкращий спосіб перетворити java.util.Dateоб’єкт на новий JDK 8 / JSR-310 java.time.LocalDate?

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

Відповіді:


828

Коротка відповідь

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

Пояснення

Незважаючи на свою назву, java.util.Dateявляє собою момент на часовій лінії, а не "дату". Фактичні дані, що зберігаються в об'єкті, - це longкількість мілісекунд з 1970-01-01T00: 00Z (опівночі на початку 1970 GMT / UTC).

Клас, еквівалентний java.util.DateJSR-310, є Instant, таким чином, існує зручний спосіб toInstant()забезпечення перетворення:

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

java.util.DateПримірник не має поняття про час зони. Це може здатися дивним, якщо ви зателефонуєте toString()на "a" java.util.Date, оскільки значення toStringвідносно часового поясу. Однак цей метод фактично використовує часовий пояс Java за замовчуванням на ходу для надання рядка. Часовий пояс не є частиною фактичного стану Росії java.util.Date.

А Instantтакож не містить жодної інформації про часовий пояс. Таким чином, для перетворення з Instantна місцеву дату необхідно вказати часовий пояс. Це може бути зона за замовчуванням - ZoneId.systemDefault()- або це часовий пояс, яким керує ваша програма, наприклад часовий пояс із налаштувань користувача. Використовуйте atZone()метод для застосування часового поясу:

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

А ZonedDateTimeмістить стан, що складається з локальної дати та часу, часового поясу та зміщення від GMT / UTC. Отже, дата - LocalDate- може бути легко вилучена за допомогою toLocalDate():

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

Відповідь Java 9

У Java SE 9 додано новий метод , який трохи спрощує це завдання:

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

Ця нова альтернатива є більш прямою, створюючи менше сміття, і, отже, повинна працювати краще.


14
Я LocalDate.from(Instant.ofEpochMilli(date.getTime()))думав, що це рівнозначно вашому, але більш пряме.
Марко Тополник

9
@MarkoTopolnik, який компілює, але не працює. Миттєвий пошук не містить часового поясу, тому немає можливості отримати LocalDate.
JodaStephen

11
@assylias Просто використовуйте sqlDate.toLocalDate ()!
JodaStephen

25
@JodaStephen Dateне має поняття часового поясу, Instantтакож не містить інформації про часовий пояс. LocalDateAPI говорить «датувати без часового поясу». Тоді чому перетворення від Dateдо Instantдо LocalDateпотреб atZone(ZoneId.systemDefault())?
Густаво

8
@Gustavo: LocalDateі LocalDateTimeне "зберігати і не представляти час або часовий пояс" (посилання: javadocs). Хоча вони не зберігають його - класи представляють Localдату та / або час, отже, перетворення на локальну дату / час передбачає часовий пояс.
Cuga

158

Кращий спосіб:

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

Переваги цієї версії:

  • працює незалежно від введення - це примірник java.util.Dateабо це підклас java.sql.Date(на відміну від способу @ JodaStephen). Це є загальним для даних JDBC. java.sql.Date.toInstant()завжди кидає виняток.

  • це те ж саме для JDK8 та JDK7 з підтримкою JSR-310

Я особисто використовую клас утиліти (але він не підтримує підтримку):

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

}

asLocalDate()Тут метод нуль-безпечна, використовує toLocalDate(), якщо вхід java.sql.Date(він може бути перевизначений драйвером JDBC до проблем уникнути часових поясів або непотрібних обчислень), в іншому випадку використовує вищезгаданий метод.


12
Якщо це кращий спосіб, це дуже некрасиво і багатослівно. Так боляче.
ceklock

3
Краще порівняно з прийнятою відповіддю, пояснюю чому. Так, це некрасиво, але саме тому написали DateConvertUtils.
Олів

Я не розумію, чому вони не впровадили клас перетворення в новому API.
ceklock

@ceklock, вони ж реалізувати, а не клас перетворення, але кілька методів перетворення, як Date.toInstant().
Оле ВВ

2
Чи існує бібліотека Котліна, яка пакує їх як функції розширення, щоб розробник міг зробити наступне. Дата х = ...; x.asLocalDate ();
Марк Ешворт

20
LocalDate localDate = LocalDate.parse( new SimpleDateFormat("yyyy-MM-dd").format(date) );

7
Тут SimpleDateFormatекземпляр обмежений поточним потоком. Він використовується безпечним для ниток способом. Тепер, SimpleDateFormatяк вважається, "дорого інстанціювати" (за рахунок усіх внутрішніх структур даних, які йому потрібні), але ви не можете поділитися ним як "синглтон" (без синхронізації доступу до нього), оскільки він справді не є потоковим, сейф. ( ThreadLocalРішення може спрацювати, якщо код "забруднює" це Threadв цьому відповідає за життєвий цикл нитки ... але це трапляється рідко). Незграбний. Уникання SimpleDateFormat- це причина використання javax.time.
Девід Баллок

6
Цей підхід має великі накладні витрати: «дорогий» SimpleDateFormat(який викидається), проміжний рядок (який викидається) та вартість розбору. Це рішення, але не рекомендується.
Девід Баллок

2
@ceklock, але тоді у вас з’являється проблема, що це вже не безпечно для
потоків

4
@ceklock SimpleDateFormat може обробляти лише одну дату, якщо ви одночасно користуєтесь нею, це дасть невдалі результати. Так що так, важливо. Не створюйте жодного примірника SimpleDateFormat у глобальній змінній.
флюп

19

Якщо ви використовуєте Java 8, відповідь @ JodaStephen очевидно найкраща. Однак, якщо ви працюєте з опорою JSR-310 , вам, на жаль, потрібно зробити щось подібне:

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

4
Неправда, відповідь @ JodaStephen все ще працює. Вам просто потрібен інший спосіб перетворити java.util.Date в миттєвий. Для цього ви можете використовувати org.threeten.bp.DateTimeUtils.toInstant: threeten.org/threetenbp/apidocs/org/threeten/bp/…
Крістіан Сіач

3
DateTimeUtils не був доступний, коли я використовував резервний порт, але ви правильні, що він доступний для всіх, хто використовує ThreeTen Backport 1.0 або пізнішої версії. Дякуємо, що вказали на це.
dhalsim2


8

Ви можете конвертувати в один рядок:

public static LocalDate getLocalDateFromDate(Date date){
   return LocalDate.from(Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()));
}

Я отримую подібну помилку за допомогою цього методу: java.time.DateTimeException: Неможливо отримати LocalDate від TemporalAccessor: 2018-01-31T11: 54: 27.964Z типу java.time.Instant
Луїджі Рубіно

@LuigiRubino дякую за те, що вказали. Будь ласка, дивіться оновлену відповідь. Раніше я забув додати Зону.
Сахіл Чхабра

8

по-перше, легко перетворити дату на миттєвий

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

Тоді ви можете конвертувати Миттєвий пошук до будь-якого api дати у jdk 8 за допомогою методу ofInstant ():

LocalDateTime date = LocalDateTime.ofInstant(timestamp, ZoneId.systemDefault()); 

Включає localDate, localDateTime, localTime
Shedom Wei

Що означає тут вказати ZoneId. Якщо я отримаю Date -> Instant від API і я можу вважати це мілісекундами з 1970 року в UTC. Коли я хочу отримати відповідний LocalDate (yyyy-mm-dd) з точки зору api, не перетвореного в будь-який часовий пояс, я не повинен використовувати ZoneOffset.UTC, щоб не здійснити це миттєве зміщення на мій місцевий часовий пояс. Чи не ваш приклад із ZoneId.systemDefault () - це зміщення сервера форми дат. Вих. момент представляє 2018-10-20 0:00, а потім перейшов з UTC в Америку / Los_Angeles Я потрапляю в Local Date 2018-10-19 замість 2018-10-20?
Michał Ziobro

Він порушується, якщо import java.sql.Dateу вашому файлі трапляється : toInstant()метод java.sql.Dateзавжди кидає.
Олів

4
Date input = new Date();
LocalDateTime  conv=LocalDateTime.ofInstant(input.toInstant(), ZoneId.systemDefault());
LocalDate convDate=conv.toLocalDate();

DateПримірник дійсно містить час теж разом з датою поки LocalDateне робить. Таким чином , ви можете в першу чергу перетворити його в LocalDateTimeвикористанні його методу ofInstant()потім , якщо ви хочете без часу потім перетворити екземпляр в LocalDate.


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

цей формат від 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();
    }

0

У мене виникли проблеми з реалізацією @ JodaStephen на JBoss EAP 6. Отже, я переписав перетворення після Java-підручника Oracle в 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();

-2

Що з цим 1 простим рядком?

new LocalDateTime(new Date().getTime()).toLocalDate();

Java скаржиться, що не може довго запускати toLocalDate () для примітивного типу. Я використовував фактичний об'єкт Дата; можливо, там є руб?
TheGeeko61

-8

Я вирішив це питання з рішенням нижче

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

У цьому випадку localDate надрукує свою дату у такому форматі "yyyy-MM-dd"


1
Питання шукає рішення, використовуючи java.timeкласи в JDK8, а не Joda Time.
піото
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.