Typ dla daty tylko w języku C # - dlaczego nie ma typu daty?

108

W naszym projekcie C # mamy potrzebę reprezentowania daty bez czasu. Wiem o istnieniu DateTime, jednak zawiera on również porę dnia. Chcę wyraźnie zaznaczyć, że niektóre zmienne i argumenty metod są oparte na datach . Dlatego nie mogę korzystać z DateTime.Datenieruchomości

Jakie są standardowe podejścia do tego problemu? Z pewnością nie jestem pierwszym, który się z tym spotka? Dlaczego nie ma Dateklasy w C #?

Czy ktoś ma ładną implementację wykorzystującą strukturę i może jakieś metody rozszerzeń w DateTime i może implementując jakieś operatory, takie jak == i <,>?

Carlo V. Dango
źródło
1
Chociaż rozumiem, że chcę wyraźnej, jasnej semantyki, jakie konkretne problemy DateTimepowodują?
Jeff Sternal
15
1 Muszę pamiętać o usunięciu godzin na początku metody. 2 nie mówi dobrze, że działa wyłącznie na randkach. Jest to ważne np. Przy składowaniu i ładowaniu z Db, gdzie wystarczy wąski typ. Programowanie to komunia dla ludzi, a nie komputerów
Carlo V. Dango
6
Chciałem tylko powiedzieć, że brak klasy daty JEST wielką sprawą, a używanie DateTime nie jest dobre. Gdy tylko zapiszesz swoje „daty” jako datę i godzinę, jesteś zakładnikiem problemów z lokalnymi / strefami czasowymi dotyczącymi oszczędności światła dziennego. Wyrzucenie części dotyczącej czasu może spowodować cofnięcie wszystkich dat o jeden dzień, gdy zegary się zmienią (!). Użytkownicy w różnych strefach czasowych zobaczą różne daty, gdy spróbują przekonwertować daty i godziny. Daty są odpowiednie do przedstawiania dokładnych momentów w czasie (wahania z jakiegoś punktu lub czegokolwiek), ale są bardzo nieodpowiednie do reprezentowania abstrakcyjnej daty.
TheMathemagician
3
Później podobne pytanie stackoverflow.com/questions/7167710/… , a Jon Skeet mówi, że powinna istnieć data.
goodeye,
9
Typ danych zawierający tylko datę ma wartość DateTime, ponieważ typ danych całkowitoliczbowych jest dziesiętny. Ci, którzy twierdzą, że nie potrzebujemy daty, ponieważ możesz po prostu wyrzucić część czasu, jest to podobne do stwierdzenia, że ​​nie potrzebujemy liczb całkowitych, ponieważ możemy odrzucić część dziesiętną. Nasz świat ma koncepcję daty, która nie zawiera czasu. 5 marca to nie 5 marca 00:00:00.
Vague

Odpowiedzi:

55

Pozwól mi dodać aktualizację do tego klasycznego pytania:

  • Biblioteka Jona Skeeta Noda Time jest teraz dość dojrzała i zawiera tylko typ daty o nazwie LocalDate. (Lokalny w tym przypadku oznacza po prostu lokalny dla kogoś , niekoniecznie lokalny dla komputera, na którym działa kod).

  • Wywołany typ zawierający tylko datę Dateto proponowany dodatek do platformy .NET Core za pośrednictwem projektu corefxlab . Znajdziesz go w System.Timepakiecie wraz z TimeOfDaytypem i kilkoma metodami rozszerzającymi do istniejących typów.

