Преобразование между 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
Другие вопросы по тегам