Введение в API даты/времени Java 8

Введение в 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

Read more

Проверьте, является ли строка действительной датой в Java

Взгляните на разные способы проверить, является ли String действительной датой в Java

Read more

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.