Przestudiowałem ten problem w znacznym stopniu, więc podzielę się również kilkoma powodami konieczności tego typu:

  1. Istnieje logiczna rozbieżność między wartością zawierającą tylko datę a wartością typu „data północy”.

    • Nie każdy lokalny dzień ma północ w każdej strefie czasowej. Przykład: Brazylijska zmiana czasu wiosennego na czas letni przesuwa zegar z 11:59:59 na 01:00:00.

    • Data-godzina zawsze odnosi się do określonej godziny w ciągu dnia, podczas gdy tylko data może odnosić się do początku dnia, końca dnia lub całego zakresu dnia.

  2. Dołączanie godziny do daty może prowadzić do zmiany daty, ponieważ wartość jest przekazywana z jednego środowiska do drugiego, jeśli strefy czasowe nie są bardzo uważnie obserwowane. Zwykle ma to miejsce w JavaScript (którego Dateobiektem jest tak naprawdę data + godzina), ale może się łatwo zdarzyć również w .NET lub w serializacji, gdy dane są przesyłane między JavaScript a .NET.

  3. Serializacja DateTimez XML lub JSON (i innymi) zawsze będzie zawierać czas, nawet jeśli nie jest to ważne. Jest to bardzo zagmatwane, zwłaszcza biorąc pod uwagę takie rzeczy, jak daty urodzenia i rocznice, gdzie czas nie ma znaczenia.

  4. Z architektonicznego DateTimepunktu widzenia jest obiektem wartości DDD , ale narusza pojedynczą zasadę odpowiedzialnie na kilka sposobów:

    • Został zaprojektowany jako typ daty i godziny, ale często jest używany tylko jako data (ignorowanie godziny) lub tylko jako godzina (ignorowanie daty). ( TimeSpanjest również często używany do określania pory dnia, ale to inny temat).

    • DateTimeKindWartość dołączone do .Kindmajątku dzieli jeden typ do trzech, Unspecifiedrodzaj jest naprawdę oryginalne założenia struktury i powinny być używane w ten sposób. The Utckind Wyrównuje wartości specyficznie z czasem UTC, a Localkind Wyrównuje wartości z lokalnej strefy czasowej środowiska jest.

      Problem z posiadaniem oddzielnej flagi dla rodzaju polega na tym, że za każdym razem, gdy konsumujesz DateTime, powinieneś sprawdzić, .Kindaby zdecydować, jakie zachowanie przyjąć. Wszystkie metody ramowe to robią, ale inni często zapominają. Jest to naprawdę naruszenie SRP, ponieważ typ ma teraz dwa różne powody do zmiany (wartość i rodzaj).

    • Oba z nich prowadzą do kompilacji zastosowań API, ale często są bezsensowne lub mają dziwne przypadki skrajne spowodowane efektami ubocznymi. Rozważać:

      // nonsensical, caused by mixing types
      DateTime dt = DateTime.Today - TimeSpan.FromHours(3);  // when on today??
      
      // strange edge cases, caused by impact of Kind
      var london = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
      var paris = TimeZoneInfo.FindSystemTimeZoneById("Romance Standard Time");
      var dt = new DateTime(2016, 3, 27, 2, 0, 0);  // unspecified kind
      var delta = paris.GetUtcOffset(dt) - london.GetUtcOffset(dt);  // side effect!
      Console.WriteLine(delta.TotalHours); // 0, when should be 1 !!!
      

Podsumowując, chociaż a DateTime może być używane tylko do daty, powinno to robić tylko wtedy, gdy każde miejsce, które go używa, jest bardzo ostrożne, aby zignorować czas, a także bardzo ostrożnie, aby nie próbować konwertować do iz UTC lub innego strefy czasowe.

Matt Johnson-Pint
źródło
2
Gdyby tylko System.Time.Dateskończył we frameworku .NET: /
Robert Jørgensgaard Engdahl
1
Możesz tego użyć dzisiaj, po prostu zasubskrybuj kanał myget corefx i możesz go pobrać System.Timetak jak każdy inny pakiet. Po prostu nie jest to jeszcze „oficjalne”.
Matt Johnson-Pint
16

Podejrzewam, że nie ma dedykowanej czystej Dateklasy, ponieważ już masz, DateTimektóra może sobie z tym poradzić. Posiadanie Dateprowadziłoby do powielania i zamieszania.

Jeśli chcesz zastosować podejście standardowe, spójrz na DateTime.Datewłaściwość, która podaje tylko część daty a DateTimez wartością czasu ustawioną na 12:00:00 północ (00:00:00).

