Конвертировать java.util.Date в java.time.LocalDate

495

Каков наилучший способ преобразования java.util.Dateобъекта в новый JDK 8 / JSR-310 java.time.LocalDate?

Date input = new Date();
LocalDate date = ???
JodaStephen
источник

Ответы:

828

Короткий ответ

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

объяснение

Несмотря на свое название, java.util.Dateпредставляет собой момент времени, а не «дату». Фактические данные, хранящиеся в объекте, представляют собой longколичество миллисекунд с 1970-01-01T00: 00Z (полночь в начале 1970 года по Гринвичу / UTC).

Класс, эквивалентный классу java.util.Dateв JSR-310, является Instant, таким образом, существует удобный метод toInstant()для обеспечения преобразования:

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

java.util.DateЭкземпляр не имеет понятия о времени зоны. Это может показаться странным , если вы звоните toString()на 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());

Эта новая альтернатива является более прямой, создает меньше мусора и, следовательно, должна работать лучше.

JodaStephen
источник
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не «хранить или представлять время или часовой пояс» (ref: javadocs). Хотя они не хранят его - классы действительно представляют Localдату и / или время, поэтому преобразование в локальную дату / время подразумевает часовой пояс.
Cuga
159

Лучший способ это:

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

Я лично использую служебный класс (но он не совместим с 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);
    }

}

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

Олив
источник
12
Если это лучший способ, он очень уродливый и многословный. Так больно.
ceklock
4
Это лучше по сравнению с принятым ответом, я объясняю почему. Да, это некрасиво, но именно поэтому и написал DateConvertUtils.
Олив
Я не понимаю, почему они не реализовали класс преобразования в новом API.
ceklock
@ceklock, они же реализовать, а не класс преобразования, но несколько методов преобразования, как Date.toInstant().
Оле В.В.
2
Есть ли библиотека Kotlin, которая упаковывает их как функции расширения, чтобы разработчик мог сделать следующее. Дата х = ...; x.asLocalDate ();
Марк Эшворт
20
LocalDate localDate = LocalDate.parse( new SimpleDateFormat("yyyy-MM-dd").format(date) );
ceklock
источник
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));
dhalsim2
источник
4
Не правда, ответ @ JodaStephen все еще работает. Вам просто нужен другой способ конвертировать java.util.Date в Instant. Для этого вы можете использовать org.threeten.bp.DateTimeUtils.toInstant: threeten.org/threetenbp/apidocs/org/threeten/bp/…
Кристиан Сиах
3
DateTimeUtils не был доступен, когда я использовал бэкпорт, но вы правы, что он доступен любому, кто использует ThreeTen Backport 1.0 или более позднюю версию. Спасибо за указание на это.
dhalsim2
14
LocalDate ld = new java.sql.Date( new java.util.Date().getTime() ).toLocalDate();
Gustavo
источник
1
И это простейшая реализация: LocalDate.of (getYear () + 1900, getMonth () + 1, getDate ())
Григорий Кислин
Строки, которые говорят, устарели, хотя: |
TheRealChx101
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(); 

Затем вы можете преобразовать Instant в любую дату api в jdk 8, используя метод ofInstant ():

LocalDateTime date = LocalDateTime.ofInstant(timestamp, ZoneId.systemDefault()); 
Шедом Вэй
источник
включает в себя localDate, localDateTime, localTime
Шедом Вэй
Что значит указать ZoneId здесь. Если я получу Date -> Instant от API, и я могу думать об этом как о миллисекундах с 1970 года в UTC. Когда я хочу получить только соответствующую LocalDate (гггг-мм-дд) с точки зрения API, не преобразованного в какой-либо часовой пояс, я не должен использовать ZoneOffset.UTC, чтобы не иметь этого мгновенного смещения к моему локальному часовому поясу. Разве твой пример с ZoneId.systemDefault () не смещает форму даты сервера. Ex. Мгновенное значение представляет 2018-10-20 0:00, а затем смещено из UTC в Америку / Los_Angeles Я получаю в Местную дату 2018-10-19 вместо 2018-10-20?
Михал Зиобро
Он ломается, если у вас есть 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 Tutorial 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();
rogerio_gentil
источник
-2

Что не так с этой простой строкой?

new LocalDateTime(new Date().getTime()).toLocalDate();
Лоренс
источник
Java жалуется, что не может вызывать toLocalDate () для примитивного типа long. Я использовал реальный объект Date; возможно есть загвоздка ??
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 выведите вашу дату в формате «гггг-мм-дд»

estevamdf
источник
1
Вопрос в поиске решения с использованием java.timeклассов в JDK8, а не с Joda Time.
фото