Un tipo per Data solo in C #: perché non esiste un tipo di data?


108

Nel nostro progetto C # abbiamo la necessità di rappresentare una data senza un'ora. So dell'esistenza del DateTime, tuttavia incorpora anche un'ora del giorno. Voglio rendere esplicito che alcune variabili e argomenti del metodo sono basati sulla data . Quindi non posso usare la DateTime.Dateproprietà

Quali sono gli approcci standard a questo problema? Sicuramente non sono il primo a incontrarlo? Perché non esiste una Dateclasse in C #?

Qualcuno ha una bella implementazione usando una struttura e forse alcuni metodi di estensione su DateTime e magari implementando alcuni operatori come == e <,>?


1
Pur comprendendo il desiderio di una semantica chiara e esplicita, quali problemi specifici DateTimecrea?
Jeff Sternal

15
1 Devo ricordarmi di rimuovere le ore all'inizio del metodo. 2 non comunica bene che funziona solo nelle date. Questo è importante, ad esempio, quando si memorizza e si carica da Db, dove sarà sufficiente un tipo stretto. La programmazione è comunione per le persone non per i computer
Carlo V. Dango

6
Volevo solo dire che la mancanza di una classe data è un grosso problema e l'utilizzo di DateTime non va affatto bene. Non appena memorizzi le tue "date" come data-ora, sei ostaggio dei problemi di risparmio di locali / fuso orario. Gettare via la parte del tempo può inviare tutte le date indietro di un giorno in cui gli orologi cambiano (!). E gli utenti in fusi orari diversi vedranno date diverse quando provano a convertire le date-ore. Le date / ore vanno bene per rappresentare momenti precisi nel tempo (jiffies da un punto o qualsiasi altra cosa) ma sono molto inadatte per rappresentare una data astratta.
TheMathemagician

3
Una successiva domanda simile stackoverflow.com/questions/7167710/… , e Jon Skeet dice che dovrebbe esserci una data.
arrivederci

9
Un tipo di dati solo data è a DateTime come un tipo di dati intero è a un decimale. Coloro che sostengono che non abbiamo bisogno di una data perché puoi semplicemente buttare via la parte del tempo è come dire che non abbiamo bisogno di numeri interi poiché possiamo buttare via la parte decimale. Il nostro mondo ha un concetto di una data che non include un'ora. Il 5 marzo non è il 5 marzo alle 00:00:00.
Vago

Risposte:


55

Consentitemi di aggiungere un aggiornamento a questa domanda classica:

  • La libreria Noda Time di Jon Skeet è ora abbastanza matura e ha un tipo di sola data chiamato LocalDate. (Locale in questo caso significa solo locale per qualcuno , non necessariamente locale per il computer in cui è in esecuzione il codice.)

  • Un tipo di sola data chiamato Dateè un'aggiunta proposta a .NET Core, tramite il progetto corefxlab . Lo troverai nel System.Timepacchetto, insieme a un TimeOfDaytipo e diversi metodi di estensione ai tipi esistenti.

Ho studiato questo problema in modo significativo, quindi condividerò anche diversi motivi per la necessità di questi tipi:

  1. Esiste una discrepanza logica tra un valore di sola data e un valore di data a mezzanotte.

    • Non tutti i giorni locali hanno una mezzanotte in ogni fuso orario. Esempio: la transizione all'ora legale primaverile del Brasile sposta l'orologio dalle 11:59:59 all'01: 00:00.

    • Una data-ora si riferisce sempre a un'ora specifica all'interno della giornata, mentre una sola data può riferirsi all'inizio della giornata, alla fine della giornata o all'intero intervallo del giorno.

  2. Associare un'ora a una data può portare alla modifica della data quando il valore viene passato da un ambiente a un altro, se i fusi orari non vengono osservati con molta attenzione. Ciò si verifica comunemente in JavaScript (il cui Dateoggetto è in realtà una data + ora), ma può accadere facilmente anche in .NET o nella serializzazione quando i dati vengono passati tra JavaScript e .NET.

  3. Serializzare un DateTimecon XML o JSON (e altri) includerà sempre l'ora, anche se non è importante. Questo è molto confuso, soprattutto considerando cose come date di nascita e anniversari, dove l'ora è irrilevante.

  4. Architettonicamente, DateTimeè un oggetto valore DDD , ma viola il Principio Unico Responsabilmente in diversi modi:

    • È progettato come un tipo data + ora, ma spesso viene utilizzato come solo data (ignorando l'ora) o solo come ora (ignorando la data). ( TimeSpanviene spesso utilizzato anche per l'ora del giorno, ma questo è un altro argomento.)

    • Il DateTimeKindvalore annesso alla .Kindproprietà divide in tre il singolo tipo, Il Unspecifiedtipo è davvero l'intento originale della struttura, e dovrebbe essere usato in questo modo. Il Utctipo allinea il valore in modo specifico con UTC e il Localtipo allinea il valore con il fuso orario locale dell'ambiente.

      Il problema con avere un flag separato per tipo è che ogni volta che consumi un DateTime, dovresti controllare .Kindper decidere quale comportamento adottare. Tutti i metodi del framework lo fanno, ma altri spesso lo dimenticano. Questa è veramente una violazione SRP, poiché il tipo ora ha due diversi motivi per cambiare (il valore e il tipo).

    • I due di questi portano a utilizzi API che si compilano, ma sono spesso privi di senso o hanno strani casi limite causati da effetti collaterali. Tener conto di:

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

