Статьи

Обзор новых функций Java SE 8: расчет временных интервалов с помощью нового API DateTime

Более четкое, удобочитаемое и мощное кодирование с помощью Java SE 8 Новый API DateTime JSR 310… ..

Java-дата и время-JavaSE8-500-300

Java SE 8, JSR 310

В предыдущей статье « Обработка коллекций с помощью Streams API »; Я глубоко погрузился в объяснение и изучение способов обхода коллекций с помощью потоков , создания потоков из коллекций и массивов и, наконец, агрегации значений потоков. В этой статье серии «Обзор новых функций Java SE 8» мы углубимся в изучение объяснение и исследование кода спецификации JSR 310, о том, как вычислять временные интервалы с помощью нового API DateTime, вычислять интервалы времени с мгновенными и длительными значениями, представлять значения даты и времени, форматировать значения даты и времени и поддерживать смещения часовых поясов.

Исходный код размещен на моей учетной записи Github : клонируйте его отсюда .

Содержание:

  1. Расчет промежутков времени с Instant и Duration.
  2. Представление даты и времени.
  3. Форматирование значений даты и времени.
  4. Поддержка часовых поясов.

1- Расчет промежутков времени с Мгновенным и Длительностью

Вступление:
Java SE 8 включает полностью новый API для управления значениями даты и времени. Классы, которые на самом деле содержат данные в этом новом API, являются неизменяемыми и поточно-ориентированными . Это означает, что вам не нужно беспокоиться о передаче объектов в многопоточном окружении. И если вы используете их в параллельных потоках , все всегда будет работать идеально. Все классы в этом новом API являются членами пакета java.time . И я начну с двух основных классов, названных Instant и Duration .

Как это работает:
Я начну с этого примера в пакете eg.com.tm.java8.features.datetime.InstDuration проекта Java8Features . В коде класса с именем InstantAndDuration . И я помещу весь этот код в метод main . Первый класс, который я опишу, называется Instant . Я java.time имя класса и java.time Ctrl + Space , и выберу класс из пакета java.time , и он импортирован выше. Мгновенный объект представляет момент на временной шкале Java. Как и в случае с классом даты, который является частью старого способа управления датами и временем, экземпляр представляет количество миллисекунд.

Начиная с эпохи Java, 1 января 1970 года. Чтобы объявить мгновенный объект, я объявлю его с его типом, и дам ему имя start. И тогда я вызову статический метод мгновенного класса, который вызывается now() . И это представляет момент на текущей машине, когда код был вызван. Затем я выведу это значение в виде строки, используя стандартный системный вывод. Запустите код, и вы увидите, что вывод начинается с даты в формате год, месяц, дата, а затем время после буквы T.

Результат:

1
2016-08-05T21:21:59.601Z

Если у вас есть момент времени, вы можете использовать его для вычисления разницы между этим и другим моментом времени. Поэтому я создам еще одно мгновение, которое назову концом. И я получу это значение из метода now() . Затем я буду использовать системный вывод и выводить это значение. Обратите внимание, что между этими двумя значениями есть небольшая разница, и это время, которое требуется моей системе. Чтобы обработать эту строку кода, это выводит начальное значение.

Результат:

1
2
2016-08-05T21:33:55.971Z
2016-08-05T21:33:56.048Z

Если бы я переместил эту строку кода вниз, чтобы я не выполнял никакой другой обработки между двумя вызовами метода now, эти два значения были бы идентичны, или они могли бы быть отключены на одну тысячную секунды.

Результат:

1
2
2016-08-05T21:34:43.365Z
2016-08-05T21:34:43.365Z

Теперь я покажу вам, как рассчитать разницу между этими двумя значениями. Когда вы сравниваете два момента друг с другом, вы получите объект, который называется продолжительностью. Он представлен классом Duration , который также является членом Java.time . Я назову этот объект прошедшим. Я вызову статический метод класса продолжительности, который between(Temporal startInclusive, Temporal endExclusive) . Обратите внимание, что он ищет объекты, напечатанные как нечто, называемое Temporal . Мгновенный класс является подклассом Temporal.

Результат:

1
Elapsed: PT0S

