Введение в API даты / времени Java 8
1. обзор
Java 8 представила новые API дляDate иTime, чтобы устранить недостатки старыхjava.util.Date иjava.util.Calendar.
В рамках этой статьи давайте начнем с проблем в существующих APIDate иCalendar и обсудим, как новые API Java 8Date иTime решают их.
Мы также рассмотрим некоторые из основных классов нового проекта Java 8, которые являются частью пакетаjava.time, напримерLocalDate,LocalTime, LocalDateTime, ZonedDateTime, Period, Duration и поддерживаемые ими API.
Дальнейшее чтение:
Работа с параметрами даты в Spring
Узнайте, как работать с параметрами Date в Spring MVC
Проверьте, является ли строка действительной датой в Java
Взгляните на разные способы проверить, является ли String действительной датой в Java
2. Проблемы с существующими APIDate /Time
-
Thread Safety - классыDate иCalendar не являются потокобезопасными, поэтому разработчикам приходится иметь дело с головной болью, связанной с трудностями при отладке проблем параллелизма и написанием дополнительного кода для обеспечения безопасности потоков. Напротив, новые APIDate иTime, представленные в Java 8, являются неизменяемыми и потокобезопасными, что избавляет разработчиков от этой головной боли, связанной с параллелизмом.
-
APIs Design and Ease of Understanding - API-интерфейсыDate иCalendar плохо спроектированы с неадекватными методами для выполнения повседневных операций. Новые APIDate/Time ориентированы на ISO и соответствуют согласованным моделям предметной области для даты, времени, продолжительности и периодов. Существует множество различных служебных методов, которые поддерживают самые распространенные операции.
-
ZonedDate and Time - Разработчикам пришлось написать дополнительную логику для обработки логики часового пояса со старыми API, тогда как с новыми API обработка часового пояса может выполняться сLocal иZonedDate /Time API.
3. ИспользуяLocalDate,LocalTime иLocalDateTime
Чаще всего используются классыLocalDate,LocalTime иLocalDateTime. Как показывают их имена, они представляют локальную дату / время из контекста наблюдателя.
Эти классы в основном используются, когда часовой пояс не требуется явно указывать в контексте. В рамках этого раздела мы рассмотрим наиболее часто используемые API.
3.1. Работа сLocalDate
LocalDate представляетa date in ISO format (yyyy-MM-dd) without time.
Он может быть использован для хранения дат, таких как дни рождения и зарплаты.
Экземпляр текущей даты может быть создан из системных часов, как показано ниже:
LocalDate localDate = LocalDate.now();
LocalDate, представляющий определенный день, месяц и год, может быть получен с использованием метода «of» или с помощью метода «parse». Например, приведенные ниже фрагменты кода представляютLocalDate на 20 февраля 2015 года:
LocalDate.of(2015, 02, 20);
LocalDate.parse("2015-02-20");
LocalDate предоставляет различные служебные методы для получения различной информации. Давайте кратко рассмотрим некоторые из этих методов API.
Следующий фрагмент кода получает текущую локальную дату и добавляет один день:
LocalDate tomorrow = LocalDate.now().plusDays(1);
Этот пример получает текущую дату и вычитает один месяц. Обратите внимание на то, как он принимаетenum в качестве единицы времени:
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. Работа сLocalTime
LocalTime представляетtime without a date.
ПодобноLocalDate, экземплярLocalTime может быть создан из системных часов или с помощью методов «parse» и «of». Ниже приведены некоторые из наиболее часто используемых API.
Экземпляр текущегоLocalTime может быть создан из системных часов, как показано ниже:
LocalTime now = LocalTime.now();
В приведенном ниже примере кода, мы создаемLocalTime, представляющее 06:30, путем анализа строкового представления:
LocalTime sixThirty = LocalTime.parse("06:30");
Заводской метод «of» может использоваться для созданияLocalTime. Например, приведенный ниже код создаетLocalTime, представляющий 06:30 утра, с использованием фабричного метода:
LocalTime sixThirty = LocalTime.of(6, 30);
В приведенном ниже примере создаетсяLocalTime путем анализа строки и добавляется к ней час с помощью API «плюс». Результатом будетLocalTime, представляющий 07:30:
LocalTime sevenThirty = LocalTime.parse("06:30").plus(1, ChronoUnit.HOURS);
Доступны различные методы получения, которые можно использовать для получения определенных единиц времени, таких как часы, минуты и секунды, как показано ниже:
int six = LocalTime.parse("06:30").getHour();
Мы также можем проверить, является ли определенное время до или после другого определенного времени. В приведенном ниже примере кода сравниваются дваLocalTime, для которых результат будет верным:
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 используется для представленияa combination of date and time.
Это наиболее часто используемый класс, когда нам нужна комбинация даты и времени. Класс предлагает множество API, и мы рассмотрим некоторые из наиболее часто используемых.
ЭкземплярLocalDateTime может быть получен из системных часов аналогичноLocalDate иLocalTime:
LocalDateTime.now();
Приведенные ниже примеры кода объясняют, как создать экземпляр, используя фабричные методы «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. ИспользованиеZonedDateTime API
Java 8 provides ZonedDateTime # [.typ] # когда нам нужно иметь дело с датой и временем конкретного часового пояса. ZoneId - это идентификатор, используемый для представления различных зон. Существует около 40 различных часовых поясов, иZoneId используются для их представления следующим образом.
В этом фрагменте кода мы создаемZone для Парижа:
ZoneId zoneId = ZoneId.of("Europe/Paris");
Набор всех идентификаторов зоны может быть получен, как показано ниже:
Set allZoneIds = 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 можно создать, как показано ниже, с помощьюZoneOffset. Здесь мы создаемLocalDateTime, представляющее 6:30 утра 20 февраля 2015 года:
LocalDateTime localDateTime = LocalDateTime.of(2015, Month.FEBRUARY, 20, 06, 30);
Затем мы добавляем ко времени два часа, создавZoneOffset и установив для экземпляра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
КлассPeriod представляет количество времени в годах, месяцах и днях, а классDuration представляет количество времени в секундах и наносекундах.
5.1. Работа сPeriod
КлассPeriod широко используется для изменения значений заданной даты или для получения разницы между двумя датами:
LocalDate initialDate = LocalDate.parse("2007-05-10");
Date можно изменять с помощьюPeriod, как показано в следующем фрагменте кода:
LocalDate finalDate = initialDate.plus(Period.ofDays(5));
КлассPeriod имеет различные методы получения, такие какgetYears, getMonths иgetDays, для получения значений из объектаPeriod. В приведенном ниже примере кода возвращается значениеint, равное 5, поскольку мы пытаемся получить разницу в днях:.
int five = Period.between(initialDate, finalDate).getDays();
Period между двумя датами можно получить в определенных единицах измерения, таких как дни, месяц или годы, используяChronoUnit.between:.
long five = ChronoUnit.DAYS.between(initialDate, finalDate);
Этот пример кода возвращает пять дней. Давайте продолжим, посмотрим на классDuration.
5.2. Работа сDuration
ПодобноPeriod,Duration class используется для работы сTime.. В следующем коде мы создаемLocalTime на 6:30 утра, а затем добавляем продолжительность 30 секунд, чтобы aLocalTime 06:30:30:
LocalTime initialTime = LocalTime.of(6, 30, 0);
LocalTime finalTime = initialTime.plus(Duration.ofSeconds(30));
Duration между двумя моментами может быть получен либо какDuration, либо как конкретная единица. В первом фрагменте кода мы используем методbetween() классаDuration, чтобы найти разницу во времени междуfinalTime иinitialTime и вернуть разницу в секундах:
long thirty = Duration.between(initialTime, finalTime).getSeconds();
Во втором примере мы используем методbetween() классаChronoUnit для выполнения той же операции:
long thirty = ChronoUnit.SECONDS.between(initialTime, finalTime);
Теперь посмотрим, как преобразовать существующиеDate иCalendar в новыеDate /Time..
6. Совместимость сDate иCalendar
В Java 8 добавлен методtoInstant(), который помогает преобразовать существующие экземплярыDate иCalendar в новый 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);
Теперь перейдем к форматированиюDate иTime.
7. Date иTime Форматирование
Java 8 предоставляет API-интерфейсы для простого форматированияDate иTime: __
LocalDateTime localDateTime = LocalDateTime.of(2015, Month.JANUARY, 25, 6, 30);
Приведенный ниже код передает формат даты ISO для форматирования локальной даты. Результат будет 2015-01-25:
String localDateString = localDateTime.format(DateTimeFormatter.ISO_DATE);
DateTimeFormatter предоставляет различные стандартные параметры форматирования. Пользовательские шаблоны также могут быть предоставлены для метода форматирования, как показано ниже, который вернетLocalDate как 2015/01/25:
localDateTime.format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
Мы можем передать стиль форматирования какSHORT,LONG илиMEDIUM как часть параметра форматирования. Приведенный ниже пример кода даст результат, представляющийLocalDateTime на 25 января 2015 г., 06:30:00:
localDateTime
.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM))
.withLocale(Locale.UK);
Давайте посмотрим на альтернативы, доступные API Java 8 CoreDate /Time.
8. Backport и альтернативные варианты
8.1. Использование Project Threeten
Для организаций, которые находятся на пути перехода на Java 8 с Java 7 или Java 6 и хотят использовать API даты и времени, проектthreeten предоставляет возможность резервного копирования. Разработчики могут использовать классы, доступные в этом проекте, для достижения той же функциональности, что и новые API Java 8Date иTime, и после перехода на Java 8 пакеты можно переключать. Артефакт для проекта threeten можно найти вmaven central repository:
org.threeten
threetenbp
1.3.1
8.2. Joda-Time Library
Другой альтернативой библиотеке Java 8Date иTime является библиотекаJoda-Time. Фактически, API Java 8Date Timeразрабатывались совместно автором библиотеки Joda-Time (Стивен Колеборн) и Oracle. Эта библиотека предоставляет практически все возможности, которые поддерживаются в проекте Java 8Date Time. Артефакт можно найти вmaven central, включив в свой проект указанную ниже зависимость pom:
joda-time
joda-time
2.9.4
9. Заключение
Java 8 предоставляет богатый набор API с согласованным дизайном API для упрощения разработки.
Примеры кода для указанной выше статьи можно найти в git-репозиторииJava 8 Date/Time.