Разбираем подвох getValue() месяца в Java LocalDate

Все надеялись, что третий API дат в Java наконец-то всё починит. А нет — Month.getValue() в LocalDate подкинул разработчикам свежий кошмар с индексами.

Вывод enum Month в Java LocalDate с несоответствием индексации getValue и ordinal

Key Takeaways

  • Month.getValue() в LocalDate нумерует с 1, нарушая традиции нулевой индексации массивов.
  • Держитесь ordinal() для совместимости; иначе getValue() минус 1.
  • API дат в Java эволюционируют, но прагматизм побеждает "интуитивные" новшества.

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 выйд

Priya Sundaram
Written by

Hardware and infrastructure reporter. Tracks GPU wars, chip design, and the compute economy.

Worth sharing?

Get the best AI stories of the week in your inbox — no noise, no spam.

Originally reported by Dev.to