In sintesi, mentre a DateTime può essere utilizzato solo per una data, dovrebbe farlo solo quando ogni luogo che lo utilizza è molto attento a ignorare l'ora, ed è anche molto attento a non cercare di convertire da e verso UTC o altro Fusi orari.


2
Se solo System.Time.Datefinisse nel framework .NET: /
Robert Jørgensgaard Engdahl

1
Puoi usarlo oggi, basta iscriverti al feed myget corefx e puoi eseguire il pull System.Timecome qualsiasi altro pacchetto. Semplicemente non è ancora "ufficiale".
Matt Johnson-Pint

16

Sospetto che non ci sia una Datelezione pura dedicata perché hai già DateTimechi può gestirlo. Avere Dateavrebbe portato a duplicazioni e confusione.

Se si desidera l'approccio standard, esaminare la DateTime.Dateproprietà che fornisce solo la parte della data di a DateTimecon il valore dell'ora impostato su 12:00:00 mezzanotte (00:00:00).


61
Un grande vantaggio di una classe Date dedicata è che non soffre delle complessità dei fusi orari e dell'ora legale.
Dimitri C.

3
@DimitriC. Non sono d'accordo: puoi usare DateTime con UTC e non soffri dei problemi spiegati, inoltre con DateTime, anche se vuoi solo date puoi comunque fare matematica che coinvolge il tempo (cioè dammi la data se sottraggo 20 x 2 ore da oggi).
Robert MacLean

@Robert MacLean: Grazie per aver sottolineato la comodità di utilizzare UTC DateTimes. Ho fatto alcuni test e sembra che DateTimeKind.Unspecified agisca come UTC per quanto riguarda le sottrazioni. Quindi, in effetti, se stai attento al "tipo" di DateTimes con cui stai lavorando, tutto andrà bene.
Dimitri C.

10
Dover pensare all'UTC e a qualsiasi cosa abbia a che fare con il fuso orario è solo uno spreco di energia perché può essere facilmente evitato da una classe Date separata. E non vedo alcuna confusione tra Date e DateTime.
maulik13

6
Concordo sul fatto che C # dovrebbe davvero avere una classe Date. Non solo la conversione del fuso orario è una fonte costante di bug sottomarini, ma è semplicemente dolorosa quando si ha a che fare con cose che si basano su una giornata lavorativa piuttosto che sul tempo.
Julian Birch

12

Ho inviato un'e-mail a refsrcfeedback@microsoft.com e questa è la loro risposta

Marcos, questo non è un buon posto per fare domande come queste. Prova http://stackoverflow.com La risposta breve è che hai bisogno di un modello per rappresentare un punto nel tempo, e DateTime lo fa, è lo scenario più utile nella pratica . Il fatto che gli esseri umani usino due concetti (data e ora) per contrassegnare i punti nel tempo è arbitrario e non è utile separarli.

Separare solo dove è garantito, non fare le cose solo per il gusto di fare le cose alla cieca. Pensa in questo modo: quale problema hai risolto dividendo DateTime in Date e Time? E quali problemi avrai che non hai adesso? Suggerimento: se si esaminano gli utilizzi di DateTime nel framework .NET: http://referencesource.microsoft.com/#mscorlib/system/datetime.cs#df6b1eba7461813b#references Vedrai che la maggior parte viene restituita da un metodo. Se non avessimo un unico concetto come DateTime, dovresti usare i parametri out o Tuple per restituire una coppia di Date e Time.

