Co jest nie tak z Java Date & Time API? [Zamknięte]

105

Bardzo często spotykam się z negatywnymi opiniami na temat Javy Datei innych zajęć związanych z datą. Będąc programistą .NET nie mogę w pełni (bez korzystania z nich) zrozumieć, co jest z nimi nie tak.

Czy ktoś może rzucić na to trochę światła?

Anton Gogolev
źródło

Odpowiedzi:

142

Ach, Dateklasa Java . Być może jeden z najlepszych przykładów tego, jak nie robić czegoś w żadnym języku i nigdzie. Gdzie mam zacząć?

Przeczytanie JavaDoc może skłonić nas do pomysłu, że programiści mają naprawdę dobre pomysły. Mówi o różnicy między UTC i GMT , pomimo faktu, że różnica między nimi to w zasadzie sekundy przestępne (co zdarza się dość rzadko ).

Jednak decyzje projektowe naprawdę marnują wszelkie myśli o byciu dobrze zaprojektowanym API. Oto niektóre z ulubionych błędów:

  • Pomimo tego, że został zaprojektowany w ostatniej dekadzie tysiąclecia, ocenia lata jako dwie cyfry od 1900 roku. Istnieją dosłownie miliony obejść wykonujących 1900+ (lub 1900-) w świecie Java w wyniku tej banalnej decyzji.
  • Miesiące są indeksowane zerami, aby zaspokoić spektakularnie niezwykły przypadek posiadania tablicy miesięcy i nie życia z tablicą trzynastoelementową, z których pierwszy zawiera null. W rezultacie mamy 0..11 (dziś jest to 11 miesiąc 109 roku). Istnieje podobna liczba ++ i - w miesiącach w celu konwersji na ciąg.
  • zmienne . W rezultacie za każdym razem, gdy chcesz podać datę wstecz (powiedzmy jako strukturę instancji), musisz zwrócić klon tej daty zamiast samego obiektu daty (ponieważ w przeciwnym razie ludzie mogą mutować twoją strukturę).
  • Calendar, Przeznaczony do „fix” to rzeczywiście czyni te same błędy. Nadal są zmienne.
  • Datereprezentuje a DateTime, ale aby odnieść się do tych w krainie SQL, istnieje inna podklasa java.sql.Date, która reprezentuje jeden dzień (choć bez skojarzonej z nim strefy czasowej).
  • Nie ma TimeZones skojarzonych z a Date, więc zakresy (takie jak „cały dzień”) są często przedstawiane jako północ i północ (często w dowolnej strefie czasowej)

Na koniec warto zauważyć, że sekundy przestępne generalnie korygują się względem dobrego zegara systemowego, który jest aktualizowany za pomocą ntp w ciągu godziny (patrz linki poniżej). Prawdopodobieństwo, że system będzie nadal działał po wprowadzeniu dwóch sekund przestępnych (minimum co sześć miesięcy, praktycznie co kilka lat) jest raczej mało prawdopodobne, zwłaszcza biorąc pod uwagę fakt, że od czasu do czasu trzeba ponownie wdrażać nowe wersje kodu . Nawet użycie dynamicznego języka, który regeneruje klasy lub coś takiego jak silnik WAR, zanieczyszcza przestrzeń klas i ostatecznie zabraknie permgen.

AlBlue
źródło
43

JSR 310 , który zastąpił stare klasy daty i godziny java.time w Javie 8, uzasadnia się w oryginalnym JSR następująco:

2.5 Jakie potrzeby społeczności Java zostanie uwzględnione w proponowanej specyfikacji?

Obecnie Java SE ma dwa oddzielne interfejsy API daty i czasu - java.util.Date i java.util.Calendar. Oba interfejsy API są konsekwentnie opisywane jako trudne w użyciu przez programistów Java na blogach i forach. Warto zauważyć, że oba używają indeksu zerowego przez miesiące, co jest przyczyną wielu błędów. Przez lata kalendarz cierpiał z powodu wielu błędów i problemów z wydajnością, głównie z powodu wewnętrznego przechowywania jego stanu na dwa różne sposoby.

Jeden klasyczny błąd (4639407) uniemożliwił utworzenie pewnych dat w obiekcie kalendarza. Można napisać sekwencję kodu, która mogłaby utworzyć datę w niektórych latach, ale nie w innych, uniemożliwiając niektórym użytkownikom wprowadzenie prawidłowych dat urodzenia. Było to spowodowane tym, że klasa Calendar zezwalała na uzyskanie czasu letniego tylko o jedną godzinę, podczas gdy historycznie było to plus 2 godziny w okresie drugiej wojny światowej. Chociaż ten błąd został już naprawiony, jeśli w jakimś momencie w przyszłości jakiś kraj zdecyduje się na wprowadzenie oszczędności czasu letniego o dodatkowe trzy godziny, to klasa kalendarza ponownie zostanie zerwana.

