Jestem tak śmiesznie sfrustrowany, że muszę używać wartości DateTime dla zestawów danych, które są naprawdę „tylko jeden dzień”. Urodziny są najczęstszym przykładem, ale cały czas pojawia się w aplikacjach biznesowych.
Przyzwyczaiłem się do ustawiania części Time rekordów „tylko data” na „południe” (co pozwala uniknąć zmiany daty bez względu na strefę czasową). To wygląda na włamanie i zawsze znajduję błędy od młodszych deweloperów, które potknęły się o ten problem.
Czas zawsze zależy od stałego punktu. 16.00 to 4 godziny po południku lub w południe. Obserwowany jest najwyższy punkt tranzytowy Słońca i pozwala nam ustawić układ współrzędnych. 3 godziny przed południem (Ante Meridian), 2 godziny po południu, 1441899402938 milisekund od 1 stycznia 1970 r. Dla ludzi wychowanych w kartezjańskim świecie jest to druga natura.
Ale nasz kalendarz poprzedza Kartezjusza. Moim argumentem jest to, że bardziej poprawnie jest traktowany jako wyliczenie, w którym stosowana jest funkcja modulo. Poniedziałek następuje po niedzieli i tak dalej, aż dojdziesz do faktu, że niedziela następuje po sobocie. Nie ma dodatniego i ujemnego, jest to moduł lub wartość bezwzględna.
Podobnie z powtarzającymi się latami. Co 365 dni (lub tak) jest dla mnie kilka specjalnych dni: urodziny, rocznice, urodziny dla dzieci itp. Itp. Aplikacje do planowania spotkań biznesowych są pełne przykładów spotkań co siedem dni, pierwszy wtorek miesiąca itp. Tylko dlatego, że MOŻEMY odwzorować to na liczbę zmiennoprzecinkową, a tak naprawdę odwzorowanie na wspomnianą liczbę rozwiązuje wiele problemów, które są naprawdę trudne w stary sposób, ale to nie znaczy, że jest to jedyny sposób, aby to zrobić.
Świadomość i zrozumienie natury „kwadratowego kołka w okrągłej dziurze” polegającej na używaniu DateTimes do przechowywania Daty sprawia, że moim zdaniem jesteś lepszym programistą.
Czy w aplikacji definiującej klasę Date jest wartość, która jest wyraźnie przeznaczona jako aplikacja do planowania, czy też najlepszym rozwiązaniem jest „ustawienie na cały czas do południa”? Jakie mogą być problemy z użyciem DateTime i ustawieniem składnika Time na Noon? Czy w takim podejściu można uwzględnić przesunięcie strefy czasowej? Użyłem MomentJS, ale myślę, że to po prostu lepsza klasa Date.
źródło
Odpowiedzi:
Przede wszystkim usuńmy jedno: urodziny to jedno, a daty urodzenia to drugie. Urodzin to egzotyczny typ danych, ponieważ brakuje nie tylko składniki godzin, minut itd ale również brakuje składnika rok. Jeśli naprawdę chcesz poradzić sobie z urodzinami, zaleciłbym wymyślenie własnego typu danych, który zawiera tylko numer miesiąca i numer dnia i nie jest powiązany z żadnym z wbudowanych typów danych data-godzina.
Z drugiej strony, jeśli chcesz również śledzić rok urodzenia, to to, co masz, to nie urodziny, to daty urodzenia . Powstaje teraz pytanie, dlaczego nie ma typu danych zawierającego tylko datę, dzięki czemu można wygodnie przedstawiać daty urodzenia, a zamiast tego popularne języki zmuszają do korzystania z niektórych typów, które zawierają również składnik czasu.
Pozwolę sobie krótko wspomnieć, że nie jest prawdą, że wszystkie języki programowania oferują tylko typy danych czasowych, które zawierają składnik czasu. Natknąłem się na typy danych tylko z datą w RDBMS i odpowiadających im dialektach SQL. Ale to nie ma znaczenia: fakt, że te typy danych istnieją, nie oznacza, że warto je mieć, a RDBMS mają długą historię mylenia pamięci z reprezentacją.
Zrozumiesz, dlaczego posiadanie tego typu danych tylko z datą jest kiepskim pomysłem, gdy tylko zorientujesz się, że czas jest współrzędną. Większość ludzi ma bardzo niejasne pojęcie o tym, która jest godzina, i ta idea zawiera tajemnicze pojęcia kulturowe, takie jak lata, miesiące i dni, nie zdając sobie sprawy, że te pojęcia są wyłącznie reprezentacyjne : są one przydatne tylko do reprezentowania czasu człowiekowi i otrzymywania czas jako wkład człowieka. Na dowolnej warstwie poniżej rzeczywistej kontroli GUI wprowadzania czasu, czas powinien być i zwykle jest reprezentowany jako współrzędna czasu, która jest pojedynczą liczbą jednostek czasu od pewnego początku.
Na przykład w
DateTime
typie danych Microsoft Dotnet jednostka czasu wynosi 100 nanosekund, a początek czasu to 12:00 północy, 1 stycznia 0001 CEInnym przykładem tajemnej, wyłącznie reprezentacyjnej notacji są pomiary kąta przy użyciu stopni, minut stopnia i sekund stopnia. Oczywiście, aby wykonać użyteczne obliczenia, musisz wewnętrznie użyć radianów, a jeśli to konieczne, przekonwertować do i od stopni podczas interakcji z ludzkim użytkownikiem.
Nie należy więc mylić czytelnej dla człowieka reprezentacji pomiaru z faktyczną naturą pomiaru. Bardzo często idealna metoda wykonania pomiaru, która najbardziej odpowiada naturze pomiaru, bardzo różni się od czytelnej dla człowieka reprezentacji tego pomiaru.
W świetle tego wszystkiego, twoje żądanie czasowego typu danych reprezentującego tylko daty jest podobne do żądania kątowego typu danych, który byłby w stanie reprezentować tylko stopnie, wyraźnie uniemożliwiając większą precyzję. Taki typ danych byłby dość ograniczony, a ostatecznie bezużyteczny, ponieważ musiałbyś i tak przekonwertować go na iz radianów, aby zrobić z nim cokolwiek pożytecznego.
Twój problem z datą urodzenia polega na tym, że masz nieprecyzyjną współrzędną czasową: osoba ta oczywiście urodziła się w określonym momencie, ale godzina i minuta albo nie zostały zarejestrowane przez szpital, albo nie dbać o nich. Tak naprawdę dzieje się tak, że twoja współrzędna czasu i daty urodzenia ma margines błędu, tolerancję lub niepewność, jeśli chcesz, i najlepiej traktować to w ten sposób: ustaw dokładnie na środku dnia i rozważ domniemaną niepewność +12-12 godzin. I to jest dokładnie rozwiązanie, do którego intuicyjnie dotarłeś.
źródło
Daty i godziny różnią się w zależności od kontekstu i potrzebujesz wielu osobnych typów, aby uwzględnić wszystkie przypadki użycia.
DateTime
Typ występuje w wielu językach przedstawiają dokładny punkt w czasie ( „natychmiastowy czas”). Poza tym mamy wiele względnych lub „ludzkich” koncepcji czasu i przedziału czasowego, takich jak dni kalendarzowe, powtarzające się daty, miesiące, lata itp., Które w wielu przypadkach są niejednoznaczne i zależą od kontekstu. Te typy nie są tak powszechnie przydatne, ale konieczne w określonych domenach aplikacji, takich jak kalendarze, narzędzia do planowania i inne aplikacje, które wchodzą w interakcje z ludzkimi koncepcjami czasu.Jeśli piszesz coś w rodzaju aplikacji kalendarza, na pewno skorzystasz z biblioteki takiej jak Joda-time, która zapewnia bogatszy zestaw typów czasu. Na przykład
LocalDate
data bez godziny. Ma to inną semantykę niż zwykłaDateTime
z częścią czasową ustawioną na zero, ponieważDateTime
nadal wskazuje konkretny punkt w czasie (północ w określonej strefie czasowej), podczas gdyLocalDate
wskazuje cały dzień i nie jest powiązany z określoną strefą czasową. Oznacza to również, że nie możesz bezpośrednio tłumaczyć jednego na drugi.LocalDate
jest z pewnością prostsze niżDateTime
to, że nie musi uwzględniać stref czasowych, ale należy pamiętać o innych kwestiach, np. że bieżąca data może faktycznie cofnąć się, gdy przekraczasz strefę czasową, i że ta sama chwila może odpowiadają różnym datom w różnych strefach czasowych. Jeśli używasz lokalnych dat w aplikacjach sieciowych lub internetowych, powinieneś bardzo uważać na te problemy. Usunięcie części czasu z daty nie rozwiązuje podstawowego problemu stref czasowych! A jeśli weźmie się pod uwagę daty historyczne i różne kultury, staje się to jeszcze trudniejsze, ponieważ ta sama data może odpowiadać niezwykle różnym przypadkom w czasie, na przykład kalendarzowi juliańskiemu i gregoriańskiemu.Teraz pytasz, dlaczego języki nie mają czegoś takiego jak
LocalDate
wbudowane . Po pierwsze, niektóre języki, takie jak SQL i Visual Basic, mają typ daty bez części czasowej. A Java dodała równieżLocalDate
w najnowszej wersji. Ale inne platformy, takie jak .Net, nie. Tylko projektanci języków mogą naprawdę odpowiedzieć, dlaczego nie jest to uwzględnione w standardowej bibliotece, ale domyślam się, że „natychmiastowy czas” jest koncepcyjnie prosty i uniwersalnie użyteczny, podczas gdy inne koncepcje czasowe są przydatne tylko dla określonych domen aplikacji (takich jak kalendarze itp. .). Dlatego warto pozwolić twórcy aplikacji na pisanie niestandardowych typów w celu obsługi bardziej złożonych przypadków użycia lub pozwolić, aby zajmowała się tym biblioteka zewnętrzna (np. Joda-time).źródło
Wynika to prawdopodobnie z faktu, że kalendarz jest skomplikowany i używany na tak wiele różnych sposobów, że nikt nie był w stanie wymyślić klasy, która jest prosta, ale na tyle ogólna, aby była przydatna w wielu dziedzinach.
Typ daty powszechnie spotykany w językach programowania może służyć do dokładnego datowania transakcji w systemie komputerowym. Inne przypadki użycia mogą wymagać niestandardowej biblioteki.
Oto krótka lista faktów na temat kalendarzy, pokazująca ich złożoność - większość z nich ma charakter historyczny, więc jeśli ograniczasz swoją uwagę do dat po 1.1.1970, nie będzie to miało na ciebie wpływu. Jeśli jednak aplikacja musi pracować z datami sprzed końca XIX wieku, fakty te będą miały znaczenie. Możliwe przypadki użycia to wszelkiego rodzaju historyczne bazy danych (książki, genealogia), ale także aktywa dużych firm lub organizacji, które nadal działają.
Wszystkie te fakty są cytowane z doskonałego FAQ znalezionego w bibliotece kalendarza dla OCaml napisanej przez Juliena Signollesa.
Kalendarz juliański został wprowadzony przez Juliusza Cezara w 45 rpne. Był w powszechnym użyciu do 1500 roku, kiedy kraje zaczęły zmieniać kalendarz gregoriański (sekcja 2.2). Jednak niektóre kraje (na przykład Grecja i Rosja) korzystały z niego w latach XX wieku, a cerkiew w Rosji nadal go używa, podobnie jak niektóre inne cerkwie.
Przejście z kalendarza juliańskiego na kalendarz gregoriański nie nastąpiło jednolicie, aw zależności od roku zmiany skrócono 10 do 13 dni. Na przykład we Francji 9 grudnia 1582 r. Nastąpił 20 grudnia 1582 r., Aw Grecji 9 marca 1924 r. Nastąpił 23 marca 1924 r.
Nawet w epoce nowożytnej używa się wielu różnych kalendarzy (gregoriański, prawosławny, islamski i chiński), aby zacytować kilka, z których wszystkie używają różnych sposobów obliczania lat i rocznic lub dat celebracji religijnych.
Teraz masz nadzieję na typ daty w pakiecie z operacjami przydatnymi do ogólnych operacji biznesowych. Myślę, że nie ma czegoś takiego jak ogólne operacje biznesowe. Na przykład w świecie finansów musimy obliczyć:
Ułamki roku (np. „6 miesięcy” odpowiada „0,5”), które są stosowane w połączeniu ze stopą procentową do obliczenia rzeczywistych odsetek od pożyczki na dany okres. Istnieje od 6 do 10 przepisów na obliczenie tych ułamków, z których każdy różni się sposobem obsługi długości roku przestępnego, pozycją okresu w odniesieniu do ostatniego dnia lutego i czasem trwania miesiąca.
Podczas obliczania rocznic przy obliczaniu roczników korzystamy z kalendarza biznesowego i reguły (wybranej z zestawu ponad 6 różnych reguł), aby zmienić rocznicę z wakacji na dzień roboczy.
Dla osób pracujących w branży finansowej każdy typ kalendarza, który nie wdraża wszystkich tych funkcji i zasad, jest bezużyteczny. Jest prawdopodobne, że wiele innych branż ma inne nawyki i konwencje wymagające niestandardowych obliczeń w kalendarzu.
Jeśli chcesz śledzić pojedynczy dzień kalendarzowy, najlepszym sposobem jest prawdopodobnie użycie dużej liczby całkowitej reprezentującej dzień juliański tego dnia kalendarzowego. Algorytmy konwertowania tam iz powrotem z dnia juliańskiego na dzień kalendarzowy - opisane z rokiem, miesiącem i kalendarzem - są powszechnie znane i dokładnie testowane, dzięki czemu można je łatwo wdrożyć w aplikacji - i dowiedzieć się, jaka reguła jest właściwa w twoim przypadek, aby obliczyć rocznicę zdarzenia, które miało miejsce 29. lutego.
źródło
Myślę, że Mike Nakis w swojej powyższej odpowiedzi ma lepszą robotę niż wyjaśnienie, w jaki sposób czas ogólnie jest bezwzględnie mierzoną współrzędną, a jakakolwiek inna komunikacja, przypuszczalny stan lub trwałość tej współrzędnej czasu jest jedynie abstrakcyjnym przedstawieniem wspomnianej współrzędnej czasu.
Mówisz do takich reprezentacji, odnosząc się do Dnia Tygodnia, jako do pewnego rodzaju modułu reprezentacji rzeczywistego punktu w czasie. W rzeczywistości jest to nieco bardziej skomplikowane. Jeśli masz za zadanie napisać funkcję, która zwróci Dzień tygodnia dla danego punktu w czasie, rozważ następujące informacje, które będą potrzebne jako dane wejściowe do takiego algorytmu. Będziesz potrzebował punktu w czasie, kalendarza, strefy czasowej do rozważenia (pamiętaj, że strefy czasowe zmieniają CAŁY CZAS, więc musisz wiedzieć, kiedy ta efektywna strefa czasowa zaczęła się, kiedy skończyła się w określonych współrzędnych czasowych. Korea Północna niedawno zmienił ich na przykład!), a jeśli obowiązuje czas letni, to również zmienia się z czasem. Zastanów się, czy otrzymałeś DateTime w lokalnej strefie czasowej,
Możesz zobaczyć, jak skomplikowane może być to pozornie proste pytanie.
Wiem, że odczuwasz ból, gdy w pewnym momencie byłem w twoich butach, naprawiając wszystkie błędy w aplikacji do planowania wizyt dla produktu napisanego przez niedoświadczonych programistów. Całą rzecz trzeba było złomować.
Czas jest rzeczywiście współrzędną, ale oprócz zwykłej daty, rozważ inne dane wrażliwe na czas, które mogą być potrzebne, takie jak:
Czas trwania: Zakres milisekund, który może wystąpić, który oznacza długość lub upływ czasu bez określonych współrzędnych czasowych. Przypadkiem użycia może być
Interwał: zakres czasu między dwiema konkretnymi współrzędnymi czasu. Przypadek użycia, w którym warto rozważyć przerwę.
Kolejną szybką kwestią, o której chciałem powiedzieć, jest to, że skomentowałeś liczby zmiennoprzecinkowe dla danych opartych na czasie i odradzam to. Arytmetyka zmiennoprzecinkowa nieuchronnie prowadzi do błędów zaokrąglania, które mogą nie dać ci tak dokładnego pomiaru, jak to konieczne do czasu.
Podsumowując, wszystkie te informacje nieuchronnie prowadzą do następujących rozważań projektowych:
źródło
Krótko mówiąc, ponieważ większość typów czasu opartych na komputerze koncentruje się na prawidłowym zarządzaniu czasem i strefą czasową.
Istnieją 2 przypadki skrajne, które nie są dobrze obsługiwane przez zwykłe podejście. Ustawienie punktu czasowego po drugiej stronie zmiany czasu letniego przy użyciu czasu lokalnego, który następnie jest konwertowany w UTC o niższą warstwę abstrakcji, a następnie powoduje godzinę wcześniejszą / późną na spotkanie.
Drugim jest (zgodnie z pytaniem) modelowanie dowolnych informacji o dacie, takich jak rejestracja daty urodzenia osoby. Wyobraź sobie przypadek, w którym dwie osoby rodzą się jednocześnie, jedna w Nowej Zelandii, druga na Hawajach. Prawdopodobnie będą mieli różne daty urodzenia w paszportach, a jeśli osoba urodzona na Hawajach przeprowadzi się do Nowej Zelandii, zostanie uznana za dzień starszą niż osoba urodzona w Nowej Zelandii, mimo że żyła dokładnie w tym samym czasie.
Sugestia zawarta w pytaniu jako ustawienie godziny na południe, UTC będzie działać, PRAWIE wszędzie. Przesunięcia UTC wynoszą od -12 do +14, więc jest kilka miejsc na Pacyfiku, w których takie podejście się nie powiedzie. Zwykle traktuję te typy danych jako ciągi znaków w formacie rrrrmmdd, a jeśli muszę wykonać obliczenia porównań między dwiema datami, można to bezpiecznie zrobić jako porównanie ciągów. Dokonując porównań delta (np. Między datą a teraz lub jak długo osiągają wiek X), musisz upewnić się, że wszystkie daty są tworzone w tym samym przesunięciu UTC, a następnie możesz użyć standardowych funkcji czasu do wykonania pracy.
źródło
Date
typ danych, po prostu przenoszony w aString
Z tych samych powodów, myślę, że wartości DateTime są ogólnie określone w UTC: prostota i niezawodność . Punktem wartości DateTime jest określenie pojedynczego punktu w czasie, na który nie ma wpływu strefa czasowa, czas letni, kalendarz i inne dostosowania lokalne. Wartości DateTime określają jedną chwilę (do limitu rozdzielczości typu), a nie przedział czasu lub zestaw czasów. Ograniczenia te umożliwiają porównywanie wartości DateTime w niezawodny, przewidywalny i nieskomplikowany sposób.
Próba podania daty o wartości DateTime jest jak próba użycia punktu do określenia obszaru.Możesz to zrobić, stosując konwencję, na przykład „ten punkt reprezentuje środek koła o promieniu 100 m”, ale jest tam wiele problemów: wszyscy muszą stosować tę samą konwencję, musisz napisać kilka obsługa kodu, aby praca z niewłaściwym typem była mniej bolesna, i jest prawie pewne, że w pewnym momencie będziesz musiał określić obszar większy lub mniejszy niż obszar konwencjonalny. Tak też jest z datami: możesz użyć „południa” jako tradycyjnego czasu do określania dat, ale potem wchodzisz w strefy czasowe, ponieważ ludzie oczekują określenia dat w czasie lokalnym, a nie UTC. I nawet jeśli wymyślisz zadowalający sposób użycia DateTime do określenia daty, potrzebujesz więcej informacji, aby wiedzieć, czy jest to data bezwzględna czy względna: czy to 4 lipca, 1776 czy co 4 lipca? Co jeśli chcesz powtórzyć używając innego okresu? A kalendarze mają wiele szalonych problemów: niektóre miesiące są dłuższe niż inne, niektóre lata są dłuższe niż inne, niektóre dni są nawet dłuższe niż inne, a niektóre kalendarze mają luki. Nie chciałbyś rozwiązywać tych problemów tylko przez całe dni, ponieważ te same problemy pojawiają się w krótszych okresach: prawdopodobnie chciałbyś być w stanie napisać kod, który wyraża „weź 1 tabletkę co 4 godziny” tak łatwo jak „ grupa spotyka się w każdy trzeci piątek ”.
Praca z datami wiąże się z wieloma komplikacjami. Względnie (bez zamierzonej gry słów) łatwo jest podać typ, który określa punkt w czasie i pracować z nim tak jak w przypadku liczby, ale niezwykle trudno jest podać typ, który odnosi się do wszystkich sposobów używania dat.
Jak inni zwrócili uwagę, że są języki i biblioteki, które zapewniają dobre wsparcie dla dat, a to często dobry pomysł, aby wykorzystać je biorąc pod uwagę to dość trudno dostać kod daty związane dokładnie.
źródło
Istnieje wiele takich typów w różnych bibliotekach dla różnych języków. Jest prawie na pewno jeden dla twojego obecnego języka. Pakiet użytkownika Java miał okropne API do obliczania czasu, ale wprowadzenie pakietu java.time znacznie poprawiło życie. Zobacz java.time.LocalDate, zawierający wartość rok-miesiąc-dzień, lub java.time.MonthDay, zawierający tylko numer miesiąca i dnia.
źródło
Manipulowanie kalendarzem jest jednym z najgorzej rozumianych aspektów informatyki. Na ten temat napisano całe książki. @MichealBlackburn ma absolutną rację, prosząc o typ danych zawierający tylko datę, taki, który nie zostanie rozwiązany do punktu na osi czasu, z zastrzeżeniem ponownej interpretacji. Historycznie istniały uzasadnione spory dotyczące znaczenia daty. Nie trzeba szukać dalej niż przyjęcie kalendarza gregoriańskiego, aby dowiedzieć się, jak skomplikowany może być. Co więcej, lata nie zawsze zaczynały się 1 stycznia, nawet w Europie Zachodniej i jej koloniach ( np. Wielka Brytania i Ameryka Brytyjska rozpoczęły rok 25 marca).
źródło
W odpowiedzi na:
Najczęstszym powodem może być „ponieważ nie jest to konieczne”. Jeśli chcesz, aby data i godzina nie obchodziły godzin, minut, sekund itp., Po prostu zainicjuj ją w następujący sposób:
Jeśli chcesz, możesz sam przedłużyć DateTime:
Uwaga: celowo ignoruję domyślny czas i strefę czasową. Tak naprawdę nie ma znaczenia, co je ustawisz, o ile są takie same dla wszystkich
Date
s. Możesz złożyć skargę na UTC. Możesz uzasadnić wykorzystanie strefy czasowej, w której znajduje się serwer - w każdym razie nie uważam, że jest to ważne dla reprezentowania nieprecyzyjnej wartości, takiej jakDate
. To samo z czasem domyślnym - możesz ustawić 0, możesz ustawić południe. To nie ma znaczenia Jeśli Facebook wyśle mi powiadomienie urodzinowe o 00:01, ale urodziłem się o 23:59, nie obchodzi mnie to i nie obrażę się, że mieli więcej niż 12 godzin wolnego.Powyższe dotyczy języka Java, ale działałoby podobnie w każdym języku z
DateTime
dziedziczeniem. Java ma wiele sposobów rozwiązania tego problemu, a powyższe jest przestarzałe (chcą, abyś używał goCalendar
teraz). Ale jak inni napisali w komentarzach, niektóre języki faktycznie zrobić zapewnićDate
klasę, prawdopodobnie właśnie z tego powodu.Najprawdopodobniej każda
Date
implementacja jest prawdopodobnie tylko opakowaniem dla językaDateTime
i zeruje czas. W przeciwnym razie potrzebujesz zduplikowanego kodu, aby rozwiązać problemy, takie jak liczba dni między dwiema datami / datami lub czy dwaDate
s są równe (co z 29 lutego i 1 marca?). Tego rodzaju rzeczy są zazwyczaj rozwiązywane naDateTime
zajęciach. Sensowne jest ponowne użycie tego samego kodu dlaDate
.źródło
LocalDate
lubDate
typu, a ty musisz „rzucić własny”.new Date(2000, 1, 1);
inew Date(2000, 1, 1);
? Czy możesz powiedzieć, który z nich ma puste godziny, minuty i sekundy pod spodem? Jeśli martwisz się wyświetlaniem dodatkowych funkcji (takich jak setMinutes ()), możesz pójść drogąDate
zawijaniaDateTime
zamiast dziedziczenia po nim, a następnie ujawniasz tylko setYear, setMonth, setDay itp. I na pewno nie mniej poprawna niż data i godzina w bibliotece, której używasz.