Dlaczego Styczeń jest miesiącem 0 w kalendarzu Java?

W java.util.Calendar Styczeń jest zdefiniowany jako miesiąc 0, a nie Miesiąc 1. Czy jest jakiś konkretny powód ?

Widziałem, jak wielu ludzi się tym pogubiło...
 261
Author: Stéphane Bonniez, 2008-12-05

16 answers

To tylko część okropnego bałaganu, jakim jest Java date / time API. Wymienienie tego, co jest nie tak, zajęłoby bardzo dużo czasu (i jestem pewien, że nie znam połowy problemów). Co prawda praca z datami i godzinami jest trudna, ale aaargh i tak.

Zrób sobie przysługę i użyj Joda Time zamiast tego, lub ewentualnie JSR-310 .

EDIT: co do powodów - jak zaznaczono w innych odpowiedziach, może to być spowodowane starymi API C, lub po prostu ogólnym poczuciem rozpoczęcia wszystko od 0... z tym, że dni zaczynają się od 1, oczywiście. Wątpię, czy ktokolwiek spoza oryginalnego zespołu wdrożeniowego mógłby naprawdę podać powody - ale ponownie zachęcam czytelników, aby nie martwili się tak bardzo o , dlaczego podjęto złe decyzje, aby spojrzeć na całą gamę złośliwości w java.util.Calendar i znaleźć coś lepszego.

Jednym punktem, który jest za używaniem indeksów opartych na 0 jest to, że ułatwia to takie rzeczy jak "tablice nazw":

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

Oczywiście, to zawiedzie, gdy tylko otrzymasz kalendarz z 13 miesiącami... ale przynajmniej określony rozmiar to liczba miesięcy, których oczekujesz.

To nie jest dobry powód, ale powód...

EDIT: jako komentarz prosi o jakieś pomysły co moim zdaniem jest nie tak z datą/kalendarzem:

    1900 jako baza roku w dacie, co prawda dla przestarzałych konstruktorów; 0 jako baza miesiąca w obu) {33]}
  • zmienność-używanie typów niezmiennych sprawia, że praca z naprawdę efektywnymi wartościami jest znacznie prostsza ]}
  • niewystarczający zestaw typów: miło jest mieć Date i Calendar jako różne rzeczy, jednak brakuje oddzielenia wartości "lokalnych" od "strefowych", podobnie jak data/czas vs Data vs czas
  • W 2007 roku, w ramach projektu, stworzono nową wersję interfejsu API, która umożliwiła tworzenie kodu z magicznymi stałymi, zamiast wyraźnie nazwanych metod.]}
  • API, które jest bardzo trudne do rozumowania - cała sprawa o tym, kiedy rzeczy są rekomputowane etc
  • W 1998 roku, w wyniku połączenia z firmą Caterpillar, Firma caterpillar została założona w 1999 roku.]}
  • implementacja Date.toString(), która zawsze używa systemowej lokalnej strefy czasowej (co wcześniej było mylące dla wielu użytkowników stosu)
 297
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-07 11:04:45

Języki oparte na C kopiują C do pewnego stopnia. Struktura tm (zdefiniowana w time.h) ma pole integer tm_mon z (komentowanym) przedziałem 0-11.

Języki bazujące na C rozpoczynają tablice od indeksu 0. Tak więc było to wygodne do wypisywania ciągu znaków w tablicy nazw miesięcy, z tm_mon jako indeksem.

 34
Author: stesch,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-05 16:35:42

Ponieważ matematyka z miesiącami jest o wiele łatwiejsza.

1 miesiąc po grudniu jest Styczeń, ale aby to rozgryźć normalnie trzeba wziąć numer miesiąca i zrobić matematykę

12 + 1 = 13 // What month is 13?
Wiem! Mogę to szybko naprawić, używając modułu 12.
(12 + 1) % 12 = 1
To działa dobrze przez 11 miesięcy do listopada...
(11 + 1) % 12 = 0 // What month is 0?

Możesz zrobić to wszystko ponownie, odejmując 1 przed dodaniem miesiąca, następnie wykonaj swój moduł i na koniec dodaj 1 z powrotem... aka praca wokół podstawowy problem.

((11 - 1 + 1) % 12) + 1 = 12 // Lots of magical numbers!
Pomyślmy teraz o problemie z miesiącami 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
Wszystkie miesiące działają tak samo, a obejście ich nie jest konieczne.
 33
Author: arucker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-01 19:34:20

Było na to wiele odpowiedzi, ale i tak dam swój pogląd na ten temat. Powód tego dziwnego zachowania, jak wspomniano wcześniej, pochodzi z POSIX C time.h, gdzie miesiące, w których przechowywane są w int z zakresu 0-11. Aby wyjaśnić dlaczego, spójrz na to w ten sposób; lata i dni są uważane za liczby w języku mówionym, ale miesiące mają swoje własne nazwy. Ponieważ Styczeń jest pierwszym miesiącem, będzie on zapisywany jako offset 0, pierwszy element tablicy. monthname[JANUARY] byłoby "January". Pierwszy month in the year jest pierwszym elementem tablicy miesiąca.

