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 .