Первоначально опубликовано в моем блоге по адресу www.codebyamir.com
Класс LocalDateTime
, представленный в Java 8, хранит дату и время, но не часовой пояс. Поэтому нам нужно что-то еще при разработке приложений, которые должны знать о разных часовых поясах.
К счастью, мы можем использовать класс ZonedDateTime
для представления дат и времени с информацией о часовом поясе. Класс содержит статические методы, которые позволяют нам легко выполнять преобразования дат и вычисления.
Объекты ZonedDateTime
неизменяемы, поэтому все статические методы возвращают новый экземпляр.
Получить текущую дату и время в местном часовом поясе
Дата в зоне.сейчас()
ZonedDateTime now = ZonedDateTime.now(); // 2018-05-02T15:45:20.981-04:00[America/New_York]
Получить текущую дату и время в другом часовом поясе
Дата и время зоны.сейчас(идентификатор зоны)
ZonedDateTime nowInParis = ZonedDateTime.now(ZoneId.of("Europe/Paris")); // 2018-05-02T21:45:20.982+02:00[Europe/Paris]
Значения Идентификатора часового пояса
Список значений идентификатора часового пояса хорошо задокументирован здесь .
Мы также можем получить их с помощью метода:
SetzoneIds = ZoneId.getAvailableZoneIds();
Всегда используйте значения часового пояса в формате Регион/Город (Америка/Нью-Йорк) и избегайте коротких сокращений (EST), поскольку они неоднозначны и нестандартны.
Создайте ZonedDateTime из строки
ZonedDateTime.синтаксический анализ(строка даты)
ZonedDateTime departureTime = ZonedDateTime.parse("2018-07-01T10:00:00Z[America/New_York]"); ZonedDateTime arrivalTime = ZonedDateTime.parse("2018-07-01T22:00:00Z[Europe/London]");
Преобразовать локальное время в зональное время
Мы можем преобразовать LocalDateTime в ZonedDateTime несколькими различными способами.
LocalDateTime ldt = LocalDateTime.parse("2018-07-01T08:00"); ZoneId zoneId = ZoneId.of("Europe/Paris");
Дата и время зоны.из(ldt, идентификатор зоны)
ZonedDateTime zdt = ZonedDateTime.of(ldt, zoneId); // 2018-07-01T08:00+02:00[Europe/Paris]
atZone(идентификатор зоны)
ZonedDateTime zdt = ldt.atZone(zoneId); // 2018-07-01T08:00+02:00[Europe/Paris]
Обратите внимание, что это не относится к преобразованию часового пояса в наш LocalDateTime
. Он просто добавляет часовой пояс к любой дате и времени, которые были сохранены в исходном объекте.
Сравнение объектов ZonedDateTime
находится перед(здт)
boolean departureBeforeArrival = departureTime.isBefore(arrivalTime);
находится после(zdt)
boolean arrivalAfterDeparture = arrivalTime.isAfter(departureTime);
Равнозначный(zdt)
При этом сравниваются фактические даты и время, а не ссылки на объекты.
LocalDateTime ldt = LocalDateTime.parse("2018-07-01T08:00"); ZonedDateTime zdtParis = ZonedDateTime.of(ldt, ZoneId.of("Europe/Paris")); // 2018-07-01T08:00+02:00[Europe/Paris] ZonedDateTime zdtNewYork = ZonedDateTime.of(ldt, ZoneId.of("America/New_York")); // 2018-07-01T08:00-04:00[America/New_York] boolean equal = zdtParis.isEqual(zdtNewYork); // false
Найдите разницу во времени между объектами ZonedDateTime
ChronoUnit
в java.time.temporal
позволяет нам вычислять разницу во времени между объектами ZonedDateTime
:
long flightTimeInHours = ChronoUnit.HOURS.between(departureTime, arrivalTime); // 7 long flightTimeInMinutes = ChronoUnit.MINUTES.between(departureTime, arrivalTime); // 420
Создайте ZonedDateTime с новым часовым поясом из существующего объекта ZonedDateTime
Учитывая ZonedDateTime
объект в часовом поясе A, мы можем создать другой ZonedDateTime
объект в часовом поясе B, который представляет один и тот же момент времени.
С постоянными зонами(идентификатор зоны)
ZonedDateTime nowInLocalTimeZone = ZonedDateTime.now(); // 2018-05-02T20:10:27.896-04:00[America/New_York] ZonedDateTime nowInParis = nowInLocalTimeZone.withZoneSameInstant(ZoneId.of("Europe/Paris")); // 2018-05-03T02:10:27.896+02:00[Europe/Paris]
Предположим, нам нужен метод, который определяет, можем ли мы выдать посадочный талон пассажиру авиакомпании на основе некоторой бизнес-логики.
Он должен возвращать значение true, если до времени вылета вылетающего ИЛИ обратного рейса остается менее 24 часов.
Мы могли бы написать что-то вроде этого:
package com.example.demo; import java.time.ZonedDateTime; import java.time.temporal.ChronoUnit; public class BoardingPassService { private static final int MIN_HOUR_DIFF_BOARDING_PASS = -24; public boolean canIssueBoardingPass(Ticket ticket) { if (ticket == null || ticket.getOutboundFlight() == null || ticket.getReturnFlight() == null) { return false; } ZonedDateTime now = ZonedDateTime.now(); ZonedDateTime outboundDepartureTime = ticket.getOutboundFlight().getDepartureTime(); ZonedDateTime returnDepartureTime = ticket.getReturnFlight().getDepartureTime(); long diffInHours = ChronoUnit.HOURS.between(outboundDepartureTime, now); boolean outboundDepartureInAllowableRange = (diffInHours >= MIN_HOUR_DIFF_BOARDING_PASS && diffInHours <= 0); diffInHours = ChronoUnit.HOURS.between(returnDepartureTime, now); boolean returnDepartureInAllowableRange = (diffInHours >= MIN_HOUR_DIFF_BOARDING_PASS && diffInHours <= 0); if (outboundDepartureInAllowableRange || returnDepartureInAllowableRange) { return true; } return false; } }
Это прекрасно работает, но трудно поддается модульному тестированию из-за ZonedDateTime.now()
. Мы не можем издеваться над ним, потому что это статический метод.
java.время. Часы
Новые методы() в java.time
все принимают аргумент Clock
, который мы можем использовать для рефакторинга нашего кода, чтобы его можно было проверить.
Вот тестируемая версия класса:
package com.example.demo; import java.time.Clock; import java.time.ZonedDateTime; import java.time.temporal.ChronoUnit; public class BoardingPassService { private final Clock clock; private static final int MIN_HOUR_DIFF_BOARDING_PASS = -24; public BoardingPassService(Clock clock) { this.clock = clock; } public boolean canIssueBoardingPass(Ticket ticket) { if (ticket == null || ticket.getOutboundFlight() == null || ticket.getReturnFlight() == null) { return false; } ZonedDateTime now = ZonedDateTime.now(clock); ZonedDateTime outboundDepartureTime = ticket.getOutboundFlight().getDepartureTime(); ZonedDateTime returnDepartureTime = ticket.getReturnFlight().getDepartureTime(); long diffInHours = ChronoUnit.HOURS.between(outboundDepartureTime, now); boolean outboundDepartureInAllowableRange = (diffInHours >= MIN_HOUR_DIFF_BOARDING_PASS && diffInHours <= 0); diffInHours = ChronoUnit.HOURS.between(returnDepartureTime, now); boolean returnDepartureInAllowableRange = (diffInHours >= MIN_HOUR_DIFF_BOARDING_PASS && diffInHours <= 0); if (outboundDepartureInAllowableRange || returnDepartureInAllowableRange) { return true; } return false; } }
В строке 12 мы добавили конструктор с одним аргументом, который принимает объект Clock
. Затем мы можем использовать часы на линии 21.
В вызывающем коде мы бы передали Clock.systemDefaultZone()
конструктору, чтобы использовать текущее системное время.
В наших тестах мы создадим часы с фиксированным временем, используя Clock.fixed()
и передадим их.
Модульные тесты
package com.example.demo; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.time.Clock; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.ZonedDateTime; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class BoardingPassServiceTest { private Ticket ticket; private BoardingPassService boardingPassService; private final LocalDateTime REFERENCE_DATE_TIME = LocalDateTime.of(2018, 7, 1, 10, 0); // 2018-07-01 at 10:00am private final ZoneId defaultZone = ZoneId.systemDefault(); private final Clock FIXED_CLOCK = Clock.fixed(REFERENCE_DATE_TIME.atZone(defaultZone).toInstant(), defaultZone); @BeforeEach public void setUp() { ticket = new Ticket(); boardingPassService = new BoardingPassService(FIXED_CLOCK); } @Test public void testCanIssueBoardingPass_Null() { assertFalse(boardingPassService.canIssueBoardingPass(null)); } @Test public void testCanIssueBoardingPass_OutboundFlightWithinTimeRange() { Flight outboundFlight = new Flight(); outboundFlight.setDepartureTime(ZonedDateTime.parse("2018-07-02T07:30:00Z[America/Chicago]")); Flight returnFlight = new Flight(); returnFlight.setDepartureTime(ZonedDateTime.parse("2018-07-09T11:30:00Z[America/New_York]")); ticket.setOutboundFlight(outboundFlight); ticket.setReturnFlight(returnFlight); assertTrue(boardingPassService.canIssueBoardingPass(ticket)); } @Test public void testCanIssueBoardingPass_ReturnFlightWithinTimeRange() { Flight outboundFlight = new Flight(); outboundFlight.setDepartureTime(ZonedDateTime.parse("2018-06-20T07:30:00Z[America/Chicago]")); Flight returnFlight = new Flight(); returnFlight.setDepartureTime(ZonedDateTime.parse("2018-07-02T09:30:00Z[America/New_York]")); ticket.setOutboundFlight(outboundFlight); ticket.setReturnFlight(returnFlight); assertTrue(boardingPassService.canIssueBoardingPass(ticket)); } @Test public void testCanIssueBoardingPass_NoFlightWithinTimeRange() { Flight outboundFlight = new Flight(); outboundFlight.setDepartureTime(ZonedDateTime.parse("2018-09-01T07:30:00Z[America/Chicago]")); Flight returnFlight = new Flight(); returnFlight.setDepartureTime(ZonedDateTime.parse("2018-09-08T11:30:00Z[America/New_York]")); ticket.setOutboundFlight(outboundFlight); ticket.setReturnFlight(returnFlight); assertFalse(boardingPassService.canIssueBoardingPass(ticket)); } }
Оригинал: “https://dev.to/codebyamir/add-a-timezone-to-localdatetime-with-zoneddatetime-in-java-8-608”