Liczby dni z drugiej strony, ponieważ nie mają nazw, przechowywanie ich w int jako 0-30 byłoby mylące, dodać wiele day+1 instrukcji dotyczących wyprowadzania i, oczywiście, być podatnym na wiele błędów.

To powiedziawszy, niespójność jest myląca, szczególnie w javascript (który również odziedziczył tę "funkcję"), języku skryptowym, w którym powinno to być abstrakcyjne z dala od langague.

TL; DR: ponieważ miesiące mają nazwy, a dni miesiąca nie.

 22
Author: piksel bitworks,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-13 18:54:21

W Javie 8 istnieje nowe Api daty/czasu JSR 310 , które jest bardziej rozsądne. Spec lead jest taki sam jak główny autor JodaTime i mają wiele podobnych koncepcji i wzorców.

 10
Author: Alex Miller,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-05 02:10:57

Powiedziałbym lenistwo. Tablice zaczynają się od 0( każdy to wie); miesiące w roku są tablicami, co skłania mnie do przekonania, że jakiś inżynier w Sun po prostu nie zadał sobie trudu, aby umieścić ten jeden mały ładny kod w Javie.

 9
Author: TheSmurf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-05 16:25:13

Prawdopodobnie dlatego, że "struct tm" C robi to samo.

 9
Author: Paul Tomblin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-05 16:37:16

Ponieważ programiści mają obsesję na punkcie indeksów opartych na 0. OK, jest to nieco bardziej skomplikowane: ma to większy sens, gdy pracujesz z logiką niższego poziomu, aby używać indeksowania opartego na 0. Ale i tak zostanę przy pierwszym zdaniu.

 5
Author: Dinah,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-05 16:26:02

Osobiście wziąłem dziwność Java calendar API jako wskazówkę, że muszę oderwać się od myślenia gregoriańskiego i spróbować programować bardziej agnostycznie w tym zakresie. W szczególności, nauczyłem się po raz kolejny unikać twardych stałych przez wiele miesięcy.

Która z poniższych sytuacji jest bardziej prawdopodobna?

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

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

Ilustruje to jedną rzecz, która trochę mnie irytuje czasem Jody - może zachęcić programistów do myślenia w kategoriach stałe zakodowane na twardo. (Tylko trochę. To nie jest tak, że Joda zmusza programistów do złego programowania.)

 4
Author: Paul Brinkley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-05 17:09:57

java.util.Month

Java zapewnia inny sposób korzystania z indeksów opartych na 1 przez wiele miesięcy. Użyj java.time.Month enum. Jeden obiekt jest predefiniowany dla każdego z dwunastu miesięcy. Mają numery przypisane do każdego 1-12 na styczeń-grudzień; zadzwoń getValue za numer.

Wykorzystaj Month.JULY (daje 7) zamiast Calendar.JULY (daje 6).

(import java.time.*;)
 3
Author: Digital_Reality,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-21 02:12:44

Tl;dr

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

2

Szczegóły

Odpowiedź Jona Skeeta jest prawidłowa.

Teraz mamy nowoczesny zamiennik dla tych kłopotliwych starych klas dat-czasu: java.czas klasy.

java.time.Month

Wśród tych klas jest Month enum . Enum przenosi jeden lub więcej predefiniowanych obiektów, Obiektów, które są automatycznie tworzone podczas ładowania klasy. Na Month mamy tuzin takich obiektów, każdy z nich ma swoją nazwę: JANUARY, FEBRUARY, MARCH, i tak dalej. Każda z nich jest static final public stałą klasy. Możesz używać i przekazywać te obiekty w dowolnym miejscu kodu. Przykład: someMethod( Month.AUGUST )

Na szczęście mają rozsądną numerację, 1-12 gdzie 1 to Styczeń, a 12 to Grudzień.

Uzyskaj Month obiekt dla określonego numeru miesiąca (1-12).

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

Idąc w przeciwnym kierunku, zapytaj obiekt Month o jego numer miesiąca.

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

Wiele innych przydatnych metod na tej klasie, np. znając liczbę dni w każdym miesiącu . Klasa może nawet wygenerować lokalną nazwę miesiąca.

Możesz uzyskać zlokalizowaną nazwę miesiąca, w różnych długościach lub skrótach.

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

Février

Ponadto, powinieneś przekazać obiekty tego enum wokół bazy kodu, a nie tylko liczby całkowite . Zapewnia to bezpieczeństwo typu, zapewnia poprawny zakres wartości i sprawia, że kod więcej samokontroli. Zobacz samouczek Oracle jeśli nie znasz zaskakująco potężnego obiektu enum w Javie.

Możesz również znaleźć przydatne Year oraz YearMonth klasy.


O Javie.CZAS

Java.Framework time jest wbudowany w Javę 8 i nowszą. Klasy te zastępują kłopotliwe stare dziedzictwo klasy date-time, takie jak java.util.Date, .Calendar, & java.text.SimpleDateFormat.

Projekt Joda-Time, obecnie w trybie konserwacji , radzi migrację do Javy.czas.

Aby dowiedzieć się więcej, zobacz samouczek Oracle. I wyszukaj przepełnienie stosu dla wielu przykładów i wyjaśnień. Specyfikacja to JSR 310 .

