Работа с датами и временем
Работая с различными типами данных, ранее мы не касались вопросов работы с датой и временем. Тем не менее, классы для работы с ними – одни из наиболее используемых. В рамках этого урока мы рассмотрим как устаревшие, так и современные типы данных, предназначенных для работы с датой и временем.
Date. Calendar
До Java 8 единственными, не касаясь внешних библиотек, способами работы с датами и временем были классы Date (по сути, оберткой над временем, прошедшем с начала UNIX-эпохи – с 1 января 1970 года) и Calendar. Оба класса являются изменяемыми.
Более подробно с этими классами, а также с форматированием строк для их представления в виде объектов указанных классов предлагаю познакомиться в рамках статьи: https://javarush.com/groups/posts/1941-kak-ne-poterjatjhsja-vo-vremeni--datetime-i-calendar
Данные классы все еще можно встретить в старых проектах, поэтому считаю необходимым иметь хотя бы общее представление о них и методах, которые они предоставляют.
Сами классы не считаются официально устаревшими – не помечены как @Deprecated, однако все конструкторы Date, кроме конструктора по умолчанию (формирует объект для текущих даты и времени) и принимающего время в миллисекундах – уже устарели.
Пакет java.time
В рамках Java 8 – очень крупного релиза, по сути, заложившего основу Java в том виде, в котором она известна сегодня, добавились и новые механизмы для работы с датой и временем – пакет java.time.
Он предоставляет достаточно разнообразные инструменты. Сразу отмечу характерную особенность рассматриваемых классов – они неизменяемые.
Более близкое знакомство начнем с идеологического наследника класса Date – Instant. Он также хранит время, прошедшее с начала UNIX-эпохи. Данный класс тяжело назвать популярным, но иногда с ним приходится сталкиваться. Он является достаточно специфичным в плане работы – в отличии от классов, рассматриваемых ниже, Instant лишен части узких методов по манипуляции со значением. Те же, что есть – являются более многословными, нежели у классов, рассматриваемых ниже.
Другой относительно большой блок классов представлен ниже. Их отличительной особенностью является то, что они НЕ работают с датой и временем относительно начала UNIX-эпохи. И именно с этими классами придется сталкиваться чаще всего:
· LocalDate. Класс для работы с датами. В отличии от Date или Instant, хранит год, месяц и дату в отдельных полях. Более подробно с методами можно ознакомиться в статье: https://metanit.com/java/tutorial/12.3.php;
· LocalTime. Все тоже самое, только для времени. Отдельной статьи под методы этого класса у меня нет, но большинство из них однотипны с методами LocalDate. Предлагаю ознакомиться с ними самостоятельно;
· LocalDateTime. Класс, объединяющий LocalDate и LocalTime (они представлены полями в этом классе). Имеет методы, возвращающие объект даты или времени (toLocalDate() и toLocalTime() соответственно). Обратите внимание, классы LocalDate и LocalTime (в меньшей степени) также имеют методы, возвращающие объект типа LocalDateTime. В целом, данный класс наиболее популярен из всего пакета;
· ZonedDateTime. Содержит внутри себя поле типа LocalDateTime, а также несколько объектов, отвечающих за timezone – часовой пояс. Как правило, в рамках внутренней логики используются объекты LocalDateTime, но общение с внешними интерфейсами (получение и отправка дат при взаимодействии с клиентской (браузерной) частью клиент-серверного приложения, например) происходит с помощью ZonedDateTime.
Также описанные классы имеют функционал (методы) для создания объектов из Instant. Как и Instant имеет методы создания объектов на основании параметров, являющихся наследниками Temporal (сюда относятся и сам Instant, и все описанные в данном подразделе классы). Таким образом реализуется совместимость использования двух подходов по работе с датой и временем в рамках java.time.
В данной статье нет описания методов классов из java.time, поскольку они являются достаточно предсказуемыми на основании названия (в отличии от некоторых методов String, например). Однако я настоятельно рекомендую ознакомиться с ними самостоятельно. Обратите внимание, что объекты описанных выше классов создаются через статические методы, а не конструкторы.
Пакет java.time. DateTimeFormatter
Класс DateTimeFormatter выполняет примерно те же функции в java.time, что и SimpleDateFormat для работы с Date и Calendar. Способ описания шаблонов также является схожим с SimpleDateFormat.
Для желающих ознакомиться подробнее, рекомендую обратиться к документации (там содержится и таблица обозначений для описания паттернов в DateTimeFormatter):
https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html
В рамках данного класса рекомендую ознакомиться с константами типа DateTimeFormatter, которые там представлены (в документации выше есть примеры для них, обратите внимание), а также тремя методами:
1. static ofPattern(). Является перегруженным для указания локали. Возвращает объект DateTimeFormatter на основании строки-паттерна, переданной параметром. Таким параметром может быть, например, "yyyy-MM-dd HH:mm:ss";
2. format(). Возвращает строку на основании переданной параметром реализации TemporalAccessor (предок интерфейса Temporal, который реализуют все рассмотренные в предыдущем подразделе классы java.time). Таким образом, из метода мы получим строковое отражение даты и/или времени в необходимом формате.
3. parse(). Является противоположностью format() и возвращает объекты даты и/или времени (или Temporal, или его наследника, если использовать перегруженный парметризованный метод) на основании переданной строки. Пример такой строки – "2022-12-20 19:51:00". Чаще всего этот метод не вызывается явно, вместо него используются статические методы parse() в классах, объекты которых нужно получить. Внутри этих static-методов и вызывается рассматриваемый метод.
Вместо итога
Рассмотренная сегодня тема является, безусловно, важной и нужной. При этом она крайне проста, за исключением, возможно, работы с форматерами. Однако я рекомендую плотно ознакомиться с методами создания, изменения, сравнения объектов даты и/или времени, по крайней мере в рамках класса LocalDateTime и его составляющих. Нужные методы или однотипны, или одноименны в различных классах, достаточно просты в запоминании, но их незнание может привести к усложнению логики без всякой необходимости. Также рекомендую запомнить, у каких классов объекты будут изменяемы, а у каких – нет. Это тоже несложно, но сэкономит время на исправлении простых ошибок в логике.
Безусловно, пакет java.time шире, чем классы, которые мы рассмотрели. Как минимум, мы не познакомились с интерфейсом TemporalAmount и его наследниками (наиболее известные - Duration и Period).
Это не связано со сложностью использования остальной функциональности, скорее с тем, что необходимость в использовании нерассмотренной части пакета возникает крайне редко. Но прежде чем изобретать собственный велосипед - всегда стоит сначала поискать готовое решение. Поэтому при возникновении нестандартных задач в рамках данной темы рекомендую обратиться, в первую очередь, к документации по пакету.
С теорией на сегодня все!

