1. Обзор
Java 8 представила новые API для Даты и времени для устранения недостатков более старых java.util.Дата и java.util.Календарь .
В рамках этой статьи давайте начнем с проблем в существующих Date и Calendar API и обсудим, как новые API Java 8 Date и Time решают их.
Мы также рассмотрим некоторые основные классы нового проекта Java 8, которые являются частью пакета java.time , например LocalDate , LocalDateTime, LocalDateTime, ZonedDateTime, Period, Duration и их поддерживаемые API.
Дальнейшее чтение:
Работа с параметрами даты весной
Проверьте, является ли строка Допустимой датой в Java
2. Проблемы с существующими API-интерфейсами даты и времени
- Потокобезопасность – Классы Дата и Календарь не являются потокобезопасными, оставляя разработчикам решать головную боль, связанную с трудными проблемами отладки параллелизма и написанием дополнительного кода для обработки потокобезопасности. Напротив, новые API Date и Time , представленные в Java 8, являются неизменяемыми и потокобезопасными, что избавляет разработчиков от головной боли параллелизма.
- Дизайн API и простота понимания – API Дата и Календарь плохо разработаны с неадекватными методами для выполнения повседневных операций. Новый API Date/Time является изоцентрическим и следует согласованным моделям домена для даты, времени, продолжительности и периодов. Существует большое разнообразие полезных методов, которые поддерживают самые распространенные операции.
- ZonedDate и Time – Разработчикам пришлось написать дополнительную логику для обработки логики часового пояса со старыми API, в то время как с новыми API обработка часового пояса может выполняться с помощью Local и ZonedDate /| Time API.
3. Использование LocalDate, LocalTime и LocalDateTime
Наиболее часто используемыми классами являются LocalDate , LocalTime и LocalDateTime . Как следует из их названий, они представляют локальную дату/время из контекста наблюдателя.
Эти классы в основном используются, когда часовой пояс не требуется явно указывать в контексте. В рамках этого раздела мы рассмотрим наиболее часто используемые API.
3.1. Работа С LocalDate
Локальная дата представляет дату в формате ISO (гггг-ММ-дд) без времени .
Его можно использовать для хранения дат, таких как дни рождения и дни выплаты жалованья.
Экземпляр текущей даты может быть создан из системных часов, как показано ниже:
LocalDate localDate = LocalDate.now();
Локальные данные , представляющие конкретный день, месяц и год, могут быть получены с помощью метода ” of “или с помощью метода” parse “. Например, приведенные ниже фрагменты кода представляют Локальные данные за 20 февраля 2015 года:
LocalDate.of(2015, 02, 20); LocalDate.parse("2015-02-20");
Local Date предоставляет различные служебные методы для получения разнообразной информации. Давайте быстро взглянем на некоторые из этих методов API.
Следующий фрагмент кода получает текущую локальную дату и добавляет один день:
LocalDate tomorrow = LocalDate.now().plusDays(1);
В этом примере получается текущая дата и вычитается один месяц. Обратите внимание, как он принимает перечисление в качестве единицы времени:
LocalDate previousMonthSameDay = LocalDate.now().minus(1, ChronoUnit.MONTHS);
В следующих двух примерах кода мы анализируем дату “2016-06-12” и получаем день недели и день месяца соответственно. Обратите внимание на возвращаемые значения, первое-это объект, представляющий DayOfWeek , а второе-в int , представляющее порядковое значение месяца:
DayOfWeek sunday = LocalDate.parse("2016-06-12").getDayOfWeek(); int twelve = LocalDate.parse("2016-06-12").getDayOfMonth();
Мы можем проверить, происходит ли дата в високосный год. В этом примере мы проверяем, является ли текущая дата високосным годом:
boolean leapYear = LocalDate.now().isLeapYear();
Связь одной даты с другой может быть определена до или после другой даты:
boolean notBefore = LocalDate.parse("2016-06-12") .isBefore(LocalDate.parse("2016-06-11")); boolean isAfter = LocalDate.parse("2016-06-12") .isAfter(LocalDate.parse("2016-06-11"));
Границы дат могут быть получены с заданной даты. В следующих двух примерах мы получаем LocalDateTime , представляющий начало дня (2016-06-12T00:00) данной даты, и LocalDate , представляющий начало месяца (2016-06-01) соответственно:
LocalDateTime beginningOfDay = LocalDate.parse("2016-06-12").atStartOfDay(); LocalDate firstDayOfMonth = LocalDate.parse("2016-06-12") .with(TemporalAdjusters.firstDayOfMonth());
Теперь давайте посмотрим, как мы работаем с местным временем.
3.2. Работа С Местным Временем
Местное время представляет время без даты .
Аналогично Local Data экземпляр LocalTime может быть создан из системных часов или с помощью методов “parse” и “of”. Быстрый взгляд на некоторые из часто используемых API ниже.
Экземпляр текущего Местного времени может быть создан из системных часов, как показано ниже:
LocalTime now = LocalTime.now();
В приведенном ниже примере кода , мы создаем Местное время , представляющее 06:30 утра, путем анализа строкового представления:
LocalTime sixThirty = LocalTime.parse("06:30");
Фабричный метод “of” можно использовать для создания Местного времени . Например, приведенный ниже код создает Местное время , представляющее 06:30 утра, используя заводской метод:
LocalTime sixThirty = LocalTime.of(6, 30);
В приведенном ниже примере создается Местное время путем анализа строки и добавляется к ней час с помощью API “плюс”. Результатом будет Местное время , представляющее 07:30 утра:
LocalTime sevenThirty = LocalTime.parse("06:30").plus(1, ChronoUnit.HOURS);
Доступны различные методы получения, которые можно использовать для получения определенных единиц времени, таких как час, минута и сек, как показано ниже:
int six = LocalTime.parse("06:30").getHour();
Мы также можем проверить, является ли определенное время до или после другого определенного времени. Приведенный ниже пример кода сравнивает два Местное время , для которых результат будет истинным:
boolean isbefore = LocalTime.parse("06:30").isBefore(LocalTime.parse("07:30"));
Максимальное, минимальное и полуденное время суток можно получить с помощью констант в классе LocalTime . Это очень полезно при выполнении запросов к базе данных для поиска записей в течение заданного промежутка времени. Например, приведенный ниже код представляет 23:59:59.99:
LocalTime maxTime = LocalTime.MAX
Теперь давайте погрузимся в LocalDateTime .
3.3. Работа С LocalDateTime
LocalDateTime используется для представления комбинации даты и времени .
Это наиболее часто используемый класс, когда нам нужна комбинация даты и времени. Класс предлагает множество API, и мы рассмотрим некоторые из наиболее часто используемых.
Экземпляр LocalDateTime может быть получен из системных часов, аналогичных LocalDate и LocalTime:
LocalDateTime.now();
В приведенных ниже примерах кода объясняется, как создать экземпляр с помощью методов factory “of” и “parse”. Результатом будет экземпляр LocalDateTime , представляющий 20 февраля 2015 года, 06:30 утра:
LocalDateTime.of(2015, Month.FEBRUARY, 20, 06, 30);
LocalDateTime.parse("2015-02-20T06:30:00");
Существуют служебные API для поддержки сложения и вычитания определенных единиц времени, таких как дни, месяцы, год и минуты. Приведенные ниже примеры кода демонстрируют использование методов “плюс” и “минус”. Эти API ведут себя точно так же, как их аналоги в LocalDate и LocalTime:
localDateTime.plusDays(1);
localDateTime.minusHours(2);
Методы геттера доступны для извлечения определенных единиц измерения, аналогичных классам даты и времени. Учитывая приведенный выше экземпляр LocalDateTime , приведенный ниже пример кода вернет месяц февраль:
localDateTime.getMonth();
4. Использование API ZonedDateTime
Java 8 предоставляет ZonedDateTime , когда нам нужно иметь дело с конкретной датой и временем часового пояса. ZoneId – это идентификатор, используемый для представления различных зон. Существует около 40 различных часовых поясов, и ZoneId используется для их представления следующим образом.
В этом фрагменте кода мы создаем Зону для Парижа:
ZoneId zoneId = ZoneId.of("Europe/Paris");
Набор всех идентификаторов зон можно получить следующим образом:
SetallZoneIds = ZoneId.getAvailableZoneIds();
LocalDateTime может быть преобразован в определенную зону:
ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, zoneId);
Метод ZonedDateTime предоставляет метод parse для получения конкретной даты часового пояса:
ZonedDateTime.parse("2015-05-03T10:15:30+01:00[Europe/Paris]");
Другой способ работы с часовым поясом-использовать OffsetDateTime . OffsetDateTime – это неизменяемое представление даты-времени со смещением. Этот класс хранит все поля даты и времени с точностью до наносекунд, а также смещение от UTC/Гринвича.
Экземпляр OffsetDateTime можно создать, как показано ниже, с помощью Zone Offset . Здесь мы создаем LocalDateTime , представляющий 6:30 утра 20 февраля 2015 года:
LocalDateTime localDateTime = LocalDateTime.of(2015, Month.FEBRUARY, 20, 06, 30);
Затем мы добавим два часа к этому времени, создав смещение зоны и установив для экземпляра LocalDateTime :
ZoneOffset offset = ZoneOffset.of("+02:00"); OffsetDateTime offSetByTwo = OffsetDateTime .of(localDateTime, offset);
Теперь у нас есть LocalDateTime 2015-02-20 06:30 +02:00. Теперь давайте перейдем к тому, как изменить значения даты и времени с помощью классов Period и Duration .
5. Использование периода и продолжительности
Класс Period представляет количество времени в годах, месяцах и днях, а класс Duration представляет количество времени в секундах и наносекундах.
5.1. Работа С Периодом
Класс Period широко используется для изменения значений заданной даты или для получения разницы между двумя датами:
LocalDate initialDate = LocalDate.parse("2007-05-10");
Датой можно управлять с помощью Периода , как показано в следующем фрагменте кода:
LocalDate finalDate = initialDate.plus(Period.ofDays(5));
Класс Period имеет различные методы получения, такие как getyear, getmonth и getday для получения значений из объекта Period . Приведенный ниже пример кода возвращает int значение 5, поскольку мы пытаемся получить разницу в днях :
int five = Period.between(initialDate, finalDate).getDays();
Период между двумя датами может быть получен в определенной единице, такой как дни, месяц или годы, с помощью ChronoUnit.between:
long five = ChronoUnit.DAYS.between(initialDate, finalDate);
Этот пример кода возвращает пять дней. Давайте продолжим, взглянув на класс Duration .
5.2. Работа С Длительностью
Подобно Period, класс Duration используется для работы с Time. В следующем коде мы создаем Местное время 6:30 утра, а затем добавляем продолжительность 30 секунд, чтобы сделать Местное время 06:30:30 утра:
LocalTime initialTime = LocalTime.of(6, 30, 0); LocalTime finalTime = initialTime.plus(Duration.ofSeconds(30));
Длительность между двумя моментами может быть получена либо как Длительность , либо как определенная единица измерения. В первом фрагменте кода мы используем метод between() класса Duration , чтобы найти разницу во времени между final Time и initialTime и вернуть разницу в секундах:
long thirty = Duration.between(initialTime, finalTime).getSeconds();
Во втором примере мы используем метод between() класса ChronoUnit для выполнения той же операции:
long thirty = ChronoUnit.SECONDS.between(initialTime, finalTime);
Теперь мы рассмотрим, как преобразовать существующую Дату и Календарь в новую Дату Время.
6. Совместимость с датой и календарем
Java 8 добавила их в метод Instant () , который помогает преобразовать существующие Данные и Календарь экземпляр в новый API даты и времени, как показано в следующем фрагменте кода:
LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()); LocalDateTime.ofInstant(calendar.toInstant(), ZoneId.systemDefault());
LocalDateTime может быть построен из секунд эпохи, как показано ниже. Результатом приведенного ниже кода будет LocalDateTime , представляющий 2016-06-13T11:34:50:
LocalDateTime.ofEpochSecond(1465817690, 0, ZoneOffset.UTC);
Теперь перейдем к Дате и времени форматированию.
7. Форматирование даты и времени
Java 8 предоставляет API для удобного форматирования Даты и времени :
LocalDateTime localDateTime = LocalDateTime.of(2015, Month.JANUARY, 25, 6, 30);
Приведенный ниже код передает формат даты ISO для форматирования локальной даты. Результат будет 2015-01-25:
String localDateString = localDateTime.format(DateTimeFormatter.ISO_DATE);
DateTimeFormatter предоставляет различные стандартные параметры форматирования. Пользовательские шаблоны также могут быть предоставлены для метода форматирования, как показано ниже, который вернет Локальную дату как 2015/01/25:
localDateTime.format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
Мы можем передать стиль форматирования как SHORT , LONG или MEDIUM как часть опции форматирования. Приведенный ниже пример кода даст вывод, представляющий LocalDateTime в 25-Jan-2015, 06:30:00:
localDateTime .format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)) .withLocale(Locale.UK);
Давайте рассмотрим альтернативы, доступные для Java 8 Core Date |/Time |/API.
8. Backport и альтернативные варианты
8.1. Использование проектной информации
Для организаций, которые находятся на пути перехода на Java 8 с Java 7 или Java 6 и хотят использовать API даты и времени, project threeten предоставляет возможность обратного порта. Разработчики могут использовать классы, доступные в этом проекте, для достижения той же функциональности, что и в новом Java 8 Date и Time API, и как только они перейдут на Java 8, пакеты могут быть переключены. Артефакт для проекта можно найти в центральном репозитории maven :
org.threeten threetenbp 1.3.1
8.2. Библиотека Joda-Time
Другой альтернативой для Java 8 Date и Time library является Joda-Time library. На самом деле Java 8 Date Time API был совместно разработан автором библиотеки Joda-Time (Стивен Коулборн) и Oracle. Эта библиотека предоставляет практически все возможности, которые поддерживаются в Java 8 Date Time project. Артефакт можно найти в maven central , включив в свой проект приведенную ниже зависимость pom:
joda-time joda-time 2.9.4
9. Заключение
Java 8 предоставляет богатый набор API с согласованным дизайном API для облегчения разработки.
Примеры кода для приведенной выше статьи можно найти в репозитории Java 8 Date/Time git.