HTH, Kirill Osenkov

Nella mia e-mail mi ero chiesto se fosse perché DateTime usa TimeZoneInfo per ottenere l'ora della macchina - in proprietà Now. Quindi direi che è perché "le regole del business" sono "troppo accoppiate", me lo hanno confermato.


Questo post fornisce davvero approfondimenti sui pensieri dietro la decisione di progettazione di non avere una classe data incorporata. Qual è stata la domanda che hai inviato loro? Non intendo dire che sono d'accordo con questa decisione per i motivi che @TheMathemagician ha elencato sopra.
Robert Jørgensgaard Engdahl

@ RobertJørgensgaardEngdahl purtroppo non ho più accesso a quell'account e-mail. Ma credo di aver chiesto loro perché avevano accoppiato Time e Date insieme nella struttura DateTime. E penso di essere d'accordo con TheMathemagician, penso che MS abbia adottato questo approccio progettuale perché come azienda internazionale ripaga le loro esigenze - e ora è troppo tardi per cambiarlo - mentre la divisione dei concetti non lo fa.
MVCDS

2
Forse MS potrebbe semplicemente andare fino in fondo e implementare una SpaceTimeclasse! Ehi, secondo Einstein, lo spazio e il tempo sono strettamente collegati, quindi non dovremmo nemmeno aver bisogno di differenziarli, giusto? (!!!!!!!!!!!) Sono un po 'nuovo per C #, ma devo dire, è un campo minato proveniente da VB.NET dove è, semplicemente, date, Today(), now, ecc Nessun DateTimerifiuti anteponendo, senza pasticciare. (E questi punti e virgola e questa distinzione tra maiuscole e minuscole sono sicuramente fastidiosi!
Sparami

2
E il proprio SQL Server ha il Datetipo e il risultato deve essere di tipo Date, se era il Daterisultato del tipo previsto come stringa senza tempo. Ad esempio Delphi ha anche Date come DateTime, ma typeinfo è diverso per Date e DateTime.
user2091150

1
Kirill Osenkov sta rispondendo alla domanda di "Perché non avere classi di data e ora separate rispetto alla classe DateTime?". La domanda effettiva era "Perché non avere anche classi di data e ora separate?". Capisco che Data e Ora dovrebbero essere accoppiate in una Classe per i molti casi d'uso concessi del concetto di data-ora . Tuttavia, probabilmente ce ne sono almeno altrettanti, se non di più, altrettanto validi casi d'uso di un semplice concetto di data . E naturalmente ci sono anche molti casi d'uso validi di un concetto di tempo .
Tom


4

Se è necessario eseguire confronti di date, utilizzare

yourdatetime.Date;

Se stai visualizzando sullo schermo usa

yourdatetime.ToShortDateString();

La parte .Date è quello che stavo cercando.
Brendan Vogt

3

Consentitemi di speculare: forse è perché fino a SQL Server 2008 non esisteva alcun tipo di dati Data in SQL, quindi sarebbe stato difficile memorizzarlo in SQL server ?? E dopo tutto è un prodotto Microsoft?


Un datetime db è diverso da un datetime di C #. Un db datetime non ha un fuso orario quindi non si riferiscono effettivamente a un particolare istante. Ma C # sa che l'istante è e memorizza i tick dall'epoca UTC.
artsrc

2
la discussione riguarda la DATA dedicata, non tanto la parte datetime quindi non capisco il punto che stai cercando di fare?
Pleun

Questo non fornisce una risposta alla domanda. Per criticare o richiedere chiarimenti a un autore, lascia un commento sotto il suo post.
Barranka

@Barranka - La domanda contiene "Perché non esiste una classe Date in C #?"
STLDev

2

Chissà perché è così. Ci sono molte decisioni di progettazione sbagliate nel framework .NET. Tuttavia, penso che questo sia piuttosto secondario. Puoi sempre ignorare la parte del tempo, quindi anche se un codice decide di fare riferimento a un DateTime non solo alla data, il codice a cui importa dovrebbe guardare solo la parte della data. In alternativa, è possibile creare un nuovo tipo che rappresenta solo una data e utilizzare le funzioni in DateTime per eseguire il lavoro pesante (calcoli).