Переходим к практике. Не вижу особого смысла давать задачи на классы из java.util, поэтому задания направлены на закрепление знаний о java.time. Но вы можете самостоятельно адаптировать задачи под использование Date и Calendar, если считаете необходимым. Я не откажу в их проверке.
Задача 1:
Реализуйте метод, возвращающий объект LocalDateTime на основании переданной строки. Пример входящей строки: 20.12.2022 20:16:00.
Задача 2:
Реализуйте программу, выводящую в консоль сообщение, являются ли введенные с клавиатуры дата и время больше или меньше текущего. Формат вводимых даты и времени рекомендую взять из Задачи 1.
Задача 3:
Реализуйте программу, выводящую в консоль количество времени между двумя введенными с клавиатуры датами и временем в секундах, минутах, часах, днях, месяцах и годах.
Пример ввода:
20.12.2022 20:16:00 28.06.1932 02:51:56
Пример вывода:
Seconds: 2855323444 Minutes: 47588724 Hours: 793145 Days: 33047 Months: 1085 Years: 90 Summary: 90 years, 5 months, 22 days, 17 hours 24 minutes 4 seconds
Примечание: данная задача не предполагает реализацию через наследников интерфейса TemporalAmount. Однако вы можете использовать их для альтернативного решения.
Если что-то непонятно или не получается – welcome в комменты к посту или в лс:)
Канал: https://t.me/+relA0-qlUYAxZjI6
Мой тг: https://t.me/ironicMotherfucker
Дорогу осилит идущий!