Преобразовать java.util.Date в какой тип "java.time"?

У меня есть объект java.util.Date или объект java.util.Calendar. Как преобразовать это в нужный тип в java.time framework?

Я слышал, что теперь мы должны делать основную часть нашей бизнес-логики с помощью типов java.time. При работе со старым кодом, еще не обновленным для java.time, мне нужно иметь возможность конвертировать туда и обратно. Какие типы отображаются в java.util.Date или java.util.Calendar?

Ответ 1

Да, вам определенно следует использовать фреймворк java.time, когда это возможно.

Избегайте старых классов даты и времени

Старые классы даты и времени, включая java.util.Date, java.util.Calendar и java.text.SimpleTextFormat и другие, оказались плохо спроектированными, запутанными и хлопотными. Избегайте их, где можете. Но когда вы должны взаимодействовать с этими старыми типами, вы можете конвертировать между старым и новым.

Продолжайте читать для базового введения, несколько упрощенного, чтобы ориентировать вас в перемещении назад и вперед между старым и новым классами даты и времени.

java.time

Инфраструктура java.time определяется JSR 310, вдохновлена чрезвычайно успешной библиотекой Joda-Time и расширена проектом ThreeTen-Extra. Основная часть функциональности была перенесена на Java 6 & 7 в проекте ThreeTen-Backport с дальнейшей адаптацией для Android в проекте ThreeTenABP.

Какой тип java.time соответствует java.util.Date? Ну, объект java.util.Date основном представляет момент на временной шкале в UTC, комбинацию даты и времени суток. Мы можем перевести это на любой из нескольких типов в java.time. Каждый обсуждается ниже. Обратите внимание, что некоторые старые методы были добавлены к старым классам даты и времени, чтобы облегчить преобразования.

diagram of converting from java.util.Date/.Calendar through ZonedDateTime (or OffsetDateTime) to the three <code>Local…</code> types

Instant

Строительный блок в java.time является Instant, моментом на шкале времени в UTC с разрешением наносекунд.

Как правило, вы должны делать большую часть своей бизнес-логики в UTC. В такой работе Instant будет использоваться часто. Обходите Instant объекты, применяя часовой пояс только для презентации пользователю. Если вам нужно применить смещение или часовой пояс, используйте типы, описанные ниже.

С java.util.Date для Instant

Учитывая, что Instant и java.util.Date находятся на временной шкале в UTC, мы можем легко перейти от java.util.Date к Instant. Старый класс получил новый метод, java.util.Date::toInstant.

Instant instant = myUtilDate.toInstant();

Вы можете пойти в другом направлении, от Instant до java.util.Date. Но вы можете потерять информацию о доли секунды. Instant отслеживание наносекунд, до девяти цифр после десятичного знака, например 2016-01-23T12:34:56.123456789Z. И java.util.Date &.Calendar ограничены миллисекундами, до трех цифр после десятичного знака, например 2016-01-23T12:34:56.123Z. В этом примере переход от Instant к Date означает усечение 456789.

java.util.Date myUtilDate = java.util.Date.from(instant);

Из java.util.Calendar в Instant

Как насчет java.util.Calendar вместо java.util.Date? Внутри объекта Calendar дата-время отслеживается в виде количества миллисекунд от эталонной даты-времени эпохи первого момента 1970 года в UTC (1970-01-01T00:00:00.0Z). Таким образом, это значение может быть легко преобразовано в Instant.

Instant instant = myUtilCalendar.toInstant() ;

От java.util.GregorianCalendar до ZonedDateTime

Еще лучше, если ваш объект java.util.Calendar самом деле является java.util.GregorianCalendar вы можете легко перейти непосредственно к ZonedDateTime. Этот подход имеет преимущество сохранения встроенной информации о часовом поясе.

Переход от интерфейса Calendar к конкретному классу GregorianCalendar. Затем вызовите toZonedDateTime и from методов идти вперед и назад.

if (myUtilCalendar instanceof GregorianCalendar) {
    GregorianCalendar gregCal = (GregorianCalendar) myUtilCalendar; // Downcasting from the interface to the concrete class.
    ZonedDateTime zdt = gregCal.toZonedDateTime();  // Create 'ZonedDateTime' with same time zone info found in the 'GregorianCalendar'
}

Идти в другом направлении...

java.util.Calendar myUtilCalendar = java.util.GregorianCalendar.from(zdt); // Produces an instant of 'GregorianCalendar' which implements 'Calendar' interface.

Как обсуждалось выше, будьте осторожны, что вы можете потерять информацию о доле секунды. В наносекунд в типе java.time(ZonedDateTime) получает усекается до миллисекунд в .Calendar/.GregorianCalendar.

