Более четкое, удобочитаемое и мощное кодирование с помощью Java SE 8 Новый API DateTime JSR 310… ..
В предыдущей статье « Обработка коллекций с помощью Streams API »; Я глубоко погрузился в объяснение и изучение способов обхода коллекций с помощью потоков , создания потоков из коллекций и массивов и, наконец, агрегации значений потоков. В этой статье серии «Обзор новых функций Java SE 8» мы углубимся в изучение объяснение и исследование кода спецификации JSR 310, о том, как вычислять временные интервалы с помощью нового API DateTime, вычислять интервалы времени с мгновенными и длительными значениями, представлять значения даты и времени, форматировать значения даты и времени и поддерживать смещения часовых поясов.
Исходный код размещен на моей учетной записи Github : клонируйте его отсюда .
Содержание:
- Расчет промежутков времени с Instant и Duration.
- Представление даты и времени.
- Форматирование значений даты и времени.
- Поддержка часовых поясов.
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.971Z2016-08-05T21:33:56.048Z |
Если бы я переместил эту строку кода вниз, чтобы я не выполнял никакой другой обработки между двумя вызовами метода now, эти два значения были бы идентичны, или они могли бы быть отключены на одну тысячную секунды.
Результат:
|
1
2
|
2016-08-05T21:34:43.365Z2016-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.685Z2016-08-05T22:28:43.769ZElapsed: PT1.084S |
Чтобы сделать это немного более читабельным, я добавлю вызов метода объекта duration, используя elapsed.to millis . Это означает, что получите эквивалент в миллисекундах. И я добавлю к этому, миллисекунды, и я выполню код.
Результат:
|
1
2
3
|
2016-08-05T22:32:52.657Z2016-08-05T22:32:53.731ZElapsed: 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.6252016-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-0920:37:11.5352016-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/169 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/AdenAmerica/CuiabaEtc/GMT+9Etc/GMT+8Africa/NairobiAmerica/MarigotAsia/AqtauPacific/KwajaleinAmerica/El_SalvadorAsia/PontianakAfrica/CairoPacific/Pago_PagoAfrica/MbabaneAsia/KuchingPacific/HonoluluPacific/RarotongaAmerica/GuatemalaAustralia/HobartEurope/LondonAmerica/BelizeAmerica/PanamaAsia/ChungkingAmerica/ManaguaAmerica/Indiana/PetersburgAsia/YerevanEurope/BrusselsGMTEurope/WarsawAmerica/ChicagoAsia/KashgarChile/ContinentalPacific/YapCETEtc/GMT-1Etc/GMT-0Europe/JerseyAmerica/TegucigalpaEtc/GMT-5Europe/IstanbulAmerica/EirunepeEtc/GMT-4America/MiquelonEtc/GMT-3Europe/LuxembourgEtc/GMT-2Etc/GMT-9America/Argentina/CatamarcaEtc/GMT-8Etc/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, который вы можете использовать для расчета разных часовых поясов друг против друга.
Ресурсы:
- Учебные руководства по Java, Trail: Date Time
- API LocalDate
- JSR 310: API даты и времени
- JSR 337: Java SE 8 Release Содержание
- Сайт OpenJDK
- Платформа Java, стандартное издание 8, спецификация API
Надеюсь, вам понравилось читать, так как мне понравилось писать, пожалуйста, поделитесь, если вам нравится, распространите слово.
| Ссылка: | Обзор новых возможностей Java SE 8: расчет временных интервалов с помощью нового API DateTime от нашего партнера по JCG Мохамеда Тамана в блоге « Улучшите свою жизнь с помощью науки и искусства» . |
