Преобразование между 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)
Круглое отключение: Date
↔ LocalDateTime
Учитывая: 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
Самый быстрый способ для LocalDateTime
→ Date
:
Date.from(ldt.toInstant(ZoneOffset.UTC))