OffsetDateTime

С Instant момента мы можем применить смещение от UTC, чтобы перейти во время настенных часов для некоторой местности. Смещение - это количество часов и, возможно, минут и секунд, перед UTC (на восток) или позади UTC (на запад). Класс ZoneOffset представляет эту идею. Результатом является объект OffsetDateTime.

ZoneOffset offset = ZoneOffset.of("-04:00"); 
OffsetDateTime odt = OffsetDateTime.ofInstant(instant, zoneOffset);

Вы можете пойти в другом направлении, от OffsetDateTime до java.util.Date. Извлеките Instant а затем продолжайте, как мы видели в коде выше. Как обсуждалось выше, любые наносекунды усекаются до миллисекунд (потеря данных).

java.util.Date myUtilDate = java.util.Date.from(odt.toInstant());

ZonedDateTime

Еще лучше применить полный часовой пояс. Часовой пояс - это смещение плюс правила для обработки аномалий, таких как переход на летнее время (DST).

Применение ZoneId дает вам объект ZonedDateTime. Используйте правильное название часового пояса (континент/регион). Никогда не используйте 3-4 буквенные сокращения, такие как EST или IST поскольку они не являются ни стандартизированными, ни уникальными.

ZoneId zoneId = ZoneId.of("America/Montreal");
ZonedDateTime zdt = ZonedDateTime.ofInstant(instant, zoneId);

Вы можете пойти в другом направлении, от ZonedDateTime до java.util.Date. Извлеките Instant а затем продолжайте, как мы видели в коде выше. Как обсуждалось выше, любые наносекунды усекаются до миллисекунд (потеря данных).

java.util.Date myUtilDate = java.util.Date.from( zdt.toInstant() );

И выше мы увидели, что ZonedDateTime может быть преобразован в GregorianCalendar.

LocalDate

Иногда вам может понадобиться значение только для даты, без времени суток и без часового пояса. Для этого используйте объект java.time.LocalDate.

См. Этот вопрос для дальнейшего обсуждения. Преобразуйте java.util.Date в java.time.LocalDate, особенно этот ответ, написанный главным человеком, который придумал изобретение Joda-Time и java.time.

Ключ должен пройти через ZonedDateTime (как сгенерировано в коде выше). Нам нужен часовой пояс, чтобы определить дату. Дата меняется по всему миру, с новым днем на востоке. Например, после полуночи в Париже новый день, а в Монреале все еще "вчера". Таким образом, хотя LocalDate не содержит часовой пояс, часовой пояс необходим для определения LocalDate.

LocalDate localDate = zdt.toLocalDate();

Преобразование в LocalDate направлении от LocalDate к дате-времени означает создание времени суток. Вы можете выбрать любое время суток, которое имеет смысл в вашем бизнес-сценарии. Для большинства людей первый момент дня имеет смысл. Вы можете испытать желание записать жесткий код в этот первый момент времени 00:00:00.0. В некоторых часовых поясах это время может быть недействительным в качестве первого момента из-за перехода на летнее время (DST) или других аномалий. Поэтому позвольте java.time определить правильное время с помощью вызова atStartOfDay.

ZonedDateTime zdt = localDate.atStartOfDay(zoneId);

LocalTime

В редких случаях вам может потребоваться только время суток без даты и без часового пояса. Эта концепция представлена классом LocalTime. Как обсуждалось выше с LocalDate, нам нужен часовой пояс для определения LocalTime даже если объект LocalTime не содержит (не запоминает) этот часовой пояс. Итак, снова мы ZonedDateTime объект ZonedDateTime полученный из Instant как показано выше.

LocalTime localTime = zdt.toLocalTime();

LocalDateTime

Как и в случае с двумя другими типами Local…, LocalDateTime имеет ни часового пояса, ни смещения. Таким образом, вы можете использовать это редко. Это дает вам приблизительное представление о дате и времени, но не является точкой на временной шкале. Используйте это, если вы имеете в виду некоторую общую дату и время, которое может быть применено к часовому поясу.

Например, "Рождество начинается в этом году" будет 2016-12-25T00:00:00.0. Обратите внимание на отсутствие какого-либо смещения или часового пояса в этом текстовом представлении LocalDateTime. Рождество в Дели, Индия, начинается раньше, чем в Париже, Франция, а позже - в Монреале, Квебеке, Канада. Применение каждого из этих областей часового пояса приведет к другому моменту на временной шкале.

LocalDateTime ldt = zdt.toLocalDateTime();

enter image description here