Преобразование между java.time.LocalDateTime и java.util.Date
Java 8 имеет совершенно новый API для даты и времени. Один из самых полезных классов в этом API LocalDateTime
, для хранения значения даты-времени, не зависящего от часового пояса.
Вероятно, миллионы строк кода используют устаревший класс java.util.Date
для этого. Таким образом, при взаимодействии старого и нового кода будет необходимо преобразование между ними. Как, кажется, нет прямых методов для достижения этой цели, как это можно сделать?
7 ответов
Короткий ответ:
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
относительно часового пояса. Однако этот метод на самом деле использует часовой пояс 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
имеет потенциал, чтобы ввести неожиданное поведение. Это связано с тем, что не каждое местное время существует из-за перехода на летнее время. Осенью / осенью местное время перекрывается, когда одна и та же местная дата-время встречается дважды. Весной есть разрыв, где час исчезает. См. Javadoc atZone(ZoneId)
для большего определения того, что будет делать преобразование.
Резюме, если вы туда и обратно java.util.Date
к LocalDateTime
и обратно к java.util.Date
у вас может быть другое мгновение из-за перехода на летнее время.
Дополнительная информация: есть еще одно отличие, которое повлияет на очень старые даты. java.util.Date
использует календарь с вероятностью 15 октября 1582 года, а даты до этого используют юлианский календарь вместо григорианского. В отличие от java.time.*
использует систему календаря ISO (эквивалент григорианского) для всех времен. В большинстве случаев система календаря ISO - это то, что вам нужно, но вы можете увидеть странные эффекты при сравнении дат до 1582 года.
Вот то, что я придумал (и, как и все загадки Date Time, это, вероятно, будет опровергнуто на основе некоторой странной корректировки timezone-leapyear-daylight: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
Гораздо более удобный способ, если вы уверены, что вам нужен часовой пояс по умолчанию:
Date d = java.sql.Timestamp.valueOf( myLocalDateTime );
Самый быстрый способ для LocalDateTime
-> Date
является:
Date.from(ldt.toInstant(ZoneOffset.UTC))
Кажется, что следующее работает при преобразовании из нового API LocalDateTime в java.util.date:
Date.from(ZonedDateTime.of({time as LocalDateTime}, ZoneId.systemDefault()).toInstant());
обратное преобразование может быть (надеюсь) достигнуто аналогичным способом...
Надеюсь, поможет...
Все здесь: http://blog.progs.be/542/date-to-java-time
Ответ с "круговым отключением" не является точным: когда вы делаете
LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneOffset.UTC);
если ваш системный часовой пояс не UTC/GMT, вы меняете время!
Если вы на Android и используете Threetenbp вы можете использовать DateTimeUtils
вместо.
например:
Date date = DateTimeUtils.toDate(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
ты не можешь использовать Date.from
так как он поддерживается только на API 26+
Я не уверен, если это самый простой или лучший способ, или есть какие-либо подводные камни, но это работает:
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();
}
Я думаю, что приведенный ниже подход решит преобразование без учета часового пояса. Прокомментируйте, если есть подводные камни.
LocalDateTime datetime //input
public static final DateTimeFormatter yyyyMMddHHmmss_DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formatDateTime = datetime.format(yyyyMMddHHmmss_DATE_FORMAT);
Date outputDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(formatDateTime); //output