Perché non esiste un vero tipo di dati "Date-Only"?

23

Sono così ridicolmente frustrato dal dover utilizzare i valori DateTime per i set di dati che sono veramente "solo un giorno". I compleanni sono l'esempio più comune, ma questo si presenta sempre nelle applicazioni aziendali.

Mi sono abituato a impostare solo la parte Time dei record "date-only" su "noon" (che evita che la data cambi mai, indipendentemente dal fuso orario). Questo mi sembra un hack, e per sempre trovo bug da sviluppatori junior che inciampano su questo problema.

Il tempo è sempre relativo a un punto fisso. 4PM è 4 ore dopo il meridiano o mezzogiorno. Il punto più alto del sole in transito è osservabile e ci consente di impostare un sistema di coordinate. 3 ore prima di mezzogiorno (Ante Meridian), 2 ore dopo mezzogiorno, 1441899402938 millisecondi dal 1 ° gennaio 1970. Per le persone cresciute in un mondo cartesiano, questa è una seconda natura.

Ma il nostro calendario precede Descartes. La mia tesi è che è più correttamente pensato come un'enumerazione su cui viene applicata una funzione modulo. Il lunedì segue la domenica, e così via fino ad arrivare al fatto che la domenica segue sabato. Non c'è alcun positivo o negativo, è un modulo, o valore assoluto.

Allo stesso modo con gli anni che si ripetono. Ogni 365 giorni (o giù di lì) ci sono diversi giorni speciali per me: compleanni, anniversari, compleanni dei bambini, ecc. Le applicazioni di programmazione aziendale sono piene di esempi di riunioni ogni sette giorni, primo martedì del mese, ecc. Solo perché possiamo mapparlo su un numero in virgola mobile, e in verità mapparlo su detto numero risolve un sacco di problemi che sono davvero difficili alla vecchia maniera, ma questo non significa che sia l'unico modo per farlo.

La consapevolezza e la comprensione della natura del "piolo quadrato in un buco tondo" di utilizzare DateTimes per memorizzare Date ti rende un programmatore migliore a mio parere.

C'è un valore in un'applicazione esplicitamente intesa come applicazione di pianificazione nella definizione di una classe Date, o è "imposta tutte le volte a mezzogiorno" l'approccio migliore? Quali problemi potrebbero esserci con l'utilizzo di DateTime e l'impostazione del componente Ora su Noon? Lo spostamento del fuso orario può essere considerato in un simile approccio? Ho usato MomentJS, ma penso che sia solo una classe Date migliore.

    
posta Michael Blackburn 10.09.2015 - 16:29
fonte

9 risposte

15

Prima di tutto, prendiamo una cosa fuori mano: i compleanni sono una cosa, le date di nascita sono un'altra. Un compleanno è un tipo di dati esotici perché manca non solo delle componenti di ore, minuti ecc. Ma manca anche della componente dell'anno. Se vuoi davvero trattare i compleanni, ti consiglio di inventare il tuo tipo di dati che non contiene nient'altro che un numero di mese e un numero di giorni e non è correlato a nessuno dei tipi di dati di data e ora incorporati.

D'altra parte, se vuoi anche tenere traccia dell'anno di nascita, allora quello che hai non è un compleanno, è date-of-birth . Quindi, la domanda diventa ora perché non esiste un tipo di dati di sola data, in modo da poter rappresentare comodamente le date di nascita, e invece le lingue popolari sembrano obbligarti a usare un tipo che include anche un componente orario.

Lasciatemi brevemente menzionare che non è vero che i tutti linguaggi di programmazione offrono solo tipi di dati temporali che includono un componente temporale. Mi sono imbattuto in tipi di dati di sola data in RDBMS e nei loro dialetti SQL corrispondenti. Ma questo è irrilevante: il fatto che questi tipi di dati esistano non significa che siano una buona cosa da avere, e gli RDBMS hanno una lunga storia di memorizzazione confusa con la rappresentazione.