Robert MacLean
źródło
61
Dużą zaletą dedykowanej klasy Date jest to, że nie cierpi z powodu złożoności stref czasowych i czasu letniego.
Dimitri C.
3
@DimitriC. Nie zgadzam się - możesz używać DateTime z UTC i nie cierpisz z powodu wyjaśnionych problemów, a także z DateTime, nawet jeśli chcesz tylko daty, nadal możesz wykonywać obliczenia matematyczne, które obejmują czas (tj. Daj mi datę, jeśli odejmę 20 x 2 godziny od dzisiaj).
Robert MacLean
@Robert MacLean: Dziękujemy za podkreślenie wygody korzystania z UTC DateTimes. Zrobiłem kilka testów i wygląda na to, że DateTimeKind. Nieokreślony działa jak UTC w odniesieniu do odejmowania. Więc rzeczywiście, jeśli będziesz uważać na „rodzaj” DateTime, z którym pracujesz, wszystko ułoży się dobrze.
Dimitri C.
10
Myślenie o UTC i wszystkim, co ma związek ze strefą czasową, to tylko strata energii, ponieważ można tego łatwo uniknąć dzięki oddzielnej klasie Date. I nie widzę pomyłki między datą a datą i czasem.
maulik13
6
Zgadzam się, że C # naprawdę powinien mieć klasę Date. Konwersja stref czasowych jest nie tylko stałym źródłem błędów łodzi podwodnych, ale jest po prostu bolesna w przypadku spraw związanych z dniem roboczym, a nie czasem.
Julian Birch
12

Wysłałem e-mail na adres [email protected] i to jest ich odpowiedź

Marcos, to nie jest dobre miejsce do zadawania takich pytań. Wypróbuj http://stackoverflow.com Krótka odpowiedź jest taka, że ​​potrzebujesz modelu do reprezentowania punktu w czasie, a DateTime to robi, jest to najbardziej przydatny scenariusz w praktyce . Fakt, że ludzie używają dwóch pojęć (daty i godziny) do oznaczania punktów w czasie, jest arbitralny i nie ma sensu je rozdzielać.

Odłączaj tylko wtedy, gdy jest to uzasadnione, nie rób rzeczy tylko po to, by robić rzeczy na ślepo. Pomyśl o tym w ten sposób: jaki masz problem, który można rozwiązać, dzieląc DateTime na Date and Time? A jakie problemy napotkasz, których nie masz teraz? Wskazówka: jeśli spojrzysz na zastosowania DateTime w środowisku .NET: http://referencesource.microsoft.com/#mscorlib/system/datetime.cs#df6b1eba7461813b#references Zobaczysz, że większość z nich jest zwracana z metody. Gdybyśmy nie mieli jednej koncepcji, takiej jak DateTime, musielibyśmy użyć naszych parametrów lub krotek, aby zwrócić parę dat i godzin.

HTH, Kirill Osenkov

W moim e-mailu spytałem, czy to dlatego, że DateTime używa TimeZoneInfo do uzyskania czasu maszyny - we właściwości Now. Powiedziałbym więc, że to dlatego, że „zasady biznesowe” są „zbyt powiązane”, powiedzieli mi to.

MVCDS
źródło
Ten post naprawdę daje wgląd w przemyślenia leżące u podstaw decyzji projektowej o braku wbudowanej klasy dat. Jakie było pytanie, które im wysłałeś? Nie chcę sugerować, że zgadzam się z tą decyzją z powodów wymienionych powyżej @TheMathemagician.
Robert Jørgensgaard Engdahl
@ RobertJørgensgaardEngdahl Niestety nie mam już dostępu do tego konta e-mail. Ale wydaje mi się, że zapytałem ich, dlaczego połączyli razem godzinę i datę w strukturze DateTime. I myślałem, że zgadzam się z TheMathemagician, myślę, że MS przyjęło takie podejście do projektowania, ponieważ jako firma międzynarodowa opłaca się ich potrzebom - a teraz jest za późno, aby to zmienić - podczas gdy dzielenie koncepcji nie.
MVCDS
2
Może stwardnienie rozsiane mogłoby po prostu pójść na całość i wprowadzić SpaceTimeklasę! Hej, według Einsteina, przestrzeń i czas są ze sobą ściśle powiązane, więc nie powinniśmy też musieć ich rozróżniać, prawda? (!!!!!!!!!!!) Jestem trochę nowych do C #, ale muszę powiedzieć, że to pole minowe pochodzących z VB.NET, gdzie jest, po prostu, date, Today(), now, itd. Nie DateTimepoprzedzając śmieci, nie grzebać. (A te średniki i ta rozróżnianie wielkości liter są z pewnością irytujące! Po prostu zastrzel mnie!)
SteveCinq
2
A ich własny SQL Server ma Datetyp, a wynik musi być typu Date- jeśli był to Datetyp wynik oczekiwany jako ciąg bez czasu. Na przykład Delphi mają również Date jako DateTime, ale typinfo jest inny dla Date i DateTime.
user2091150
1
Kirill Osenkov odpowiada na pytanie „Dlaczego nie mieć oddzielnych klas daty i czasu w porównaniu z klasą daty i godziny ?”. Rzeczywista Q został „Może też mieć osobną Data i klas razem?”. Rozumiem, że data i godzina powinny być połączone w jedną klasę dla wielu przypadków użycia koncepcji daty i godziny . Jednak prawdopodobnie istnieje co najmniej tyle samo, jeśli nie więcej, tak samo ważnych przypadków użycia samej koncepcji daty . Oczywiście istnieje również wiele ważnych przypadków użycia pojęcia czasu .
Tom
4