Я передам начало и конец как два моих временных значения. И тогда я выведу разницу. Я передам буквальный ярлык истекшего, а затем я передам в моей переменной. Этот объект длительности начинается с буквы p, а затем t для времени. Это снова значение в формате ISO. И тогда он показывает мне ноль секунд. Хорошо, давайте посмотрим, что произойдет, если мы бросим вызов метода сна. Я установлю курсор между начальным и конечным вызовами. И я буду использовать класс Thread .

Я нажму точку, а затем нажмите Ctrl + Пробел . Затем я вызову метод sleep() и передам значение 1,000. Значение сна на одну секунду. Метод sleep может вызвать ошибку, поэтому я воспользуюсь быстрым исправлением и добавлю объявление throws в сигнатуру основных методов. Я сохраняю и запускаю код, и вижу, что мое время перерыва теперь составляет 1,001 секунды. Вы никогда не можете рассчитывать на то, что все будет точно, все зависит от того, что происходит, на компьютере обработки.

Результат:

1
Elapsed: PT1.001S

Затем я возьму этот вызов printline и верну его в исходное местоположение. Так что теперь, после того как я получу начальное значение, я буду выполнять команду printline. И я буду спать одну секунду. И я буду запускать код. И теперь мой промежуток времени составляет 1,057 секунды.

Результат:

1
2
3
2016-08-05T22:28:42.685Z
2016-08-05T22:28:43.769Z
Elapsed: PT1.084S

Чтобы сделать это немного более читабельным, я добавлю вызов метода объекта duration, используя elapsed.to millis . Это означает, что получите эквивалент в миллисекундах. И я добавлю к этому, миллисекунды, и я выполню код.

Результат:

1
2
3
2016-08-05T22:32:52.657Z
2016-08-05T22:32:53.731Z
Elapsed: 1074 milliseconds

Вывод:
И теперь я вижу, читаемое значение 1054 миллисекунды. Итак, это класс Instant класс Duration . Два основных класса нового API даты и времени в Java SE 8.

2- Представление значений даты и времени:

Вступление:
Ранее я описывал, как использовать моментальный класс в новом API времени и даты для представления момента на временной шкале Java. Вот еще три полезных класса для представления частей дат и времени. Они называются местная дата , местное время и местное время . Скажем, например, что вы хотите представлять только значение даты. И вас не волнует время, секунды или миллисекунды, а только текущая дата. Создайте экземпляр класса с именем LocalDate .
Как это работает :
Я работаю в пакете eg.com.tm.java8.features.datetime.localdt проекта Java8Features . В коде класса с именем LocalDateAndTime . с основным методом. Я начну с названия класса LocalDate . И когда я нажимаю Ctrl + Space , я выбираю класс из пакета java.time. Я назову объект currentDate и получу его значение с помощью localDate.now . Обратите внимание, что в синтаксисе есть согласованность между работой с моментом, датой, временем и датой-временем.

Чтобы получить текущее значение на текущем компьютере, вы всегда используете метод now . Теперь я выведу эту дату в формате по умолчанию. Я сохраняю и запускаю код, и он показывает мне дату в формате год-месяц-дата .

Результат:

1
2016-08-06

Вы также можете создать объект даты, используя конкретные значения года, месяца и даты. Еще раз, я создам объект, типизированный как LocalDate . И я назову эту specificDate . Чтобы получить это значение, вызовите LocalDate.of .

И есть несколько доступных версий. Я собираюсь использовать тот, который принимает три целых значения. Они не названы в документации, но представляют год, месяц и день. Я исправлю значения 2000, 1 и 1. Теперь, в более старой версии API даты и времени, использующего класс даты. Когда вы имели дело с месяцами, вам всегда приходилось делать это с нулевым смещением. Так что для января вы будете использовать 0, для 1 февраля и так далее.

И это не было особенно интуитивно понятно. В новом дневном API все основано на 1. Итак, 1 означает январь , 2 означает февраль и так далее. Как и следовало ожидать.

Я снова буду использовать вывод системы, и на этот раз я поставлю новую конкретную дату. И когда я сохраняю и запускаю это, я получаю значение, которое я ввел, 1 января 2000 года.

