Полезняшки от "Разбора Полетов"
1.46K subscribers
164 photos
17 videos
4 files
4.68K links
Полезные статьи и новости от авторов подкаста "Разбор Полетов". Единственный подкаст, где бывшие ведущие призывают к убийству бывших слушателей 🔥🔥

По всем вопросам @abashev
Download Telegram
Forwarded from javawatch
Обзор на очередную идиотскую статью "Власти отказываются тратить сотни миллионов на проекты на базе Java - CNews".
Не тот обзор, который мы ждали, но которого мы достойны :)

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

Разных дорожных карт - чертово количество, если этому журналисту в руки попадет хотя бы часть, он сможет писать свои статьи до пенсии. В них постоянно кого-то вносят, удаляют, пересматривают. Это нормально.

Сразу же бьет в глаза, что этот журналист не смог даже Jakarta EE и Tomcat без ошибок написать.

Дальше откуда-то вылезает некий Радик Хисметов, который почему-то рассуждет про устаревание Java EE (в то время как ее нет с 2016 года). Этот же Радик противопоставляет Tomcat неким "современным технологиям", не осознавая, что внутри Spring Boot тоже есть Tomcat, а новые версии Jakarta-серверов выпускаются по кд каждый месяц такими маленькими никому не известными компаниями как RedHat и IBM. Или Spring Boot - это недостаточно современная технология? На чем они там пишут, на Helidon 4?

Этот же персонаж не понял, что под "доверенным репозиторием" подразумевается не аналог Nexus, а просто набор библиотек (условно, подмножество Maven Central). Там хромает то ли квалификация, то ли умение читать. Мы все понимаем, что технический директор - это в первую очередь директор, то есть менеджер, он не обязан знать детали работы Maven. Но зачем тогда выдавать себя за эксперта?

Другой эксперт из упомянутой статьи, Рензяев, противопоставил Libercat и Tomcat, то есть противопоставил Томкат Томкату. После этого статью можно было бы закрыть со словами "дальше не читал", но к счастью, на этом статья заканчивается.

Резюме: очередная то ли заказуха, то ли просто безграмотность. Но приятно, что такие заказухи вообще пишут. Libercat стал настолько популярен, что кому-то показалось необходимым его дискредитировать!

https://www.cnews.ru/news/top/2023-12-05_vlasti_otkazyvayutsya_tratit
Несмотря на то, что Java Date Time API появилось еще в Java 8, но до сих пор не утихают споры как его правильно использовать. Если суммировать основные высказывания, то это будет - Везде используем Instant и только его. - Везде используем Local* и только его. - Везде используем Offset* и только его. - Везде используем Zoned* и только его.

Но этот вопрос нельзя рассматривать в статике, как данность, которой все должны следовать. Разработка ПО - это динамический процесс и если мы примем на веру какое-то утверждение, то потеряется смысл, который стоит за ним, и нить рассуждений. А главное - цель, к которой мы хотим дойти при разработке ПО.

Уже довольно давно считается, что самый правильный способ работать со временем в любой системе это использовать timestamp во временной зоне UTC. Во-первых, если есть только одна зона, то не надо ее где-то сохранять. Во-вторых, она непрерывна в прошлое и будущее (с оговорками, но они не относятся к теме), без смены летнего/зимнего времени и т.п. И казалось бы, надо установить на backend серверах зону UTC по умолчанию, в базе данных тоже поставить UTC и везде по коду использовать только Instant.now(), который при любых условиях возвращает timestamp в UTC.

Однако, это всё работает только для новых систем, в которых сервера уже настроены на UTC, весь код мы пишем с нуля и по минимуму используем внешние библиотеки и интеграции. Как правило, системы уже живут на каком-то железе, на котором может быть установлен Pacific или Europe/Moscow, а по коду используется new Date() или System.currentTimestamp(). В этом случае, (new Date()) вам вернет время в таймзоне сервера, а Instant.now() в UTC и вы поймаете кучу головной боли с тестированием и отлавливанием багов. Можно, конечно, использовать Instant.now(systemDefault()), но тогда код становится тяжелее и теряется весь смысл использования Instant.

Поэтому тут на помощь приходит LocalDateTime.now(), который полностью повторяет поведение (new Date()) и позволяет мигрировать код и тесты на новый API с минимальными затратами и при этом получить доступ к вызовам на получение информации об отдельных полях, например getYear(), или для модификации plus*() minus*(), или вызов format(), которых нет у Instant. Избавит ли это вас от проблем при переносе серверов на UTC? Нет, но как минимум, в местах где это важно, вы сможете поменять LocalDateTime.now() на LocalDateTime.now(ZoneId.of(”Europe/Moscow”)) и опять сохранить предыдущее поведение, а потом уже что-то с ним делать.

Кроме того, нельзя забывать про особенность хранения данных внутри Instant. Unix системы и базы данных уже давно перешли на хранение timestamp в области размером 8 байт. При том, что для PostgreSQL точность это одна микросекунда, т.е. 10e-6 секунды. Однако Instant хранит данные в 12 байтах - long используется для хранение секунд, а int для хранения нано секунд от 0 до 999_999_999. Поэтому для того, чтобы сохранить 12 байт в 8 у Instant подрезаются наносекунды до микросекунд и старшие биты у long. И вроде всё пока работало, но тут начинается платформозависимая магия - возможность высчитывать наносекунды зависит от операционной системы, процессора и версии JVM. Поэтому в системах где, например, необходимо проверять правильность записи данных, т.е.

Instant i1 = …..

int id = saveToDb(i1);

Instant i2 = findById(id);

нельзя сделать i1.equals(i2) потому, что потеряются значащие наносекунды при конвертации из данных базы. Т.е. везде надо делать i1.truncatedTo(ChronoUnit.MILLIS).equals(i2.truncatedTo(ChronoUnit.MILLIS)) И кроме того код типа Instant.now().atZone(ZoneOffset.UTC).format(ISO_INSTANT) на одной машине может выдавать 2021-03-10T12:28:19.228816Z, а на другой 2021-03-10T12:28:19.228816800Z при одинаковой версии JVM.

Стоит ли где-то использовать OffsetDateTime - его применение очень ограничено потому что временные зоны привязаны к городам и не учитываются переходы на зимнее и летнее время. Такой тип может быть удобен если вам надо сохранить в базу текущий часовой пояс клиента в определенный момент времени. Но тогда этот объект надо собирать из двух колонок базы данных.
👍102
ZonedDateTime гораздо удобнее, потому что можно привязать клиента по геолокации и следить за изменением зон и переходов на зимнее/летнее время. Но, опять же, использоваться это должно в строго изолированных местах, где обязательно надо сделать расчет в зоне клиента - календари или оповещения, например.

Кроме того не забываем про два дополнительных типа LocalDate и LocalTime которые позволят ограничить объем данных и возможности его интепретации.
3