Java и даты. Эпическая сага. Раньше все ждали, что класс Date из коробки — простые таймстампы, без цирка. Потом Y2K-паника и кривые методы взорвали его, предшественники Oracle впарили Calendar как спасение. Неплохо, но тяжеловесно. Теперь java.time.LocalDate — третий заход, объявленный неуязвимым фиксом. Только вот не совсем.
Это перевернёт всё с ног на голову? Едва ли. Просто новая головная боль вместо старой: теперь каждый раз вычитай единицу для имени месяца. Кто в плюсе? Не ты, корящийся над ошибкой индекса массива в два часа ночи.
Смотрите, я слежу за Java со времён апплетных войн — 20 лет свары Sun, Oracle и Eclipse за руль. Даты всегда были ахиллесовой пятой языка. Часовые пояса пляшут как политики, високосные секунды подкрадываются, каждый апдейт API клянётся усмирить хаос. LocalDate? Современный, неизменяемый, по ISO. На бумаге — супер. Но загляни в Month.getValue() — и цинизм берёт верх.
Почему месяцы в Java LocalDate считаются с 1?
В чём соль. Берёшь LocalDate.now() — допустим, 2026-04-07, как в первоисточнике. Вызываешь getMonth().getValue() — бац: 4 для апреля. Логично? Соответствует ISO 8601, январь — 1. Нетехнари одобрительно кивают. А программисты? Мы с K&R C заточены под нулевую индексацию везде.
Любимый массив имён месяцев — Enero на 0, Febrero на 1 — вдруг не сходится. Индексируешь по getValue()? Попадаешь на Mayo вместо Abril. Классический off-by-one, чума нашей профессии.
Fecha: 2026-04-07 Mes (val): Mayo Mes (ord): Abril
Прямо из кодового демо первоисточника. Запущено 7 апреля — ordinal() (нулевой, индекс 3) выдаёт Abril верно. А getValue()? Принуждает к хаку: MESES[fecha.getMonth().getValue() - 1]. Безобразно. И теперь в каждом новом проекте.
Ordinal() пока держит линию — январь=0, слава богу. Но зачем плодить сущности? Словно на машине новый щиток, где одометр стартует с 1 км. Пассажирам удобно, механикам — ярость.
А не лучше ли считать с 1?
Вот таблица. Адаптирована из оригинала:
| Método | Basado en | Ejemplo | Concepción |
|---|---|---|---|
| getValue() | 1 | Enero == 1 | Intuitiva |
| ordinal() | 0 | Enero == 0 | Costumbre |
В точку. Люди считают январь первым. Дети так учат. Но в коде? localtime() в C пихает tm_mon от 0. Date.getMonth() в JS — ноль, плюс 1 для экрана. Perl, PHP — то же. Даже старый Calendar.get(Calendar.MONTH) — 0-11.
Сломает ли это ваш старый Java-код?
Коротко: при миграции на java.time — да, незаметно. Тот хак с именами месяцев, который все копипастили десятилетиями? Покойся с миром. Отлаживай странные испанские месяцы (или английские аналоги) в логах. Предрекаю всплеск воплей на Stack Overflow к Q2 2025 — команды апгрейдятся под Jakarta EE очередную.
Необычный ракурс, который никто не осветил: эхо войн с Enum.ordinal() в Java 5. Тогда народ бесился, когда enum’ы отказались от числовых значений ради рефлексии. Oracle ничему не научился. Гонятся за “чистотой” — соответствием ISO — ценой прагматичных поломок. Кто в выигрыше? Консультанты Red Hat с аудитом дат. HotSpot JVM от Oracle? Продастся при любом раскладе.
Фикс кода? Пустяки, но повсюду. Каждый туториал, утилитный класс — переписывай. В корпоративной Java, где даты в payroll и compliance, это часы на тарифе. Циничный я говорит: идеально. Java-экономика крутится.
Вспомним историю. struct tm в C99: tm_mon 0-11, печать с +1. JS ECMA-262: явно ноль. Аутсайдеры BASIC с 1? Реликты COBOL, над которыми посмеёмся. Java мог бы держаться де-факто стандарта. Вместо этого “инновация” через интуицию. Для новичков. За счёт ветеранов.
Общая картина: часовые пояса всё ещё отстой
Не расслабляйся. LocalDate игнорирует время — ни часов, ни офсетов. Норм для дней рождений. Но цепляй к ZonedDateTime? Данные поясов отстают, правительства капризничают с DST. tzdb в Java тянет из IANA, но JAR раздувается. Третий заход? Всё так же латание дырявой лодки.
Я прошёл три переделки дат в Java. Каждая “починила” предыдущую, родив пять багов. Прогноз: к Java 25 выйд