Результат:

1
2016-01-01

Если вы хотите представить только значение времени, используйте класс LocalTime , я LocalTime имя класса и импортирую его, назову объект currentTime и получу его значение из LocalTime.now .

Опять же, используя тот же синтаксис, что и для localdate, и для Instant. Тогда я буду использовать системный вывод. И я выведу это текущее время. Значением по умолчанию для времени является 24-часовая запись, и оно показывает часы, минуты, секунды и миллисекунды.

Результат:

1
01:18:11.779

Я буду использовать LocalTime . Я назову это specificTime . И, как и в случае с локальным классом дат, я вызову метод с именем of . Опять же, существует множество разных версий, принимающих разное количество аргументов.

Я буду использовать версию, которая ищет три целочисленных значения, и я введу 14, 0 и 45. И затем я выведу это значение на консоль. И есть результат. 14, 00 и 45 секунд. Обратите внимание, что поскольку я не указал значение в миллисекундах, форматированная версия этого времени не отображает значения после точки.

Результат:

1
14:00:45

Наконец, я покажу, как использовать класс LocalDateTime .

Я напишу имя класса и импортирую его. Я назову этот объект currentDT . И я получу его значение из LocalDateTime.now . Когда вы выводите значение даты и времени, вы получите длинный формат даты и времени ISO. Начиная с даты и заканчивая временем. И если в значении есть миллисекунды, они будут отображены. И, наконец, я создам конкретную дату и время, и я сделаю это, комбинируя мою конкретную дату и мое конкретное время.

Этот код будет выглядеть как следующий код. Я LocalDateTime объект LocalDateTime . Я назову его, specificDT и снова вызову LocalDateTime.of . И на этот раз я буду использовать эту первую версию метода, который принимает объект локальной даты и объект местного времени. Вы также можете построить свое значение даты и времени из комбинаций лет, месяцев, дат и значений времени. Я передам свою конкретную дату и мое конкретное время. И тогда я выведу его на консоль. И когда я запускаю этот код, мое конкретное время даты представляет собой комбинацию моей конкретной даты и моего конкретного времени.

Результат:

1
2
2016-08-06T01:30:46.625
2016-01-01T14:00:45

Вывод:
Итак, это три класса, которые вы можете использовать для представления значений даты и времени на локальном компьютере в текущем часовом поясе. Есть также классы, которые вы можете использовать для получения значений, чувствительных к часовому поясу. И я опишу эти дальше.

3- Форматирование значений даты и времени:

Вступление:
Ранее я описывал, как использовать LocalDate , LocalTime и LocalDateTime для представления значений времени. Чтобы представить эту информацию пользователю, вам необходимо отформатировать ее. И для этой цели есть новый класс с именем DateTimeFormatter . Я покажу вам, как создавать форматтеры, используя простой синтаксис. А затем, как сделать очень нестандартную работу, используя класс Daytime Formatter Builder.

Как это работает:
Я работаю в пакете eg.com.tm.java8.features.datetime.format проекта Java8Features . В коде класса с именем DateTimeFormater . с основным методом.

Сначала я создам дату. Я дам ему тип LocalDate , LocalDate , что импортировал этот класс. И я назову это currentDate . И я получу его значение из LocalDate.now . Далее я создам объект форматирования. Я java.time.format имя класса DateTimeFormatter и java.time.format его из пакета java.time.format . Я назову этот объект df . Сейчас существует несколько способов создания форматера. Одним из самых простых является использование константы класса DateTmeFormatter .

Я еще раз DateTimeFormatter в DateTimeFormatter . И затем после ввода периода я вижу список всех доступных констант. Я выберу ISO Date . И это обеспечит форматирование по умолчанию для этого объекта. Затем я буду использовать вывод системы. Я вызову метод форматирования отформатированного объекта и передам объект даты. И вот результат. Я вывожу значение в формате год-месяц-дата. С месяцем и датой, дополненной до двух символов каждый. Теперь вы можете делать то же самое со временем и датой.

Результат:

1
2016-08-06

