Рубрики
Без рубрики

Переход на новый API даты и времени Java 8

Краткое и практическое руководство по переходу на новый API DateTime Java 8.

Автор оригинала: baeldung.

1. Обзор

В этом уроке вы узнаете, как рефакторинговать свой код, чтобы использовать новый API даты и времени, представленный в Java 8.

2. Новый API с первого взгляда

Работа с датами в Java раньше была трудной. Старая библиотека даты, предоставленная JDK, включала только три класса: java.util.Дата, java.util.Календарь и java.util.Часовой пояс .

Они подходили только для самых простых задач. Для чего-либо даже отдаленно сложного разработчикам приходилось либо использовать сторонние библиотеки, либо писать тонны пользовательского кода.

Java 8 представила совершенно новый API даты и времени ( java.util.time.* ), который свободно основан на популярной библиотеке Java под названием JodaTime. Этот новый API значительно упростил обработку даты и времени и исправил многие недостатки старой библиотеки дат.

1.1. Ясность API

Первым преимуществом нового API является ясность – API очень понятен, лаконичен и прост для понимания. В нем не так много несоответствий, найденных в старой библиотеке, таких как нумерация полей (в календарных месяцах они основаны на нуле, а в днях недели-на единице).

1.2. Гибкость API

Еще одним преимуществом является гибкость – работа с несколькими представлениями времени . Старая библиотека дат включала только один класс представления времени – java.util.Дата , которая, несмотря на свое название, на самом деле является меткой времени. Он хранит только количество миллисекунд, прошедших с эпохи Unix.

Новый API имеет множество различных представлений времени, каждое из которых подходит для разных случаев использования:

  • Instant – представляет момент времени (метка времени)
  • LocalDate – представляет дату (год, месяц, день)
  • LocalDateTime – то же , что и LocalDate , но включает время с точностью до наносекунды
  • OffsetDateTime – то же , что LocalDateTime , но со смещением часового пояса
  • LocalTime – время с точностью до наносекунды и без информации о дате
  • ZonedDateTime – то же , что и OffsetDateTime , но включает идентификатор часового пояса
  • OffsetLocalTime – то же самое , что LocalTime , но со смещением часового пояса
  • MonthDay – месяц и день, без года и времени
  • YearMonth – месяц и год, без дня и времени
  • Продолжительность – количество времени, представленное в секундах, минутах и часах. Имеет наносекундную точность
  • Период – количество времени, представленное в днях, месяцах и годах

1.3. Неизменность и безопасность резьбы

Еще одним преимуществом является то, что все представления времени в Java 8 Date Time API являются неизменяемыми и, следовательно, потокобезопасными.

Все мутирующие методы возвращают новую копию вместо изменения состояния исходного объекта.

Старые классы, такие как java.util.Date не были потокобезопасными и могли привести к очень тонким ошибкам параллелизма.

1.4. Цепочка методов

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

ZonedDateTime nextFriday = LocalDateTime.now()
  .plusHours(1)
  .with(TemporalAdjusters.next(DayOfWeek.FRIDAY))
  .atZone(ZoneId.of("PST"));

2. Примеры

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

Получение текущего времени

// Old
Date now = new Date();

// New
ZonedDateTime now = ZonedDateTime.now();

Представление определенного времени

// Old
Date birthDay = new GregorianCalendar(1990, Calendar.DECEMBER, 15).getTime();

// New
LocalDate birthDay = LocalDate.of(1990, Month.DECEMBER, 15);

Извлечение определенных полей

// Old
int month = new GregorianCalendar().get(Calendar.MONTH);

// New
Month month = LocalDateTime.now().getMonth();

Сложение и вычитание времени

// Old
GregorianCalendar calendar = new GregorianCalendar();
calendar.add(Calendar.HOUR_OF_DAY, -5);
Date fiveHoursBefore = calendar.getTime();

// New
LocalDateTime fiveHoursBefore = LocalDateTime.now().minusHours(5);

Изменение определенных полей

// Old
GregorianCalendar calendar = new GregorianCalendar();
calendar.set(Calendar.MONTH, Calendar.JUNE);
Date inJune = calendar.getTime();

// New
LocalDateTime inJune = LocalDateTime.now().withMonth(Month.JUNE.getValue());

Усечение

Усечение сбрасывает все поля времени, меньшие указанного поля. В приведенном ниже примере минуты и все, что ниже, будут равны нулю

// Old
Calendar now = Calendar.getInstance();
now.set(Calendar.MINUTE, 0);
now.set(Calendar.SECOND, 0);
now.set(Calendar.MILLISECOND, 0);
Date truncated = now.getTime();

// New
LocalTime truncated = LocalTime.now().truncatedTo(ChronoUnit.HOURS);

Преобразование часовых поясов

// Old
GregorianCalendar calendar = new GregorianCalendar();
calendar.setTimeZone(TimeZone.getTimeZone("CET"));
Date centralEastern = calendar.getTime();

// New
ZonedDateTime centralEastern = LocalDateTime.now().atZone(ZoneId.of("CET"));

Получение промежутка времени между двумя точками во времени

// Old
GregorianCalendar calendar = new GregorianCalendar();
Date now = new Date();
calendar.add(Calendar.HOUR, 1);
Date hourLater = calendar.getTime();
long elapsed = hourLater.getTime() - now.getTime();

// New
LocalDateTime now = LocalDateTime.now();
LocalDateTime hourLater = LocalDateTime.now().plusHours(1);
Duration span = Duration.between(now, hourLater);

Форматирование и анализ времени

DateTimeFormatter-это замена старого SimpleDateFormat, который является потокобезопасным и обеспечивает дополнительную функциональность.

// Old
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
Date now = new Date();
String formattedDate = dateFormat.format(now);
Date parsedDate = dateFormat.parse(formattedDate);

// New
LocalDate now = LocalDate.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
String formattedDate = now.format(formatter);
LocalDate parsedDate = LocalDate.parse(formattedDate, formatter);

Количество дней в месяце

// Old
Calendar calendar = new GregorianCalendar(1990, Calendar.FEBRUARY, 20);
int daysInMonth = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);

// New
int daysInMonth = YearMonth.of(1990, 2).lengthOfMonth();

3. Взаимодействие С Устаревшим Кодом

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

В Java 8 старые классы библиотеки дат были расширены методами, которые преобразуют их в соответствующие объекты из нового API дат.
Новые классы предоставляют аналогичные функциональные возможности.

Instant instantFromCalendar = GregorianCalendar.getInstance().toInstant();
ZonedDateTime zonedDateTimeFromCalendar = new GregorianCalendar().toZonedDateTime();
Date dateFromInstant = Date.from(Instant.now());
GregorianCalendar calendarFromZonedDateTime = GregorianCalendar.from(ZonedDateTime.now());
Instant instantFromDate = new Date().toInstant();
ZoneId zoneIdFromTimeZone = TimeZone.getTimeZone("PST").toZoneId();

4. Заключение

В этой статье мы рассмотрели новый API даты и времени, доступный в Java 8. Мы рассмотрели его преимущества по сравнению с устаревшим API и указали на различия, используя несколько примеров.

Обратите внимание, что мы едва поцарапали поверхность возможностей нового API даты и времени. Обязательно ознакомьтесь с официальной документацией, чтобы ознакомиться с полным спектром инструментов, предлагаемых новым API.

Примеры кода можно найти в проекте GitHub .