Почему январь месяц 0 в календаре Java? - PullRequest
283 голосов
/ 05 декабря 2008

В java.util.Calendar январь определяется как месяц 0, а не как месяц 1. Есть ли для этого какая-либо конкретная причина?

Я видел, как многие люди запутались в этом ...

Ответы [ 16 ]

311 голосов
/ 05 декабря 2008

Это просто часть ужасного беспорядка, который представляет собой API даты / времени Java. Перечисление того, что с ним не так, заняло бы очень много времени (и я уверен, что я не знаю половину проблем). По общему признанию, работать с датами и временем сложно, но все равно.

Сделайте себе одолжение и используйте Joda Time или, возможно, JSR-310 .

РЕДАКТИРОВАТЬ: Что касается причин, почему - как отмечалось в других ответах, это может быть связано со старыми API C или просто с ощущением, что все начинается с 0 ... за исключением того, что дни начинаются с 1, конечно. Я сомневаюсь, что кто-то за пределами исходной команды реализации мог бы действительно изложить причины - но я бы настоятельно призвал читателей не беспокоиться так сильно о , почему были приняты плохие решения, чтобы взглянуть на всю гамму злобности в java.util.Calendar и найди что-нибудь получше.

Единственное замечание, которое является в пользу использования индексов на основе 0, заключается в том, что это облегчает такие вещи, как "массивы имен":

// I "know" there are 12 months
String[] monthNames = new String[12]; // and populate...
String name = monthNames[calendar.get(Calendar.MONTH)];

Конечно, это не сработает, как только вы получите календарь с 13 месяцами ... но, по крайней мере, указанный размер соответствует ожидаемому числу месяцев.

Это не хорошая причина, но это причина ...

РЕДАКТИРОВАТЬ: В качестве комментария просит некоторые идеи о том, что я думаю, что неправильно с Дата / Календарь:

  • Удивительные основы (1900 как годовая база в Date, допустимо для устаревших конструкторов; 0 как месячная база в обоих)
  • Изменчивость - использование неизменяемых типов упрощает работу с действительно эффективными значениями
  • Недостаточный набор типов: приятно иметь Date и Calendar как разные вещи, но разделение "локальных" и "зонированных" значений отсутствует, как и дата / время против даты против времени
  • API, который приводит к некрасивому коду с магическими константами, вместо явно названных методов
  • API, о котором очень сложно рассуждать - все дело в том, когда что-то пересчитывается и т. Д.
  • Использование конструкторов без параметров по умолчанию "now", что приводит к трудному для тестирования коду
  • Реализация Date.toString(), которая всегда использует локальный часовой пояс системы (это смущало многих пользователей переполнения стека до сих пор)
39 голосов
/ 01 августа 2013

Потому что делать математику с месяцами гораздо проще.

1 месяц после декабря - январь, но чтобы понять это обычно, вам нужно взять номер месяца и выполнить математику

12 + 1 = 13 // What month is 13?

Я знаю! Я могу исправить это быстро, используя модуль 12.

(12 + 1) % 12 = 1

Это прекрасно работает в течение 11 месяцев до ноября ...

(11 + 1) % 12 = 0 // What month is 0?

Вы можете сделать все это снова, вычтя 1 перед тем, как добавить месяц, затем сделайте свой модуль и, наконец, снова добавьте 1 ... то есть обойти основную проблему.

((11 - 1 + 1) % 12) + 1 = 12 // Lots of magical numbers!

Теперь давайте подумаем о проблеме с месяцами 0 - 11.

(0 + 1) % 12 = 1 // February
(1 + 1) % 12 = 2 // March
(2 + 1) % 12 = 3 // April
(3 + 1) % 12 = 4 // May
(4 + 1) % 12 = 5 // June
(5 + 1) % 12 = 6 // July
(6 + 1) % 12 = 7 // August
(7 + 1) % 12 = 8 // September
(8 + 1) % 12 = 9 // October
(9 + 1) % 12 = 10 // November
(10 + 1) % 12 = 11 // December
(11 + 1) % 12 = 0 // January

Все месяцы работают одинаково, и обходить не нужно.

35 голосов
/ 05 декабря 2008

C основанные языки копируют C до некоторой степени. Структура tm (определенная в time.h) имеет целочисленное поле tm_mon с (прокомментированным) диапазоном 0-11.

Языки на основе C начинают массивы с индекса 0. Таким образом, это было удобно для вывода строки в массиве названий месяцев с tm_mon в качестве индекса.

22 голосов
/ 24 августа 2011

На это было много ответов, но я все равно выскажу свое мнение по этому вопросу. Причина этого странного поведения, как указано ранее, исходит от POSIX C time.h, где хранятся месяцы в целых числах в диапазоне 0-11. Чтобы объяснить почему, посмотрите на это так; годы и дни считаются числами в разговорной речи, но месяцы имеют свои собственные названия. Так как январь является первым месяцем, он будет сохранен как смещение 0, первый элемент массива. monthname[JANUARY] будет "January". Первый месяц в году является первым элементом массива месяца.

С другой стороны, числа дней, так как они не имеют имен, хранение их в int как 0-30 может привести к путанице, добавить много day+1 инструкций для вывода и, конечно, быть склонным ко многим ошибок.

При этом несоответствие сбивает с толку, особенно в javascript (который также унаследовал эту "особенность"), языке сценариев, где это следует абстрагировать далеко от langague.

TL; DR : потому что месяцы имеют названия, а дни месяца - нет.

12 голосов
/ 05 декабря 2008

В Java 8 есть новый API даты / времени JSR 310 , который является более разумным. Спецификация ведет себя так же, как основной автор JodaTime, и у них много общих концепций и шаблонов.

9 голосов
/ 05 декабря 2008

Возможно, потому что C "struct tm" делает то же самое.

9 голосов
/ 05 декабря 2008

Я бы сказал, лень. Массивы начинаются с 0 (это знают все); месяцы года - это массив, и это заставляет меня поверить, что какой-то инженер в Sun просто не удосужился внести эту маленькую хитрость в код Java.

6 голосов
/ 05 декабря 2008

Потому что программисты помешаны на индексах, основанных на 0. Хорошо, это немного сложнее, чем это: более логично, когда вы работаете с низкоуровневой логикой, чтобы использовать индексирование на основе 0. Но в целом я все равно буду придерживаться своего первого предложения.

4 голосов
/ 28 июня 2016

java.util.Month

Java предоставляет вам другой способ использования индексов на основе 1 в течение нескольких месяцев. Используйте перечисление java.time.Month. Один объект предопределен для каждого из двенадцати месяцев. У них есть номера, присвоенные каждому 1-12 на январь-декабрь; позвоните getValue по номеру.

Используйте Month.JULY (Дает вам 7) вместо Calendar.JULY (дает вам 6).

(import java.time.*;)
4 голосов
/ 05 декабря 2008

Лично я воспринял странность API-интерфейса Java-календаря как признак того, что мне нужно отделиться от григориано-ориентированного мышления и попытаться программировать в этом отношении более агностично. В частности, я снова научился избегать жестко закодированных констант для таких вещей, как месяцы.

Что из следующего является более вероятным?

if (date.getMonth() == 3) out.print("March");

if (date.getMonth() == Calendar.MARCH) out.print("March");

Это иллюстрирует одну вещь, которая немного раздражает меня в Joda Time - это может побудить программистов мыслить в терминах жестко закодированных констант. (Однако немного. Это не так, как если бы Joda заставляла программистов плохо программировать.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...