Я возьму приведенный выше фрагмент кода, пару раз дублирую его и внесу некоторые изменения. Во второй версии я поменяю тип с местного на местное время. Имя объекта до текущего времени и имя класса, который я использую, чтобы получить значение по местному времени. Я изменю название форматера даты и времени с DF на TF для форматера времени. И я заменю постоянную, которую я использую, на ISO Time . И тогда я изменю объект, который я форматирую. Я обязательно импортирую класс LocalTime .

А потом я внесу аналогичные изменения в третью версию. Класс, с которым я буду работать в этот раз, — LocalDateTime . Я буду обязательно импортировать его. Я назову этот объект, текущий DT. И я изменю класс, из которого я вызываю метод now. Я изменю форматер на DTF для DateTimeFormatter . И я изменю константу на ISO Date Time. И тогда я отформатирую текущий объект DT. Я буду уверен, что использую правильные средства форматирования в каждой версии кода. Я сохраню изменения и выполню код.

И есть три отформатированных значения. Пока что я не очень-то многого достиг, потому что я использовал константы, которые представляют форматирование по умолчанию. Но давайте посмотрим на некоторые пользовательские форматы, которые доступны.

Результат:

1
2
3
2016-08-09
20:37:11.535
2016-08-09T20:37:11.538

Я опускаюсь ниже существующего кода. И я создам еще один DateTimeFormatter , назову этот f_long для длинного формата даты и получу его значение, вызвав метод класса DateTimeFormatter, называемый Localized Date.

Обратите внимание, что существуют методы для даты, времени и даты-времени с различными аргументами. Я выберу один из локализованной даты и передам константу класса с именем FormatStyle . Обязательно импортируйте этот класс. И после того, как вы введете точку, вы увидите, что доступно четыре константы. Полный, длинный, средний и короткий. Я выберу длинную версию, а затем выведу отформатированную дату, вызвав F _ long.format, и передам текущий объект DT.

Когда я запускаю этот код, я получаю длинную версию даты.

Результат:

1
August 9, 2016

Я покажу вам другую версию этого, дублируя эти две строки кода, и для этой версии я изменю имя форматера на f_short, я также изменю используемую константу на short. И я поменяю имя форматера, которому я звоню. Таким образом, длинная версия — это название месяцев. Запятая после даты, а затем года в четырехзначном формате и краткой версии, по крайней мере, для текущей локали, — это месяц и дата без отступов, с косыми чертами, разделяющими значения, и двухсимвольный год.

Результат:

1
8/9/16

А затем я покажу вам, как использовать локали. Я создам пару строк. Первый будет называться fr_ short, для французского, в коротком формате. Чтобы получить это значение, я вызову мой форматтер f_short, а затем назову имя метода с помощью withLocal() . Чтобы получить значение локали, я буду использовать класс Local , это существующий класс, который был доступен в предыдущих версиях Java . Это член пакета Java.util .

И тогда я могу назвать одну из множества констант, представляющих различные локали. Я буду использовать французский. Затем я вызову метод форматирования и передам текущую дату.

Я продублирую эту строку кода и для этой версии буду использовать fr_long. Я буду использовать длинный форматер, иначе код будет таким же. И тогда я fr_short эти два значения fr_short и fr_long .

И вот результат. Обратите внимание на fr_short, что месяц и день поменялись местами с американской версии. И это потому, что в Европе сначала указывается дата, а затем месяц, а затем год. И когда я использую длинную версию, я получаю месяцы, записанные на французском языке.

Результат:

1
2
09/08/16
9 août 2016

Наконец, я покажу вам, как создавать полностью собственные средства форматирования, используя класс, называемый средством форматирования даты и времени. Он использует шаблон проектирования компоновщика, где вы можете вызывать несколько методов, каждый из которых возвращает экземпляр текущего компоновщика.

Я напишу имя класса и позабочусь, чтобы он был импортирован. И я назову объект б. Я создам его экземпляр с помощью нового ключевого слова и метода конструктора.