Capirai perché è una cattiva idea avere tali tipi di dati solo per la data nel momento in cui ti rendi conto che il tempo è una coordinata. La maggior parte delle persone ha un'idea molto vaga di che tempo è, e questa idea contiene nozioni culturali arcane come anni, mesi e giorni, senza rendersi conto che queste nozioni sono esclusivamente rappresentazionali : sono utili solo per rappresentare il tempo a un umano e ricevere il tempo come input da un umano. In qualsiasi livello al di sotto del controllo effettivo della GUI di immissione del tempo, il tempo dovrebbe essere, e di solito è, rappresentato come una coordinata temporale, che è un numero unico di unità di tempo da qualche origine.

Ad esempio, nel tipo di dati DateTime di Microsoft Dotnet, l'unità di tempo è 100 nanosecondi e l'origine del tempo è 12:00 mezzanotte, 1 gennaio 0001 C.E.

Un altro esempio di notazione arcana, esclusivamente rappresentativa, è la misurazione dell'angolo usando gradi, minuti di grado e secondi di grado. Ovviamente, per ottenere un calcolo utile, devi utilizzare internamente i radianti e, se necessario, convertirli in e da gradi quando interagisci con un utente umano.

Quindi, non confondere la rappresentazione leggibile dall'uomo di una misurazione con la natura effettiva della misurazione. Molto spesso il metodo ideale per realizzare una misurazione, che corrisponde più strettamente alla natura della misurazione, è molto diverso dalla rappresentazione leggibile da una persona di quella misura.

Alla luce di tutto ciò, la tua richiesta di un tipo di dati temporali che rappresenta solo date è simile a una richiesta di un tipo di dati angolare che sarebbe solo in grado di rappresentare gradi, impedendo esplicitamente una maggiore precisione. Un tipo di dati di questo tipo sarebbe piuttosto limitato, e in definitiva inutile, perché dovresti comunque convertirlo da e verso i radianti per ottenere qualcosa di utile da fare con esso.

Il tuo problema con la data di nascita è che hai una coordinata temporale imprecisa : la persona era, naturalmente, nata in un momento specifico, ma l'ora e il minuto erano entrambi non registrati dall'ospedale, o non ci importa di loro. Quindi, ciò che sta accadendo realmente è che la coordinata del tempo di data e ora di nascita ha un margine di errore, una tolleranza o incertezza se lo desideri, ed è meglio trattarlo come tale: metterlo esattamente a metà giornata e considerare un'incertezza implicita di +12 -12 ore. E questa è esattamente la soluzione a cui sei arrivato intuitivamente.

    
risposta data 10.09.2015 - 16:44
fonte
9

Date e orari sono molte cose diverse a seconda del contesto e hai bisogno di molti tipi separati per coprire tutti i casi d'uso.

Il tipo DateTime presente in molte lingue rappresenta un preciso punto temporale ("tempo istantaneo"). Oltre a questo abbiamo un numero di concetti relativi o "umani" e tempi come i giorni di calendario, le date ricorrenti, i mesi, gli anni ecc. Che sono in molti casi ambigui e dipendenti dal contesto. Questi tipi non sono universalmente utili, ma sono necessari in domini applicativi specifici come calendari, strumenti di pianificazione e altre applicazioni che interagiscono con i concetti umani del tempo.

Se stai scrivendo qualcosa come un'app di calendario trarrai sicuramente vantaggio dall'utilizzo di una libreria come Joda-time che fornisce un insieme più ricco di tipi di tempo. Ad esempio LocalDate , una data senza orario. Questo ha una semantica diversa da un normale DateTime con la parte temporale impostata a zero, poiché DateTime indica ancora un punto specifico nel tempo (mezzanotte in un determinato fuso orario), mentre LocalDate indica l'intero giorno e non è legato a un fuso orario specifico. Ciò significa anche che non è possibile tradurre direttamente l'uno con l'altro.

LocalDate è sicuramente più semplice di DateTime perché non deve prendere in considerazione i fusi orari, ma devi essere a conoscenza degli altri problemi, ad es. che la data corrente potrebbe effettivamente andare indietro quando si attraversa un fuso orario e che lo stesso istante di tempo potrebbe corrispondere a date diverse in fusi orari diversi. Se utilizzi le date locali in app in rete o web dovresti stare molto attento a questi problemi. Rimuovere la parte relativa all'ora da una data non risolve il problema fondamentale dei fusi orari! E se prendi in considerazione date storiche e culture diverse diventa ancora più complicato, dal momento che la stessa data può corrispondere a istanze selvaggiamente diverse nel tempo, ad esempio il calendario di Giuliano contro il calendario gregoriano.