Jeśli chcesz przeprowadzić porównania dat, użyj

yourdatetime.Date;

Jeśli wyświetlasz na ekranie, użyj

yourdatetime.ToShortDateString();
MattP
źródło
Szukałem części .Date.
Brendan Vogt
3

Pozwólcie, że spekuluję: Może to dlatego, że do SQL Server 2008 nie było w SQL typu danych typu Date, więc byłoby to trudne, więc przechowywać go na serwerze SQL? A to przecież produkt Microsoft?

Pleun
źródło
Data i godzina bazy danych różni się od daty i godziny w języku C #. Data i godzina w db nie ma strefy czasowej, więc w rzeczywistości nie odnoszą się do konkretnej chwili. Ale C # wie, że moment jest i przechowuje tiki od epoki UTC.
artsrc
2
dyskusja dotyczy dedykowanej DATY, a nie części daty i godziny, więc nie rozumiem, o co ci chodzi?
Pleun
To nie daje odpowiedzi na pytanie. Aby skrytykować lub poprosić autora o wyjaśnienie, zostaw komentarz pod jego postem.
Barranka
@Barranka - Pytanie zawiera „Dlaczego w języku C # nie ma klasy Date?”
STLDev
2

Kto wie, dlaczego tak jest. W środowisku .NET jest wiele złych decyzji projektowych. Uważam jednak, że jest to dość drobne. Zawsze możesz zignorować część czasu, więc nawet jeśli jakiś kod zdecyduje, że DateTime odwołuje się do czegoś więcej niż tylko daty, kod, który dba o to, powinien zawsze patrzeć tylko na część daty. Alternatywnie możesz utworzyć nowy typ, który reprezentuje tylko datę i użyć funkcji w DateTime, aby wykonać ciężkie podnoszenie (obliczenia).

siride
źródło
1
Naprawdę nie sądzę, żeby to była zła decyzja, czy chcesz użyć tylko daty, czy nie. Nie będę cię negatywnie oceniać, ale to moja opinia.
JonH
Nie sądzę, żebym to dobrze sformułował. Właściwie nie mam z tym większego problemu, jako takiego, chociaż mogłem zobaczyć, że posiadanie dwóch lub trzech typów byłoby bardziej odpowiednie z punktu widzenia abstrakcji / elegancji. Chodziło mi o to, że we frameworku .NET jest wiele rzeczy, które mogą sprawić, że podrapiesz się po głowie i nie warto się nimi denerwować, zwłaszcza biorąc pod uwagę, że ten „problem” jest dość drobny w porównaniu z niektórymi rażącymi decyzjami projektowymi (ogólne ograniczenia).
pan
+1, bo to prawda ... czy to był jedyny (lub największy) problem .NET :-) :-) Ile wersji SQL Server potrzebowało, aby dodać typy DATA i CZAS? I tam były ZNACZNIE bardziej przydatne (przynajmniej ze względów uczciwości)
xanatos
Powinienem też dodać, że myślę, że „wszystko zaczyna się od -100 punktów” to dobry sposób na stworzenie kiepskiej struktury i może to być jedna z rzeczy, które zostały wciągnięte w te śmieci.
pan
2
Właśnie ugryzł mnie ten problem, ponieważ 1 część kodu zaniedbała użycie właściwości .Date i przez to nie została poprawnie porównana. Zdecydowanie uważam, że potrzebny jest typ daty, który nie przechowuje w żadnym momencie, aby uniknąć tego typu błędu
JoelFan
2

