Ответ 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. Каждый обсуждается ниже. Обратите внимание, что некоторые старые методы были добавлены к старым классам даты и времени, чтобы облегчить преобразования.
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();