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



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



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

631   16  

16 ответов:

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

сделайте себе одолжение и использовать Джода Времени вместо этого, или, возможно,JSR-310.

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

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

EDIT: в качестве комментария запрашивает некоторые идеи о том, что я думаю, неправильно с датой/календарем:

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

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

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

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

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

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

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

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

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

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

в Java 8 есть новый API даты/времени JSR 310 это более разумно. Spec lead-это то же самое, что и основной автор JodaTime, и они разделяют много похожих концепций и шаблонов.

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

вероятно, потому что c" struct tm " делает то же самое.

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

что из нижеследующего, скорее всего, будет правильным?

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

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

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

java.util.Month

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

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

(import java.time.*;)

tl; dr

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

2

подробности

The ответ от Jon скит является правильным.

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

java.time.Month

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

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

получить , теперь режим обслуживания, советует миграцию на java.время.

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

где получить java.время занятий?

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

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

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

Gotchas

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

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

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

ошибка тысячелетия. Ошибки все еще не вышли из классов календаря. Даже в 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 имеет raw 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. Еще Параметра dateformat. разбор ведет себя традиционным способом с января=1.

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

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

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

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

Comments

    Ничего не найдено.