Почему январь месяц 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. Если бы одно было отклоняться от этого, то это привело бы к целому путанице. Не будем спорить о их формировании и кодировать с ними.