Теперь, в конце этого кода я не буду ставить точку с запятой, потому что я хочу немедленно вызвать серию методов, которые позволят мне с нуля собрать средство форматирования. Я начну с метода с именем Append Value. Обратите внимание, что есть «Добавить», «Добавить», «Добавить», «Локализовать» и многие другие. Я собираюсь вызвать метод с именем appendValue() который принимает экземпляр класса с именем TemporalField а затем я буду использовать перечислитель с именем ChronoField . Который расширен от того TemporalField .

И оттуда я буду использовать постоянное название месяца года. Далее я добавлю буквальное значение. Это может быть любой символ или любая строка. И просто, чтобы сделать это совершенно уникальным, я добавлю пару символов. Теперь я возьму эти две строки кода и продублирую их для второй версии вместо месяца года. Я положу в день месяца. Обратите внимание, что есть также день недели и день года. Затем я продублирую эту строку кода и переместу ее вниз. И я закончу выражение с ChronoField.year .

Как только вы создали объект построителя, вы можете получить средство форматирования. Я создам новый объект, типизированный как DateTimeFormatter . Я назову это f для Formatter. И вызывал объекты построителя для методов форматирования, а затем, наконец, я отформатирую текущее значение даты и времени. Я буду использовать системный вывод, вызову f.format() и currentDT .

И теперь, когда я запускаю свой код, я получаю полностью настроенный формат.

Результат:

1
222||9||2016

Вывод:
Вы можете использовать конструктор DateTimeFormatter для создания любого формата, который вам нравится. А поскольку он использует шаблон проектирования компоновщика, его легко создавать и поддерживать в коде.

4- Поддержка смещения часового пояса:

Вступление:
Новый API даты и времени предлагает ряд классов, которые позволяют вам управлять часовыми поясами . Создание объектов дневного времени, смещенных от среднего времени по Гринвичу , на определенное количество часов или на определенные местоположения , и вычисление различий между часовыми поясами.

Как это работает:
Я работаю в пакете eg.com.tm.java8.features.datetime.zone проекта Java8Features . В коде класса с именем TimeZones . с main методом.

В его основном методе я создал объект DateTimeFormatter и LocalDateTime . LocalDateTime представляет текущую дату и время в моей системе, в моем часовом поясе . И это египетское время, потому что я на Ближнем Востоке.

А потом я выводю отформатированное значение на консоль. Я выводю значение в коротком формате. А в египетской нотации это месяц, день и год.

Результат:

1
8/9/16 10:22 PM

Для представления значения даты и времени на основе часового пояса используйте класс ZonedDateTime . Как и LocalDateTime , он неизменен и безопасен для потоков. Я напишу имя класса, а затем нажмите Control + Пробел, чтобы добавить оператор импорта. И я назову объект gmt для среднего времени по Гринвичу.

Есть несколько разных способов создания этого объекта. Я покажу вам, как создать объект, вычисляющий смещение от среднего времени по Гринвичу. Я снова буду использовать класс ZonedDateTime , и после того, как я введу точку, я увижу, что доступно много методов. Я могу снова вызвать now() , чтобы получить значение даты и времени в моем регионе. Я могу вызывать методы of() которые позволяют мне делать различные вычисления. Я могу разобрать строки, но я собираюсь использовать эту версию метода now. Я передам экземпляр класса ZoneId .

ZoneId представляет собой определенное количество часов, смещенное от среднего времени по Гринвичу. И я получу это значение, вызвав метод с именем ZoneId.of() . И я передам буквальную строку «GMT + 0» . Это означает, что покажите мне текущую дату и время в среднем времени по Гринвичу.

Теперь я продублирую свой код, который выводит значение на консоль. Я переместу это вниз, и я изменю эту версию для вывода gmt. Я запускаю код, и есть результат.

Результат:

1
8/9/16 8:28 PM

Я на Ближнем Востоке, в Египте, и сейчас среднее время по Гринвичу на два часа вперед.

Вот еще один подход к получению ZonedDateTime . Допустим, вы хотели получить ZoneDateTime в Нью-Йорке . Есть много встроенных строк или констант, которые позволят вам назвать конкретные местоположения, и вы получите правильный ZoneId для этого местоположения, и вам не придется беспокоиться о математике самостоятельно. Я создам другой объект ZonedDateTime, и на этот раз я назову его ny для Нью-Йорка, и получу его значение, вызвав ZonedDateTime.now() , и снова передам Z oneId.of() , но на этот раз я передам в строку Америки / New_York .