Czemu? Możemy tylko spekulować, a to niewiele pomaga w rozwiązaniu problemów inżynieryjnych. Można przypuszczać, że DateTimezawiera wszystkie funkcje, jakie miałaby taka struktura.

Jeśli naprawdę ma to dla Ciebie znaczenie, po prostu zawiń DateTimeswoją własną niezmienną strukturę, która ujawnia tylko datę (lub spójrz na DateTime.Datewłaściwość).

Jason
źródło
2

Oprócz odpowiedzi Roberta masz również DateTime.ToShortDateStringmetodę. Ponadto, jeśli naprawdę chciałbyś mieć obiekt Date, zawsze możesz użyć wzorca Adapter i zawinąć obiekt DateTime, pokazując tylko to, co chcesz (tj. Miesiąc, dzień, rok).

Matt
źródło
2

Zawsze istnieje DateTime.Datenieruchomość, która odcina część czasową DateTime. Może możesz hermetyzować lub zawijać DateTime we własnym typie Date.

A jeśli chodzi o pytanie, cóż, myślę, że będziesz musiał zapytać Andersa Heljsberga.

Mikael Östberg
źródło
1

Ponieważ aby poznać datę, musisz znać czas systemowy (w tickach), który obejmuje czas - więc po co wyrzucać tę informację?

DateTimema Datewłasność, jeśli w ogóle nie zależy Ci na czasie.

Masyw górski
źródło
1

Tak, również System.DateTime jest zapieczętowany. Widziałem, jak niektórzy grają w takie gry, tworząc niestandardową klasę tylko po to, aby uzyskać wartość ciągu czasu, jak wspomniano we wcześniejszych postach, takie jak:

class CustomDate
{
    public DateTime Date { get; set; }
    public bool IsTimeOnly { get; private set; }

    public CustomDate(bool isTimeOnly)
    {
        this.IsTimeOnly = isTimeOnly;
    }

    public string GetValue()
    {
        if (IsTimeOnly)
        {
            return Date.ToShortTimeString();
        }

        else
        {
            return Date.ToString();
        }
    }
}

Może to być niepotrzebne, ponieważ można łatwo wyodrębnić GetShortTimeString ze zwykłego starego typu DateTime bez nowej klasy

Ta01
źródło
0

Jeśli używasz właściwości Date lub Today, aby uzyskać tylko część daty z obiektu DateTime.

DateTime today = DateTime.Today;
DateTime yesterday = DateTime.Now.AddDays(-1).Date;

Wtedy otrzymasz składnik daty tylko z elementem czasu ustawionym na północ.

eph_tagh
źródło
1
to zdecydowanie nie to, czego chciałem
Carlo V. Dango
@Carlo V. Dango: Nie zgadzam się. Myślę, że to jest dokładnie to, czego chciałeś.
pan
1
@Carlo V. Dango: Czego konkretnie szukasz, czego nie pozwalają ci te właściwości?
eph_tagh
5
To całkiem proste: ślad pamięci daty byłby prawdopodobnie tylko o połowę mniejszy niż ślad pamięci DateTime (32 zamiast 64 bitów). Byłbyś pewien, że twój głupi współpracownik nie .AddHours (1) do twojej daty zmieniając ją, ale „zachowując to samo” z punktu widzenia „tylko randka”. Jeśli (z powodu błędu) DateTime jest ustawiona na DateTimeKind.Local, a czas jest znormalizowany do UTC, prawdopodobnie Date ulegnie zmianie (zdarzyło mi się przy użyciu XmlSerialization i źle wykonanej podróży do JSON) ... Czy to wystarczy?
xanatos