Ora chiedi perché le lingue non hanno qualcosa come LocalDate built-in . Prima di tutto, alcuni linguaggi come SQL e Visual Basic hanno un tipo di data senza parte temporale. E Java ha anche aggiunto un LocalDate nella versione recente. Ma altre piattaforme come .Net non lo fanno. Solo i progettisti di linguaggi possono davvero rispondere perché questo non è incluso nella libreria standard, ma la mia ipotesi sarebbe che "il momento istantaneo" è concettualmente semplice e universalmente utile, mentre i concetti dell'altra volta sono utili solo per specifici domini applicativi (come calendari ecc. .). Quindi ha senso lasciare che lo sviluppatore dell'applicazione scriva i tipi personalizzati per gestire i casi d'uso più complessi, o lascia che sia gestito da una libreria di terze parti (come Joda-time).

    
risposta data 14.09.2015 - 17:46
fonte
5

I'm so ridiculously frustrated with having to use DateTime values for data sets that are truly "just a day." Birthdays being the most-common example, but this comes up in business applications all the time.

Ciò è probabilmente dovuto al fatto che il calendario è complicato e utilizzato in così tanti modi diversi che nessuno è stato in grado di capire una classe che sia semplice ma abbastanza generale da essere utile in molti campi.

Il tipo di data che si trova comunemente nei linguaggi di programmazione può essere utilizzato per datare con precisione le transazioni in un sistema informatico. È probabile che altri casi d'uso richiedano una libreria personalizzata.

Ecco una breve lista di fatti sui calendari, che dimostrano la loro complessità, molti dei quali sono storici, quindi se si limita l'attenzione alle date dopo l'1.1.1970, non ne risentirai. Tuttavia, se la tua applicazione ha bisogno di lavorare con date che si verificano prima della fine del 19 ° secolo, allora questi fatti sarebbero importanti. Possibili casi d'uso sono database storici di ogni sorta (libri, genealogia) ma anche attività di grandi aziende o organizzazioni ancora in attività oggi.

Tutti questi fatti sono citati nelle eccellenti Domande frequenti trovate nella libreria del calendario per OCaml scritto di Julien Signolles.

  1. Il calendario giuliano fu introdotto da Giulio Cesare nel 45 aC. Era in uso comune fino al 1500, quando i paesi iniziarono a cambiare Calendario gregoriano (sezione 2.2). Tuttavia, alcuni paesi (per esempio, Grecia e Russia) l'ha usato nel 1900 e gli ortodossi la chiesa in Russia lo usa ancora, così come alcune altre chiese ortodosse.

  2. Il passaggio dal calendario giuliano al calendario gregoriano non si è verificato in modo uniforme e, a seconda dell'anno di modifica, sono trascorsi da 10 a 13 giorni. Ad esempio in Francia il 9 dicembre 1582 fu seguito dal 20 dicembre 1582 e in Grecia il 9 marzo 1924 fu seguito dal 23 marzo 1924.

  3. Anche nell'era moderna, vengono usati molti calendari diversi (gregoriano, ortodosso, islamico e cinese) per citarne alcuni, che usano tutti modi diversi per calcolare anni e anniversari o le celebrazioni religiose della data.

Ora ti auguro un tipo di data in bundle con operazioni utili per le operazioni commerciali generali. Immagino che non ci sia una cosa come le operazioni commerciali generali. Ad esempio, nel mondo finanziario, dobbiamo calcolare:

  1. Le frazioni annuali (come "6 mesi" corrisponde a "0,5"), che vengono utilizzate in combinazione con un tasso di interesse per calcolare l'interesse effettivo su un prestito per un determinato termine. Esistono da 6 a 10 ricette per calcolare queste frazioni, ciascuna diversa nel modo in cui gestiscono la durata di un anno bisestile, la posizione del periodo rispetto all'ultimo giorno di febbraio e la durata di un mese.

  2. Data di rotazione, quando calcoliamo gli anniversari, utilizziamo un calendario aziendale e una regola (scelti da un insieme di oltre 6 diverse regole) per spostare un anniversario da una festività a un giorno lavorativo.