Obecny interfejs API Java SE również cierpi w środowiskach wielowątkowych. Wiadomo, że niezmienne klasy są z natury bezpieczne dla wątków, ponieważ ich stan nie może się zmienić. Jednak zarówno data, jak i kalendarz są zmienne, co wymaga od programistów jawnego rozważenia klonowania i wątków. Ponadto brak bezpieczeństwa wątków w DateTimeFormat nie jest powszechnie znany i był przyczyną wielu trudnych do wyśledzenia problemów z wątkami.

Oprócz problemów z klasami, które Java SE ma dla daty i godziny, nie ma klas do modelowania innych koncepcji. Daty lub godziny spoza strefy czasowej, czasy trwania, okresy i interwały nie mają reprezentacji klas w języku Java SE. W rezultacie programiści często używają int do reprezentowania czasu, a javadoc określa jednostkę.

Brak kompleksowego modelu daty i czasu powoduje również, że wiele typowych operacji jest trudniejszych niż powinny. Na przykład obliczenie liczby dni między dwiema datami jest obecnie szczególnie trudnym problemem.

Ten JSR zajmie się problemem pełnego modelu daty i czasu, w tym dat i godzin (ze strefami czasowymi i bez), czasów trwania i okresów, interwałów, formatowania i analizowania.

meriton
źródło
28
  • Instancje dat są zmienne , co prawie zawsze jest niewygodne.
  • Mają podwójną naturę. Reprezentują zarówno znacznik czasu, jak i datę kalendarzową. Okazuje się, że jest to problematyczne przy wykonywaniu obliczeń na datach.
  • Liczbowe reprezentacje danych kalendarza są w wielu przypadkach sprzeczne z intuicją. Na przykład: od getMonth()zera, od getYear()1900 (tj. Rok 2009 jest reprezentowany jako 109).
  • Brakuje im wielu funkcji, których oczekujesz od Dateklasy.
jemiołuszka
źródło
13

Współczuję Ci ... jako były programista .NET zadawałem te same pytania, API czasowe w .NET (przedziały czasowe, przeciążenie operatorów) jest bardzo wygodne.

Po pierwsze, aby utworzyć konkretną datę, użyj przestarzałego interfejsu API lub:

Calendar c = Calendar.getInstance();
c.set(2000, 31, 12)

Aby odjąć dzień, w którym robisz złe rzeczy, takie jak

Date firstDate = ...
Calendar c = Calendar.getInstance();
c.setTime(fistDate);
c.add(Calendar.DATE,-1);
Date dayAgo = c.getTime();

albo gorzej

Date d = new Date();
Date d2 = new Date(d.getTime() - 1000*60*60*24);

Aby dowiedzieć się, ile czasu minęło między dwiema datami (w dniach / tygodniach / miesiącach) ... jest jeszcze gorzej

Jednak DateUtils z apache ( org.apache.commons.lang.time.DateUtils) oferują kilka wygodnych metod i ostatnio odkryłem, że używam tylko ich

Jak napisał Brabster, Joda Time jest również dobrą biblioteką zewnętrzną, ale apache wydaje się bardziej „powszechny” niż cokolwiek innego ...

Eran Medan
źródło
Proszę - proszę, pokaż nam, jak to zrobić („dowiedz się, ile czasu minęło między dwiema datami”)!
Anton Gogolev
Żałuję, że nie znałem łatwego sposobu ... w tym lat przestępnych, niespokojnych miesięcy i niestabilnych dni ...
Eran Medan
1
Zobacz także org.apache.commons.lang.time: commons.apache.org/lang//api/org/apache/commons/lang/time/…
trashgod
@AntonGogolev W java.time użyj Periodi Durationklas do obliczania i reprezentowania czasu, który upłynął, odpowiednio w skali lat-miesięcy-dni i godzin-minut-sekund.
Basil Bourque
4

Uważam, że API Date w Javie jest użyteczne, szczerze mówiąc. Większość zagadnień widziałem i słyszałem o odnoszą się do gadatliwości, konieczność zaangażowania wielu klas, aby zrobić coś pożytecznego ( Calendar, Date, DateFormat/ SimpleDateFormat) oraz brak prostych akcesorów jak getDayOfWeek().

Joda Time to szanowany alternatywny interfejs API w Javie, aw sekcji Why Joda Time podaje kilka argumentów wyjaśniających, dlaczego jest to realna alternatywa, która może być interesująca.

brabster
źródło