Почему январь месяц 0 в Java-календаре?

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

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

Ответ 1

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

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

EDIT: Что касается причин, почему, как отмечено в других ответах, это может быть связано с старыми 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 месяцами... но, по крайней мере, указанный размер - это количество ожидаемых месяцев.

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

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

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

Ответ 2

Потому что математика с месяцами намного проще.

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

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

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

(12 + 1) % 12 = 1

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

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

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

((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

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

Ответ 3

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

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

Ответ 4

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

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

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

TL; DR. Поскольку в месяцах имена и дни месяца этого не делают.

Ответ 5

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

Ответ 6

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

Ответ 7

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

Ответ 8

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

Ответ 9

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

Какое из следующего более вероятно правильное?

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

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

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

Ответ 10

java.util.Month

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

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

(import java.time.*;)

Ответ 11

ТЛ; др

Month.FEBRUARY.getValue()  // February → 2.

2

подробности

Ответ Джона Скита правильный.

Теперь у нас есть современная замена этим проблемным старым классам даты и времени: классы java.time.

java.time.Month

Среди этих классов перечисление Month. Перечисление содержит один или несколько предопределенных объектов - объектов, которые автоматически создаются при загрузке класса. В Month у нас есть дюжина таких объектов, каждому из которых дано имя: JANUARY, FEBRUARY, MARCH и так далее. Каждый из них является static final public константой static final public класса. Вы можете использовать и передавать эти объекты в любом месте вашего кода. Пример: someMethod( Month.AUGUST )

К счастью, у них нормальная нумерация, 1-12, где 1 - январь, а 12 - декабрь.

Получить объект Month для определенного номера месяца (1-12).

Month month = Month.of( 2 );  // 2 → February.

Переход в другую сторону, задать Month объекта его номер месяца.

int monthNumber = Month.FEBRUARY.getValue();  // February → 2.

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

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

String output = 
    Month.FEBRUARY.getDisplayName( 
        TextStyle.FULL , 
        Locale.CANADA_FRENCH 
    );

Février

Кроме того, вы должны передавать объекты этого перечисления вокруг вашей кодовой базы, а не просто целые числа. Это обеспечивает безопасность типов, обеспечивает допустимый диапазон значений и делает ваш код более самодокументируемым. См. Oracle Tutorial, если вы не знакомы с удивительно мощным средством перечисления в Java.

Вы также можете найти полезными классы Year и YearMonth.


О java.time

Инфраструктура java.time встроена в Java 8 и более поздние версии. Эти классы вытеснять неприятные старые устаревшие классы даты и времени, такие как java.util.Date, .Calendar, и java.text.SimpleDateFormat.

Проект Joda-Time, находящийся сейчас в режиме обслуживания, рекомендует перейти на java.time.

Чтобы узнать больше, смотрите Oracle Tutorial. И поиск для многих примеров и объяснений. Спецификация JSR 310.

Где взять классы java.time?

  • Java SE 8 и SE 9 и позже
    • Встроенный.
    • Часть стандартного Java API со встроенной реализацией.
    • Java 9 добавляет некоторые незначительные функции и исправления.
  • Java SE 6 и SE 7
    • Большая часть функциональности java.time перенесена на Java 6 и 7 в ThreeTen-Backport.
  • Android

Проект ThreeTen-Extra расширяет java.time дополнительными классами. Этот проект является полигоном для возможных будущих дополнений к java.time. Здесь вы можете найти некоторые полезные классы, такие как Interval, YearWeek, YearQuarter и другие.

Ответ 12

Для меня никто не объясняет это лучше, чем mindpro.com:

Gotchas

java.util.GregorianCalendar имеет гораздо меньше ошибок и ошибок, чем old java.util.Date, но до сих пор нет пикника.

Если бы были программисты, когда первый день перехода на летнее время предложили, они наложили бы вето на него как на безумного и неразрешимого. С летнее время, существует фундаментальная двусмысленность. Осенью, когда вы устанавливаете часы на один час в 2 часа ночи, есть два разных моментов времени, называемых 1:30 утра по местному времени. Вы можете сказать им кроме того, если вы записываете, планируете ли вы использовать летнее время или стандартное время с чтением.

К сожалению, нет способа сказать GregorianCalendar, который вы предназначена. Вы должны прибегнуть к тому, чтобы сообщать местное время с манекеном UTC TimeZone, чтобы избежать двусмысленности. Программисты обычно закрывают свои глаза на эту проблему и просто надеюсь, что никто ничего не делает во время этого час.

Ошибка тысячелетия. Ошибки все еще не из классов Calendar. Даже в JDK (Java Development Kit) 1.3 есть ошибка 2001 года. Рассматривать следующий код:

GregorianCalendar gc = new GregorianCalendar();
gc.setLenient( false );
/* Bug only manifests if lenient set false */
gc.set( 2001, 1, 1, 1, 0, 0 );
int year = gc.get ( Calendar.YEAR );
/* throws exception */

Ошибка исчезла в 7 утра 2001/01/01 для MST.

GregorianCalendar управляется гигантским кучей нетипизированного int магические константы. Эта техника полностью разрушает любую надежду проверка ошибок компиляции. Например, чтобы получить месяц, который вы используете GregorianCalendar. get(Calendar.MONTH));

GregorianCalendar имеет GregorianCalendar.get(Calendar.ZONE_OFFSET) и летнее время GregorianCalendar. get( Calendar. DST_OFFSET), но нет способа получить используется фактическое смещение часового пояса. Вы должны получить эти два отдельно и добавьте их вместе.

GregorianCalendar.set( year, month, day, hour, minute) не устанавливается секунд до 0.

DateFormat и GregorianCalendar не корректно соединяются. Вы должны дважды укажите календарь, косвенно, как дату.

Если пользователь не настроил свой часовой пояс правильно, он будет по умолчанию тихо или PST или GMT.

В GregorianCalendar месяцы нумеруются начиная с января = 0, а не 1, как это делают все остальные на планете. Но дни начинаются с 1 как и дни недели с воскресеньем = 1, понедельник = 2,... суббота = 7. Все же Формат даты. parse ведет себя традиционным способом с January = 1.

Ответ 13

В дополнение к ответу ланды DannySmurf, я добавлю, что это побуждает вас использовать константы, такие как Calendar.JANUARY.

Ответ 14

Он точно не определен как нуль как таковой, он определен как Calendar.January. Это проблема использования ints как констант, а не перечислений. Calendar.January == 0.

Ответ 15

Потому что написание языка сложнее, чем кажется, а обработка времени, в частности, намного сложнее, чем думает большинство людей. Небольшую часть проблемы (на самом деле это не Java) смотрите в видео YouTube "Проблема с часами и часовыми поясами - компьютерный файл" по адресу https://www.youtube.com/watch?v=-5wpm-gesOY. Не удивляйтесь, если ваша голова отвалится от смеха в замешательстве.

Ответ 16

Потому что все начинается с 0. Это основной факт программирования на Java. Если бы одно было отклоняться от этого, то это привело бы к целому путанице. Не будем спорить о их формировании и кодировать с ними.