Per le persone che lavorano nel settore finanziario, qualsiasi tipo di calendario che non implementa tutte queste funzioni e regole è inutile. È probabile che molte altre industrie abbiano altri tipi di abitudini e convenzioni che richiedono calcoli personalizzati sul calendario.

Is there value in an application explicitly intended as a scheduling application in defining a Date class, or is "set all times to noon" the best approach? What issues might there be with using DateTime and setting the Time component to Noon? Can timezone shifting be accounted for in such an approach? I've used MomentJS, but I think that's just a better Date class.

Se è necessario tenere traccia di un singolo giorno di calendario, il modo migliore è probabilmente quello di utilizzare un numero intero di grandi dimensioni che rappresenta il giorno giuliano di quel giorno di calendario. Gli algoritmi per convertire avanti e indietro dal giorno giuliano al giorno di calendario - descritti con anno, mese e calendario - sono ampiamente conosciuti e accuratamente testati, in modo da poterli implementare facilmente nella tua applicazione - e capire qual è la regola pertinente caso per calcolare l'anniversario di un evento che si verifica in data 29 febbraio.

    
risposta data 16.09.2015 - 10:25
fonte
2

Penso che Mike Nakis nella sua risposta sopra faccia un lavoro migliore di quello che posso di spiegare come il tempo in generale sia una coordinata assoluta misurata e qualsiasi altra comunicazione, supposto stato o persistenza di quella coordinata temporale sia semplicemente una rappresentazione astratta di detto tempo coordinate.

Parli a tali rappresentazioni quando ti riferisci a Day of the Week come un semplice tipo di modulo di rappresentazione di un punto nel tempo reale. In realtà è un po 'più complicato di così. Se ti è stato assegnato il compito di scrivere una funzione che restituirà il giorno della settimana per un dato punto nel tempo, considera le seguenti informazioni che ti serviranno come input per tale algoritmo. Richiederai il momento, il calendario, il fuso orario da considerare (tieni presente che i fusi orari cambiano TUTTO IL TEMPO quindi devi sapere quando è iniziato il fuso orario effettivo quando termina in coordinate temporali specifiche. per esempio!), e se è in vigore l'ora legale, questo cambia anche nel tempo. Ora considera se ti è stato assegnato un DateTime nel fuso orario locale, devi anche convertirlo in una coordinata temporale per il tuo algoritmo.

Puoi vedere quanto sia davvero complicata questa domanda apparentemente semplice.

Conosco il dolore che provi mentre ero nei tuoi panni a un certo punto risolvendo tutti i bug in un'applicazione di pianificazione delle visite per un prodotto che è stato scritto da sviluppatori inesperti. L'intera faccenda doveva essere demolita.

Il tempo è effettivamente una coordinata, ma oltre a una semplice data, considera altri dati sensibili al tempo che potrebbero essere necessari come:

Durata: un intervallo di millisecondi che potrebbe verificarsi che indica una lunghezza o un passaggio di tempo senza specifiche coordinate temporali specificate. Un caso d'uso potrebbe essere,

As a user, I would like this task to be performed 15 seconds after the completion of the midnight job every other Wednesday.

Intervallo: intervallo di tempo tra due specifiche coordinate temporali. Un caso d'uso in cui potresti prendere in considerazione un intervallo.

As a user, I need to see every Day of the Month wholly contained within a specified interval of time.

Un altro punto rapido che volevo fare è che hai fatto un commento sui numeri in virgola mobile per i dati basati sul tempo e ti sconsiglio di farlo. L'aritmetica a virgola mobile porta inevitabilmente a errori di arrotondamento che potrebbero non fornirti una misurazione precisa quanto necessaria per il tempo.

Quindi, in conclusione, tutte queste informazioni portano inevitabilmente alle seguenti considerazioni progettuali:

  • I punti o gli intervalli temporali specifici devono essere persistenti in UTC o in alcuni tipi di dati che contengano informazioni sufficienti per essere facilmente riassunti in UTC dove necessario
  • La logica dell'applicazione deve essere scritta per formattare l'UTC o un intervallo di coordinate UTC in uno stato dati rappresentativo più comprensibile per l'utente finale.
  • Le durate importanti dovrebbero essere mantenute in millisecondi
  • Le preferenze locali o dell'utente finale sulla visualizzazione di qualsiasi dato relativo al tempo devono essere mantenute come dati aggiuntivi e trattati come un'opzione di visualizzazione. Ad esempio, chiosco A, Fuso orario centrale, Formato ora militare o Preferenze utente B, Ora standard a Tel Aviv (GMT + 7: 00) fuso orario, ecc ...)
  • Evita i numeri FP