1
Non penso davvero che sia stata una cattiva decisione, che tu voglia usare solo una data o meno. Non ti sottovaluterò ma questa è la mia opinione.
JonH

Non credo di averlo formulato bene. In realtà non ho molti problemi con esso, di per sé, anche se ho potuto vedere come avere due o tre tipi sarebbe più appropriato dal punto di vista dell'astrazione / eleganza. Quello che volevo dire era che ci sono molte cose nel framework .NET che potrebbero lasciarti grattarti la testa e non vale la pena arrabbiarsi troppo, soprattutto considerando che questo "problema" è piuttosto minore rispetto ad alcune decisioni di progettazione eclatanti (generiche vincoli).
siride

+1 perché è vero ... era questo l'unico problema (o il più grande) di .NET :-) :-) Di quante versioni di SQL Server avevano bisogno per aggiungere i tipi DATE e TIME? E lì erano MOLTO più utili (almeno per motivi di integrità)
xanatos

Devo anche aggiungere che penso che "tutto inizia a -100 punti" sia un buon modo per creare un quadro povero di piscio e questa potrebbe essere una delle cose che sono rimaste intrappolate in quella spazzatura.
siride

2
Sono stato appena morso da questo problema perché 1 parte del codice ha trascurato di utilizzare la proprietà .Date e quindi non è stata confrontata correttamente. Penso sicuramente che sia necessario un tipo di data che non memorizzi l'ora, per evitare questo tipo di errore
JoelFan

2

Perché? Possiamo solo speculare e non fa molto per aiutare a risolvere i problemi di ingegneria. Una buona ipotesi è che DateTimecontenga tutte le funzionalità che una struttura del genere avrebbe.

Se è davvero importante per te, avvolgi semplicemente DateTimenella tua struttura immutabile che espone solo la data (o guarda la DateTime.Dateproprietà).


2

Oltre alla risposta di Robert hai anche il DateTime.ToShortDateStringmetodo. Inoltre, se si desidera davvero un oggetto Date, è sempre possibile utilizzare il pattern Adapter e racchiudere l'oggetto DateTime esponendo solo ciò che si desidera (cioè mese, giorno, anno).


2

C'è sempre la DateTime.Dateproprietà che interrompe la parte del tempo di DateTime. Forse puoi incapsulare o avvolgere DateTime nel tuo tipo di data.

E per la domanda sul perché, beh, immagino che dovrai chiedere ad Anders Heljsberg.


1

Perché per conoscere la data, devi conoscere l'ora di sistema (in tick), che include l'ora, quindi perché buttare via quelle informazioni?

DateTimeha una Dateproprietà se non ti interessa affatto il tempo.


1

Sì, anche System.DateTime è sigillato. Ho visto alcune persone giocare con questo creando una classe personalizzata solo per ottenere il valore della stringa del tempo come menzionato nei post precedenti, cose come:

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();
        }
    }
}

Questo forse non è necessario, dal momento che potresti facilmente estrarre GetShortTimeString da un semplice vecchio tipo DateTime senza una nuova classe


0

Se si utilizzano le proprietà Date o Today per ottenere solo la parte della data dall'oggetto DateTime.

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

Quindi otterrai il componente data solo con il componente ora impostato a mezzanotte.


1
questo non è sicuramente quello che volevo
Carlo V. Dango

@Carlo V. Dango: non sono d'accordo. Penso che sia esattamente quello che volevi.
siride

1
@Carlo V. Dango: Cosa stai cercando di fare in particolare che queste proprietà non ti permettono di ottenere?
eph_tagh

5
È abbastanza semplice: un footprint di memoria Data sarebbe probabilmente solo la metà del footprint di memoria di un DateTime (32 invece di 64 bit). Saresti sicuro che il tuo stupido collega non l'ha fatto. Aggiungi Ore (1) alla tua data cambiandola ma "mantenendo lo stesso" dal POV di "solo data". Se (per un errore) il DateTime è impostato su DateTimeKind.Local e l'ora è normalizzata su UTC, probabilmente la Data cambierà (mi è successo attraverso l'uso di XmlSerialization e andata e ritorno male fatta a JSON) ... È sufficiente?
xanatos
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.