it-swarm.com.ru

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

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

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

431
Knut Arne Vedaa

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

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 года.

656
JodaStephen

Вот то, что я придумал (и, как и все загадки 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
125
The Coordinator

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

Date d = Java.sql.Timestamp.valueOf( myLocalDateTime );
19
Petrychenko

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

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

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

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

7
dizzy

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

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

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

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

3
joe-mojo

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

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();
}
3
Knut Arne Vedaa

Если вы используете Android и ​​используете threetenbp , вы можете использовать вместо него DateTimeUtils.

например:

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

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

2
humazed

Самый быстрый способ для LocalDateTime -> Date:

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

1
Lukasz Czerwinski