Убедитесь, что написали эту строку именно так, как вы видите здесь . Я создам строку кода для вывода этого значения. Я сохраню изменения и запусту их. А Нью-Йорк на восточном побережье, на три часа опережая тихоокеанское

Результат:

1
8/9/16 4:36 PM

Чтобы узнать обо всех доступных строках, вы можете вызвать метод класса getAvailableZoneIds() именем getAvailableZoneIds() . Ты вернешь набор. Я Java.util Set и Java.util Control + Space , а затем выберу set из Java.util .

И я установлю общий тип элементов в этом наборе на String . Я называю установленные зоны. И тогда я вызову метод ZoneId.getAvailableZoneIds . Затем я переберу строки с помощью метода forEach() . А потом я передам в лямбда- выражении. Так что я могу разбираться с каждым из пунктов по очереди.

Результат:
Когда я запускаю этот код, я вижу все доступные строки.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
Asia/Aden
America/Cuiaba
Etc/GMT+9
Etc/GMT+8
Africa/Nairobi
America/Marigot
Asia/Aqtau
Pacific/Kwajalein
America/El_Salvador
Asia/Pontianak
Africa/Cairo
Pacific/Pago_Pago
Africa/Mbabane
Asia/Kuching
Pacific/Honolulu
Pacific/Rarotonga
America/Guatemala
Australia/Hobart
Europe/London
America/Belize
America/Panama
Asia/Chungking
America/Managua
America/Indiana/Petersburg
Asia/Yerevan
Europe/Brussels
GMT
Europe/Warsaw
America/Chicago
Asia/Kashgar
Chile/Continental
Pacific/Yap
CET
Etc/GMT-1
Etc/GMT-0
Europe/Jersey
America/Tegucigalpa
Etc/GMT-5
Europe/Istanbul
America/Eirunepe
Etc/GMT-4
America/Miquelon
Etc/GMT-3
Europe/Luxembourg
Etc/GMT-2
Etc/GMT-9
America/Argentina/Catamarca
Etc/GMT-8
Etc/GMT-7
.................

Сейчас их так много, что может быть трудно найти то, что вы ищете. Итак, скажем, что я хотел искать Лондон .

И использовать часовой пояс для этого конкретного места, в это конкретное время года. Как я показал ранее в этой статье, я мог использовать predicate для поиска строк. Я создам объект предиката. И я установлю универсальный тип на String . И я назову состояние объекта. Затем я реализую предикат с помощью лямбда-выражения. Я передам в str, а затем я реализую предикат с условием. str.contains, и я передам в лондонской нити.

Тогда я сделаю рефакторинг своего выражения ламба. Я собираюсь обернуть System.out.println() в фигурные скобки. Затем я расширю код, чтобы было легче работать с ним. Я добавлю точку с запятой в конце строки печати, а затем создам оператор if. И я установлю условие для condition.test() , и я передам в z для текущей зоны. Я переместу rintln() p rintln() в условие, и теперь я выведу только те строки, которые соответствуют моему тесту предикатов.

Я сохраняю изменения и запускаю код, и есть результат. Я считаю, что правильная строка для Лондона:

Результат:

1
Europe/London

Вывод:
Это немного о работе с часовыми поясами. Опять же, используйте класс ZonedDateTime вместо LocalDateTime для представления значений, которые можно изменять и вычислять. ZoneId представляет собой смещение из среднего времени по Гринвичу. И есть также класс с именем Zone Offset, который вы можете использовать для расчета разных часовых поясов друг против друга.
Ресурсы:

  1. Учебные руководства по Java, Trail: Date Time
  2. API LocalDate
  3. JSR 310: API даты и времени
  4. JSR 337: Java SE 8 Release Содержание
  5. Сайт OpenJDK
  6. Платформа Java, стандартное издание 8, спецификация API

Надеюсь, вам понравилось читать, так как мне понравилось писать, пожалуйста, поделитесь, если вам нравится, распространите слово.