Наконец-то в Java появился интуитивный, надежный метод работы с датами и временем.
Принципы даты и времени являются фундаментальными во многих приложениях. Такие разные вещи как даты рождения, сроки аренды, время событий и часы открытия магазина, все основаны на датах и времени, но Java SE не предоставляла удобного способа работы с ними. Начиная с Java SE 8, появился набор пакетов java.time - который предоставляет хорошо структурированный API для работы с датами и временем.
Вот небольшой список имеющихся проблем в его реализации:
Предыстория
Когда Java впервые появилась, в версии 1.0, единственным классом для работы с датами и временем был java.util.Date. Первым на что обратили внимание разработчики было то, что он не представляет собой «дату». На самом деле он представляет собой момент времени с точностью до миллисекунд, отмеренный с даты 1-го января 1970-го года. Однако, на основании того что метод toString() у Date выводит дату и время в том часовом поясе который указан в настройках java машины, некоторые разработчики ошибочно сделали вывод о том что Date умеет работать с часовыми поясами. Исправить этот класс оказалось настолько сложно (или настолько лениво) что в версии 1.1 пришлось добавить новый класс - java.util.Calendar. К сожалению, класс Calendar оказался не сильно лучше чем Date.- Изменяемый. Такие классы как дата и время должны быть неизменяемыми.
- Смещения. Года в Date начинаются с 1900, месяца в обоих классах начинаются с нуля.
- Наименования. Date это на самом деле не «дата», и Calendar не является календарем.
- Форматирование. Форматирование работает только с Date, а не с Calendar, и не является потокобезопасным.
Обзор
Новый API java.time содержит 5 пакетов:- java.time - базовый пакет, содержащий объекты для хранения значений
- java.time.chrono - предоставляет доступ к разным календарям
- java.time.format - форматирование и распознание даты и времени
- java.time.temporal - низкоуровневые библиотеки и расширенный функционал
- java.time.zone - классы для работы с часовыми поясами
Даты
Класс LocalDate - один из самых главных в новом API. Он содержит неизменяемое значение, представляющее собой дату. Задать время или часовой пояс нельзя. Название «local» вам может быть знакомо из Joda-Time, и изначально происходит из стандарта ISO-8601. Оно обозначает именно отсутствие часового пояса. В сущности, LocalDate это описание даты, такое как «5-е апреля 2014». Фактическое же время этой даты будет отличаться, в зависимости от вашего часового пояса. К примеру, в Австралии эта дата будет на 10 часов раньше чем в Лондоне, и на 18 часов раньше чем в Сан Франциско. В классе LocalDate есть все обычно нужные методы:LocalDate date = LocalDate.of(2014, Month.JUNE, 10);
int year = date.getYear(); // 2014
Month month = date.getMonth(); // Июнь
int dom = date.getDayOfMonth(); // 10
DayOfWeek dow = date.getDayOfWeek(); // Вторник
int len = date.lengthOfMonth(); // 30 (дней в Июне)
boolean leap = date.isLeapYear(); // false (не високосный год)
В нашем примере, мы видим дату созданную с помощью метода-фабрики (все конструкторы приватные). Дальше мы запрашиваем у объекта некоторые данные. Обратите внимание, что перечисления Month и DayOfWeek созданы для того чтобы делать код более читаемым и надежным.
В следующем примере мы увидим как модифицировать дату. Так как класс неизменяемый, результатом будут новые объекты, а исходный останется как был.
LocalDate date = LocalDate.of(2014, Month.JUNE, 10);
date = date.withYear(2015); // 2015-06-10
date = date.plusMonths(2); // 2015-08-10
date = date.minusDays(1); // 2015-08-09
Это относительно простые изменения, но часто вам нужно произвести более сложные модификации даты. Для этого существует специальный механизм в java.time API - TemporalAdjuster. Его цель - предоставить встроенный инструмент позволяющий манипулировать датами, к примеру получить объект соответствующий последнему дню месяца. Некоторые из них входят в состав API, но вы можете добавлять и свои собственные. Использовать модификаторы очень просто, но для этого нужны статические импорты:
import static java.time.DayOfWeek.*
import static java.time.temporal.TemporalAdjusters.*
LocalDate date = LocalDate.of(2014, Month.JUNE, 10);
date = date.with(lastDayOfMonth());
date = date.with(nextOrSame(WEDNESDAY));
Использование модификаторов очень сильно упрощает ваш код. Никому не хочется видеть большое количество манипуляций с датой вручную. Если какая-то манипуляция с датой встречается в вашем проекте несколько раз, напишите ваш собственный модификатор, и ваша команда сможет воспользоваться им как уже написанным и протестированным компонентом.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