У java.util.Calendar
січні визначається як місяць 0, а не місяць 1. Чи є якась конкретна причина цього?
Я бачив, як багато людей плутаються з цього приводу ...
У java.util.Calendar
січні визначається як місяць 0, а не місяць 1. Чи є якась конкретна причина цього?
Я бачив, як багато людей плутаються з цього приводу ...
Відповіді:
Це просто частина жахливого безладу, який є API дати / часу Java. Перерахування того, що з цим не так, зайняло б дуже багато часу (і я впевнений, що не знаю половини проблем). Справді, робота з датами та часом - складна, але все-таки сприятлива.
Зробіть собі прихильність і скоріше використовуйте Joda Time , або, можливо, JSR-310 .
EDIT: Що стосується причин, що - як зазначається в інших відповідях, цілком може бути пов'язано зі старими API API або просто загальним відчуттям починати все з 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
як різні речі, але відмежування значень "локальне" від "районованого" відсутнє, як і дата / час проти дати проти часуDate.toString()
Реалізація , яка завжди використовує локальну тимчасову зону системи (це помилка багатьох користувачів переповнення стека до сих пір)Тому що займатися математикою з місяцями набагато простіше.
Через 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
Всі місяці працюють однаково, і обійтися не потрібно.
((11 - 1 + 1) % 12) + 1 = 12
це як раз (11 % 12) + 1
те в протягом декількох місяців 1..12 вам просто потрібно додати 1 після того, як робити по модулю. Ніякої магії не потрібно.
На це було багато відповідей, але я все-таки викладу свою думку з цього приводу. Причина такої дивної поведінки, як було зазначено раніше, походить з POSIX C, time.h
де місяці зберігаються в int з діапазоном 0-11. Щоб пояснити чому, подивіться на це так; роки і дні вважаються числами в розмовній мові, але місяці мають власні назви. Отже, оскільки січень є першим місяцем, він буде зберігатися як зміщення 0, перший елемент масиву. monthname[JANUARY]
було б "January"
. Перший місяць у році є елементом масиву першого місяця.
З іншого боку, числа днів, оскільки вони не мають імен, зберігання їх у int як 0-30 було б заплутаним, додайте багато day+1
інструкцій щодо виведення даних і, звичайно, схильні до багатьох помилок.
Незважаючи на це, непослідовність є заплутаною, особливо у javascript (який також успадкував цю "особливість"), мовою сценаріїв, де це слід абстрагувати далеко від мовної мови.
TL; DR : Тому що місяці мають назви, а дні місяця - ні.
Я б сказав, лінь. Масиви починаються з 0 (всі це знають); місяці року - це масив, що приводить мене до думки, що якийсь інженер в Sun просто не потрудився вкласти цю маленьку нікчемність у код Java.
Можливо, тому, що "stru tm" C робить те саме.
Тому що програмісти одержимі індексами на основі 0. Гаразд, це трохи складніше, ніж це: має більше сенсу, коли ви працюєте з логікою нижчого рівня, використовувати індексацію на основі 0. Але за великим рахунком, я все одно буду дотримуватися свого першого речення.
Особисто я сприйняв дивацтво API календаря Java як ознаку того, що мені потрібно розлучитися з григоріанським мисленням і спробувати запрограмувати більш агрессивно в цьому відношенні. Зокрема, я ще раз навчився уникати жорстких констант для таких речей, як місяці.
Що з перерахованого нижче є більш вірогідним?
if (date.getMonth() == 3) out.print("March");
if (date.getMonth() == Calendar.MARCH) out.print("March");
Це ілюструє одне, що мене трохи дратує про Joda Time - це може спонукати програмістів мислити з точки зору твердих констант. (Хоча лише небагато. Це не так, як Joda змушує програмістів погано програмувати.)
Для мене ніхто не пояснює це краще, ніж mindpro.com :
Gotchas
java.util.GregorianCalendar
має набагато менше помилок та класів, ніж уold java.util.Date
класу, але це все ще немає пікніка.Якби вперше було запропоновано програмістів, коли літній час було запропоновано, вони б наклали вето на це як божевільний і незгасаючий час. У режимі літнього часу спостерігається принципова двозначність. Восени, коли ви повертаєте годинник на одну годину о 2 ранку, є два різні моменти часу, обидва називаються 1:30 ранку за місцевим часом. Ви можете розказати їх окремо, лише якщо записуєте, чи планували ви літній час або звичайний час за читанням.
На жаль, немає способу сказати,
GregorianCalendar
що ви мали намір. Ви повинні вдатися до того, щоб сказати йому місцевий час за допомогою манекена UTC TimeZone, щоб уникнути неоднозначності. Програмісти зазвичай закривають очі на цю проблему і просто сподіваються, що ніхто нічого не робить за цю годину.Тисячоліття клоп. Помилки досі не виходять із класів Календар. Навіть у 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:00 2001/01/01 для MST.
GregorianCalendar
управляється гігантом купи нетипових магічних констант. Ця методика повністю знищує будь-яку надію на перевірку помилок під час компіляції. Наприклад, щоб отримати місяць, який ви використовуєте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. Проте ДатаФормат. розбір поводиться традиційно з січня = 1.
java.util.Month
Java надає вам інший спосіб використання 1 індексів на основі місяців. Використовуйте java.time.Month
enum. Один об’єкт заздалегідь визначений для кожного з дванадцяти місяців. Вони мають номери, призначені кожному 1-12 за січень-грудень; зателефонуйте getValue
за номером.
Скористайтеся Month.JULY
(дає 7) замість Calendar.JULY
(дає 6).
(import java.time.*;)
Month.FEBRUARY.getValue() // February → 2.
2
Відповіді на цей питання Jon тарілочках є правильним.
Зараз у нас є сучасна заміна для тих клопітних старих застарілих класів дати: час java.time .
java.time.Month
Серед цих класів - перелік . Перерахунок переносить один або більше заздалегідь визначених об'єктів, об'єкти, які автоматично інстанціюються під час завантаження класу. На нас є десяток таких об'єктів, кожен дав ім'я: , , , і так далі. Кожен із них - константа класу. Ви можете використовувати та передавати ці об’єкти в будь-якому місці коду. Приклад:Month
Month
JANUARY
FEBRUARY
MARCH
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
);
фев’єр
Крім того, ви повинні передавати об'єкти цього переліку навколо вашої кодової бази, а не просто цілі числа . Це забезпечує безпеку типу, забезпечує дійсний діапазон значень та робить ваш код більш самодокументованим. Дивіться підручник Oracle, якщо ви не знайомі з дивовижним потужним засобом перерахунку на Яві.
Вам також можуть бути корисні заняття Year
та YearMonth
заняття.
Java.time каркас вбудований в Java 8 і пізніших версій. Ці класи витісняти неприємні старі застарілі класи дати і часу , такі як java.util.Date
, .Calendar
, і java.text.SimpleDateFormat
.
Проект Joda-Time , який зараз знаходиться в режимі обслуговування , радить перейти на java.time.
Щоб дізнатися більше, дивіться навчальний посібник Oracle . І шукайте переповнення стека за багатьма прикладами та поясненнями. Специфікація - JSR 310 .
Де отримати класи java.time?
Проект ThreeTen-Extra розширює java.time додатковими класами. Цей проект є передумовою для можливих майбутніх доповнень до java.time. Ви можете знайти деякі корисні класи тут , такі як Interval
, YearWeek
, YearQuarter
, і більш .
Це не точно визначено як нульове саме по собі, воно визначається як Calendar.January. Проблема використання ints як констант замість enums. Календар. Січень == 0.
Тому що писати мовою важче, ніж це виглядає, а зокрема, обробляти час набагато складніше, ніж думає більшість людей. Про невелику частину проблеми (насправді це не Java) дивіться відео YouTube "Проблема з часом та часовими поясами - Computerphile" за адресою адресою https://www.youtube.com/watch?v=-5wpm-gesOY . Не дивуйтеся, якщо ваша голова впаде від сміху в замішанні.
На додаток до відповіді DannySmurf про лінь я додам, що це спонукає вас до використання констант, таких як Calendar.JANUARY
.
Тому що все починається з 0. Це основний факт програмування на Java. Якщо одне від цього відхилиться, то це призвело б до цілковитої плутанини. Не будемо сперечатися утворення їх і кодувати з ними.