Skąd pobrać Javę.zajęcia czasowe?

  • Java SE 8 oraz SE 9 i Później
    • Wbudowany.
    • część standardowego API Javy z dołączoną implementacją.
    • Java 9 dodaje kilka drobnych funkcji i poprawek.
  • Java SE 6 oraz SE 7
    • Większość Javy.funkcja czasu powraca-przeportowana do Javy 6 i 7 w ThreeTen-Backport.
  • Android

Projekt ThreeTen-Extra rozszerza Javę.czas z dodatkowymi zajęciami. Ten projekt jest poligonem dla potencjalnych przyszłych dodatków do Javy.czas. Możesz znaleźć tutaj kilka przydatnych klas, takich jak Interval, YearWeek, YearQuarter, i więcej .

 2
Author: Basil Bourque,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-26 01:59:06

Dla mnie nikt nie wyjaśnia tego lepiej niż mindpro.com :

Gotchas

java.util.GregorianCalendar ma znacznie mniej błędów i gotchas niż Klasą, ale to wciąż nie jest piknik.

Gdyby byli Programiści, gdy czas letni był pierwszy zaproponowane, zawetowaliby to jako obłąkane i nieustępliwe. Z czas letni, istnieje fundamentalna dwuznaczność. Jesienią, kiedy ustawiasz zegary z powrotem o godzinę o 2 w nocy są dwa różne momenty w czasie obu nazwane 1: 30 am czasu lokalnego. Możesz im powiedzieć osobno tylko wtedy, gdy zapisujesz, czy zamierzałeś standardowy czas z odczytem.

Niestety, nie ma sposobu, aby powiedzieć GregorianCalendar które zamierzony. Musisz uciekać się do mówienia tego czasu lokalnego z manekinem Strefa czasowa UTC, aby uniknąć niejednoznaczności. Programiści Zwykle zamykają swoje oczy na ten problem i mam nadzieję, że nikt nic nie zrobi podczas tego godzina.

Millennium bug. Błędy nadal nie są poza klasami kalendarza. Nawet w JDK (Java Development Kit) 1.3 jest błąd 2001. Rozważ następujący kod:

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 */

Błąd znika o 7 rano w dniu 2001/01/01 dla MST.

GregorianCalendar jest kontrolowany przez olbrzyma stosu Nietykalnych int magiczne stałe. Ta technika całkowicie niszczy wszelką nadzieję na sprawdzanie błędów w czasie kompilacji. Na przykład, aby uzyskać miesiąc, którego używasz GregorianCalendar. get(Calendar.MONTH));

GregorianCalendar ma surowy GregorianCalendar.get(Calendar.ZONE_OFFSET) oraz the daylight savings GregorianCalendar. get( Calendar. DST_OFFSET), ale nie sposób uzyskać używane jest rzeczywiste przesunięcie strefy czasowej. Musisz dostać te dwa oddzielnie i dodać je razem.

GregorianCalendar.set( year, month, day, hour, minute) nie ustawia sekundy do 0.

DateFormat i GregorianCalendar nie zazębiają się prawidłowo. Musisz określ Kalendarz dwa razy, raz pośrednio jako datę.

Jeśli Użytkownik nie skonfigurował poprawnie swojej strefy czasowej, domyślnie po cichu do PST lub GMT.

W GregorianCalendar miesiące są numerowane od stycznia=0, zamiast 1 Jak wszyscy na planecie. Jednak dni zaczynają się od 1 podobnie jak dni tygodnia z Sunday=1, Monday=2, ... Saturday = 7. Jeszcze DateFormat = yyyy parse zachowuje się w tradycyjny sposób z January=1.

 1
Author: Edwin Dalorzo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-29 14:26:18

Oprócz odpowiedzi DannySmurf na lenistwo, dodam, że ma zachęcić do używania stałych, takich jak Calendar.JANUARY.

 0
Author: Powerlord,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-05 16:27:01

Nie jest dokładnie zdefiniowany jako zero per se, jest zdefiniowany jako kalendarz.Styczeń. Jest to problem użycia ints jako stałych zamiast enum. Kalendarz.Styczeń = = 0.

 0
Author: Pål GD,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-06 14:32:11

Ponieważ pisanie w języku jest trudniejsze niż się wydaje, a obsługa czasu w szczególności jest o wiele trudniejsza niż większość ludzi myśli. Aby uzyskać niewielką część problemu (w rzeczywistości, nie Java), zobacz wideo na YouTube "Problem z czasem i strefami czasowymi-Computerphile" na https://www.youtube.com/watch?v=-5wpm-gesOY . nie zdziw się, jeśli Twoja głowa odpadnie od śmiechu w zamieszaniu.

 0
Author: Tihamer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-09 02:16:30

Ponieważ wszystko zaczyna się od 0. Jest to podstawowy fakt programowania w Javie. Gdyby jedna rzecz odbiegała od tej, To doprowadziłoby to do całego zamieszania. Nie kłóćmy się o ich tworzenie i kodowanie z nimi.

 -2
Author: Syrrus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-02 03:10:44