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 ?
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
iCalendar
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)
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.
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.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.
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.
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.
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.
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.
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.)
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.*;)
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
- The ThreeTenABP projekt dostosowuje ThreeTen-Backport (wymienione powyżej) specjalnie dla Androida.
- Zobacz jak używać....
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 .
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żywaszGregorianCalendar. get(Calendar.MONTH));
GregorianCalendar
ma surowyGregorianCalendar.get(Calendar.ZONE_OFFSET)
oraz the daylight savingsGregorianCalendar. 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
iGregorianCalendar
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.
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
.
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.
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.
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.
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