Преобразование между java.time.LocalDateTime и java.util.Date

Java 8 имеет совершенно новый API для даты и времени. Одним из наиболее полезных классов в этом API является LocalDateTime, для хранения независимого от времени времени значения даты со временем.

Для этой цели существует, вероятно, миллионы строк кода, используя для этого устаревший класс java.util.Date. Таким образом, при взаимодействии старого и нового кода потребуется преобразование между ними. Поскольку, как представляется, нет прямых методов для этого, как это можно сделать?

Ответ 1

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

Date in = new Date();
LocalDateTime ldt = LocalDateTime.ofInstant(in.toInstant(), ZoneId.systemDefault());
Date out = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant());

Пояснение: (на основе этого вопроса о LocalDate)

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

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

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

Экземпляр java.util.Date не имеет понятия часового пояса. Это может показаться странным, если вы вызываете toString() для java.util.Date, потому что toString относится к toString поясу. Однако этот метод на самом деле использует часовой пояс Java по умолчанию на лету для предоставления строки. Часовой пояс не является частью фактического состояния java.util.Date.

Instant также не содержит никакой информации о часовом поясе. Таким образом, для перевода из Instant в локальное время и дату необходимо указать часовой пояс. Это может быть зона по умолчанию - ZoneId.systemDefault() - или это может быть часовой пояс, которым управляет ваше приложение, например часовой пояс из пользовательских настроек. LocalDateTime имеет удобный фабричный метод, который принимает как мгновенный, так и часовой пояс:

Date in = new Date();
LocalDateTime ldt = LocalDateTime.ofInstant(in.toInstant(), ZoneId.systemDefault());

И наоборот, LocalDateTime часовой пояс указывается путем вызова atZone(ZoneId). ZonedDateTime затем может быть превращена непосредственно в Instant:

LocalDateTime ldt = ...
ZonedDateTime zdt = ldt.atZone(ZoneId.systemDefault());
Date output = Date.from(zdt.toInstant());

Обратите внимание, что преобразование из LocalDateTime в ZonedDateTime может привести к неожиданному поведению. Это связано с тем, что не каждое местное время существует из-за перехода на летнее время. Осенью/осенью местное время перекрывается, когда одна и та же местная дата-время встречается дважды. Spring есть разрыв, где час исчезает. См. Javadoc atZone(ZoneId) для более atZone(ZoneId) определения того, что будет делать преобразование.

LocalDateTime итоги, если вы отправляете java.util.Date LocalDateTime и обратно в LocalDateTime и обратно в java.util.Date вас может быть другое мгновение из-за перехода на летнее время.

Дополнительная информация: есть еще одно отличие, которое повлияет на очень старые даты. java.util.Date использует календарь, который изменяется на 15 октября 1582 года с датами до этого, используя юлианский календарь вместо григорианского. В отличие от этого, java.time.* Использует систему календаря ISO (эквивалент григорианского) для всех времен. В большинстве случаев вам нужна система календаря ISO, но вы можете увидеть странные эффекты при сравнении дат до 1582 года.

Ответ 2

Вот что я придумал (и, как и все головоломки "Время по времени", вероятно, это будет опровергнуто на основе какой-то странной регулировки часового пояса - дневного света: D)

Круглое отключение: DateLocalDateTime

Учитывая: Date date = [some date]

(1) LocalDateTime < Instant < < Date

    Instant instant = Instant.ofEpochMilli(date.getTime());
    LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneOffset.UTC);

(2) Date < Instant < LocalDateTime

    Instant instant = ldt.toInstant(ZoneOffset.UTC);
    Date date = Date.from(instant);

Пример:

Дано:

Date date = new Date();
System.out.println(date + " long: " + date.getTime());

(1) LocalDateTime < Instant < < Date:

Создать Instant от Date:

Instant instant = Instant.ofEpochMilli(date.getTime());
System.out.println("Instant from Date:\n" + instant);

Создать Date из Instant (необязательно, но для иллюстрации):

date = Date.from(instant);
System.out.println("Date from Instant:\n" + date + " long: " + date.getTime());

Создать LocalDateTime из Instant

LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneOffset.UTC);
System.out.println("LocalDateTime from Instant:\n" + ldt);

(2) Date < Instant < LocalDateTime

Создать Instant из LocalDateTime:

instant = ldt.toInstant(ZoneOffset.UTC);
System.out.println("Instant from LocalDateTime:\n" + instant);

Создать Date из Instant:

date = Date.from(instant);
System.out.println("Date from Instant:\n" + date + " long: " + date.getTime());

Выход:

Fri Nov 01 07:13:04 PDT 2013 long: 1383315184574

Instant from Date:
2013-11-01T14:13:04.574Z

Date from Instant:
Fri Nov 01 07:13:04 PDT 2013 long: 1383315184574

LocalDateTime from Instant:
2013-11-01T14:13:04.574

Instant from LocalDateTime:
2013-11-01T14:13:04.574Z

Date from Instant:
Fri Nov 01 07:13:04 PDT 2013 long: 1383315184574

Ответ 3

Гораздо более удобный способ, если вы уверены, что вам нужен часовой пояс по умолчанию:

Date d = java.sql.Timestamp.valueOf( myLocalDateTime );

Ответ 4

при преобразовании из нового API LocalDateTime в java.util.date работает следующее:

Date.from(ZonedDateTime.of({time as LocalDateTime}, ZoneId.systemDefault()).toInstant());

обратное преобразование может (надеюсь) достигнуто аналогичным образом...

надеюсь, что это поможет...

Ответ 5

Я не уверен, что это самый простой или лучший способ, или если есть какие-то подводные камни, но он работает:

static public LocalDateTime toLdt(Date date) {
    GregorianCalendar cal = new GregorianCalendar();
    cal.setTime(date);
    ZonedDateTime zdt = cal.toZonedDateTime();
    return zdt.toLocalDateTime();
}

static public Date fromLdt(LocalDateTime ldt) {
    ZonedDateTime zdt = ZonedDateTime.of(ldt, ZoneId.systemDefault());
    GregorianCalendar cal = GregorianCalendar.from(zdt);
    return cal.getTime();
}

Ответ 6

Все здесь: http://blog.progs.be/542/date-to-java-time

Ответ с "круглым отключением" не является точным: когда вы делаете

LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneOffset.UTC);

Если ваш часовой пояс системы не UTC/GMT, вы меняете время!

Ответ 7

Если вы работаете на android и используете threetenbp, вы можете использовать DateTimeUtils.

например:

Date date = DateTimeUtils.toDate(localDateTime.atZone(ZoneId.systemDefault()).toInstant());

Вы не можете использовать Date.from так как он поддерживается только на API 26+

Ответ 8

Самый быстрый способ для LocalDateTimeDate:

Date.from(ldt.toInstant(ZoneOffset.UTC))