Come modellare le date parziali in Python? Come un anno sconosciuto o un giorno sconosciuto del mese?


11

Voglio essere in grado di catturare fatti come Bob was born in 2000e Bill's birthday is May 7th.

In entrambi gli esempi conosciamo solo una parte della data di nascita della persona. In un caso conosciamo solo l'anno; nell'altro caso conosciamo il mese e il giorno, ma non l'anno.

Come acquisisco queste informazioni?

Alcuni esempi di come potrebbe funzionare:

Immagina una libreria come datetime che ha permesso a None nei campi di rappresentare incognite. Potrei avere un codice come il seguente:

date_a = date(2000, 5, None)
date_b = date(2000, 6, None)
difference = date_b - date_a
assert difference.min.days == 1
assert difference.max.days == 60  # Or something close to 60.
assert equal(date_a, date_b) == False

date_c = date(2000, 5, None)
assert equal(date_a, date_c) == Maybe

Questo è solo un esempio di come potrebbe comportarsi. Non voglio necessariamente questo comportamento preciso.


In generale, il modo in cui gestisci cose come questa è utilizzare, ad esempio, l'anno 0001 in .NET per le date che non hanno un anno e il 1 ° gennaio per anni senza un mese e un giorno.
Robert Harvey,

Ho modificato la tua domanda per rimuovere le richieste per una biblioteca. Tali domande sono fuori tema su questo sito.

@RobertHarvey Non posso usare il tuo suggerimento. Se vediamo che Bob è nato il 1 gennaio 2000, non sappiamo cosa significhi esattamente. Non possiamo dire se è nato il primo giorno del 2000 o se è nato in qualsiasi giorno del 2000. Dobbiamo conoscere la differenza.
Buttons840,

@RobertHarvey So che è comune, ma ho visto molti errori negativi a causa di scelte sbagliate di tali valori di segnale. (Inoltre, non credo che risponda alla domanda poiché l'OP deve affrontare solo alcune date sconosciute. L'impostazione al 1 ° gennaio in questi casi non consente di differenziare le date reali del 1 ° gennaio da incognite.
Gort the Robot

5
@ Buttons840: Quindi dovrai scrivere una classe che incapsuli i comportamenti che desideri. Dovresti racchiudere la classe di date esistente e aggiungere i comportamenti desiderati.
Robert Harvey,

Risposte:


3

Prima di tutto, una volta che inizi a decomporre le date nei loro componenti costitutivi, non sono più date.

Allo stesso modo in cui non è possibile rimuovere la funzionalità tramite sottoclassi senza interrompere OOP, non è possibile mescolare date e frazioni di data senza creare confusione (o peggio) rendendole compatibili come nell'esempio di codice senza interrompere qualcos'altro.

Se vuoi catturare un anno, cosa c'è di sbagliato in un oggetto che contiene un intero semplice? Se si desidera acquisire un mese e un giorno, perché non acquisire un conteggio mensile e un giorno intero? Forse anche memorizzarli internamente in un oggetto data in modo da ottenere un controllo dei limiti corretto (ad esempio, il 31 febbraio non ha senso). Esporre un'interfaccia diversa, tuttavia.

Perché vorresti confrontare una data con un anno per vedere se sono uguali, maggiori o minori? Non ha senso: non ci sono informazioni sufficienti per fare quel confronto. Tuttavia, ci sono altri confronti che potrebbero avere senso (questo è pseudocodice):

Year y = Year(2015)
Date d = Date(2015, 01, 01)
assert y.contains(d) == True

2

Il secondo commento di Robert Harvey contiene la risposta giusta, ma lasciatemi espandere un po 'su di essa.

L'anno di nascita e le date di nascita delle persone sono entità completamente diverse, quindi non è necessario (e in realtà non si dovrebbe) utilizzare lo stesso meccanismo per entrambi.

Per le date di nascita, puoi escogitare un BirthDatetipo di dati (o forse un YearlyRecurringDatesebbene non riesca a trovare un nome decente in questo momento) che contenga solo un datecon un anno costante, come 2000 per convenzione. L'anno 2000 è una buona scelta perché è stato un salto, quindi non mancherà alle persone il cui compleanno è il 28 febbraio.

Per anni di nascita, è possibile concepire un BirthYeartipo di dati (o, eventualmente, di un ApproximateDatetipo di dati) che conterrebbe una date, e un indicatore della precisione: Year, Month, Full.

Il vantaggio di questi approcci è che nel cuore delle cose si mantiene ancora un datemodo per poter ancora eseguire l'aritmetica della data.


1

Credo che ciò che stai descrivendo sarebbe una sostituzione drop-in per il datetimemodulo che implementa gli datetime.datetimeattributi (anno, mese, ecc.) Come valori con una misurazione dell'incertezza (piuttosto che solo valori).

I pacchetti Python esistono per aiutare con numeri incerti (ad esempio: il pacchetto incertezze ), e forse non sarebbe troppo difficile creare un fork datetimeche utilizza l'incertezza su ogni attributo. Anche a me piacerebbe vederne uno e potrei anche averne bisogno. Si potrebbe certamente argomentare sull'inclusione di un udatetimepacchetto di incertezze sopra collegato.

I tuoi esempi potrebbero essere qualcosa del tipo:

bob_bday = udatetime(2000, (6,6))  # 2000-06 +/- 6mo
>>> 2000-??-?? T??:??:??
bil_bday = udatetime((1970, 50), 3, 7)  # assume bill is ~40 +/- 40 
>>> [1970+/-40]-03-07 T??:??:??

I "valori del segnale" hanno molti problemi, ma in più puoi rappresentare cose con incertezza che i valori del segnale non possono:

# ali was born in spring
ali_bday = udatetime((), (4.5, 1.5))
>>> [1970+/-40]-[4.5+/-1.5]-?? T??:??:??

Un'altra considerazione è che per essere più precisi le incertezze qui dovrebbero effettivamente essere di tipo timedelta. Lascio che sia un esercizio per il lettore capire un costruttore conciso e completo per udatetimeusare le timedeltaincertezze.

Quindi alla fine direi che ciò che descrivi è "facilmente" modellato con incertezze, ma l'implementazione di un udatetimeè praticamente abbastanza difficile. La maggior parte prenderà il percorso "facile" e spezzerà il datetime in componenti e traccerà l'incertezza su di essi indipendentemente, ma se ti senti ambizioso il uncertaintiespacchetto (o un altro) potrebbe essere interessato a una richiesta pull per udatetime.


0

Perché non creare una classe "period" che implementa una struttura da.

"Bob è nato nel 2000" ->

period {
   from  {
      yy = 2000;
      mm = 01;
      dd = 01; 
   }
   to {
     yy = 2000;
     mm = 12;
     dd = 31;
   }
   fuzz = 365;
}

È quindi possibile implementare vari metodi di ricerca, tra parentesi le date da a. L'attributo fuzz fornisce un'utile indicazione della precisione della data in modo da poter specificare fuzz == 1 per corrispondenze esatte o fuzz == 31 per circa un mese circa.

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.