risposta data 14.09.2015 - 14:35
fonte
2

In breve, perché la maggior parte dei tipi di tempo basati sul computer sono incentrati sulla gestione corretta del problema del fuso orario e del fuso orario.

Ci sono 2 casi limite che non sono ben serviti con il solito approccio. Impostazione di un punto nel tempo che si trova sull'altro lato di una modifica dell'ora legale utilizzando l'ora locale, che viene quindi convertita in UTC da un livello di astrazione inferiore e quindi ti porta 1 ora in anticipo o in ritardo per la riunione.

L'altro è (secondo la domanda) la modellazione di informazioni sulla data arbitrarie, come la registrazione della data di nascita di una persona. Immagina il caso in cui due persone nascono allo stesso tempo, una in Nuova Zelanda, l'altra in Hawaii. La probabilità è che abbiano date di nascita diverse sui loro passaporti, e se la persona nata alle Hawaii si trasferisce in Nuova Zelanda, sarà considerata un giorno più vecchia della persona nata in Nuova Zelanda, nonostante abbia vissuto per lo stesso tempo.

Il suggerimento nella domanda come impostare la data per avere un orario di mezzogiorno, UTC funzionerà, QUASI ovunque. Gli offset UTC vanno da -12 a +14, quindi ci sono alcuni posti nel Pacifico in cui questo approccio fallirà. Tendo a trattare questi tipi di dati come stringhe, in un formato aaaammgg, e se devo fare calcoli di confronto tra due date, questo può essere fatto tranquillamente come un confronto tra stringhe. Quando si effettuano confronti a delta (ad esempio tra data e ora, o per quanto tempo raggiungono l'età X), è necessario assicurarsi che tutte le date siano create nello stesso offset UTC e quindi utilizzare le funzioni di tempo standard per eseguire il lavoro.

    
risposta data 14.09.2015 - 15:03
fonte
2

Why isn't there a true “Date-Only” data type?

Per gli stessi motivi, penso che i valori DateTime siano generalmente specificati in UTC: semplicità e affidabilità . Il punto di un valore DateTime è specificare un singolo punto nel tempo non influenzato dal fuso orario, dall'ora legale, dal calendario e da altre regolazioni locali. I valori DateTime specificano un istante (fino al limite della risoluzione del tipo), non un periodo di tempo o un insieme di volte. Queste limitazioni consentono di confrontare i valori DateTime in modo affidabile, prevedibile e senza complicazioni.

Cercare di specificare una data con un valore DateTime è come provare a utilizzare un punto per specificare un'area. Puoi farlo funzionare utilizzando una convenzione, ad esempio "questo punto rappresenta il centro di una cerchio con un raggio di 100 m, "ma ci sono molti problemi lì: tutti devono usare la stessa convenzione, è necessario scrivere una serie di codice di supporto per rendere meno doloroso lavorare con il tipo sbagliato, ed è praticamente garantito che a un certo punto dovrai specificare un'area più grande o più piccola dell'area convenzionale. Quindi è con le date: puoi usare "mezzogiorno" come tempo convenzionale per specificare le date, ma poi entri nei fusi orari perché le persone si aspettano di specificare date nell'ora locale piuttosto che in UTC. E anche se trovi un modo soddisfacente per utilizzare un DateTime per specificare una data, avrai bisogno di più informazioni per sapere se si tratta di una data assoluta o relativa: è il 4 luglio 1776 o il 4 luglio? Cosa succede se si desidera ripetere utilizzando un altro periodo? E i calendari hanno tutti i tipi di problemi pazzi: alcuni mesi sono più lunghi di altri, alcuni anni sono più lunghi di altri, alcuni giorni sono anche più lunghi di altri e alcuni calendari presentano delle lacune. Non vorrai risolvere questi problemi solo per giorni interi, perché gli stessi problemi si presentano per periodi più brevi: probabilmente ti piacerebbe essere in grado di scrivere codice che esprima "prendi 1 pillola ogni 4 ore" con prontezza come "il il gruppo si incontra ogni terzo venerdì. "

Quindi, ci sono molte complicazioni nel lavorare con le date. È relativamente semplice (non è un gioco di parole) facile fornire un tipo che specifica un punto nel tempo e lavorare con esso come si farebbe con un numero, ma estremamente difficile fornire un tipo che indirizzi tutti i modi in cui vengono utilizzate le date.

Come altri hanno sottolineato, sono lingue e librerie che forniscono un buon supporto per le date, ed è spesso una buona idea usarle dato che è abbastanza difficile ottenere il codice relativo alla data esattamente corretto.

    
risposta data 14.09.2015 - 21:49
fonte
2

Why isn't there a true “Date-Only” data type?

Ci sono molti tipi di questo tipo in varie librerie per varie lingue. Ce n'è quasi certamente uno per la tua lingua corrente. Il pacchetto Java util aveva un'orribile API per i calcoli temporali, ma l'introduzione del pacchetto java.time ha reso la vita molto migliore. Vedi java.time.LocalDate, contenente un valore di anno-mese-giorno o java.time.MonthDay, contenente solo un numero di mese e giorno.

    
risposta data 14.09.2015 - 22:06
fonte
1

La manipolazione di calendari è uno degli aspetti meno conosciuti dell'informatica. Sono stati scritti interi libri sull'argomento. @MichealBlackburn ha assolutamente ragione nel chiedere un datatype di sola data, che non si risolva in un punto su una timeline, soggetto a reinterpretazione. Storicamente, ci sono state controversie legittime sul significato di una data. Non si deve guardare oltre all'adozione del calendario gregoriano per scoprire quanto può essere complesso. Inoltre, gli anni non sono sempre iniziati il 1 ° gennaio, anche nell'Europa occidentale e nelle sue colonie ( per esempio , l'America britannica e britannica ha iniziato l'anno il 25 marzo).

    
risposta data 15.09.2015 - 03:25
fonte
-2

In risposta a:

Well, I'd honestly like to know why almost every language only has one data type for this.

Il motivo più comune potrebbe essere "perché non è necessario". Se desideri una data che non tenga conto di ore, minuti, secondi, ecc., Devi semplicemente inizializzarla in questo modo:

date = new DateTime(year, month, day, 0, 0, 0);

Se vuoi puoi estendere DateTime da solo:

public class Date extends DateTime {
    ...
    public Date(int year, int month, int day) {
        this(year, month, day, 0, 0, 0);
    }
}

Nota: sto ignorando intenzionalmente l'ora e il fuso orario predefiniti. Non ha importanza per cosa li imposti, a patto che siano uguali per tutti Date s. Puoi creare un caso per UTC. Puoi creare un caso per utilizzare il fuso orario in cui si trova il tuo server - in entrambi i casi non penso che sia importante per rappresentare un valore impreciso come Date . Lo stesso con il tempo predefinito - puoi farlo 0, puoi farlo mezzogiorno. Non importa. Se Facebook mi invia una notifica di compleanno alle 00:01 ma sono nato alle 23:59 a me non interessa molto, e non mi offenderò che siano trascorse più di 12 ore.

Quanto sopra è in Java ma funzionerebbe allo stesso modo in qualsiasi lingua con DateTime ed ereditarietà. Java in realtà ha numerosi modi per risolvere questo problema, e quanto sopra è deprecato (vogliono che tu usi Calendar ora). Ma come altri hanno pubblicato nei commenti, alcune lingue in realtà fanno forniscono una classe Date , presumibilmente proprio per questo motivo.

Con ogni probabilità, ogni implementazione di Date è probabilmente solo un wrapper per DateTime della lingua e azzera il tempo. Altrimenti ti servirebbe un codice duplicato per risolvere problemi come il numero di giorni tra due Date / DateTimes o se due Date s sono uguali (che ne dici di Feb 29 e Mar 1?). Questi tipi di cose vengono generalmente risolti nella classe DateTime . Ha senso riutilizzare lo stesso codice per un Date .

    
risposta data 14.09.2015 - 16:05
fonte

Leggi altre domande sui tag