¿Cómo modelo fechas parciales en Python? ¿Como un año desconocido o un día desconocido del mes?

11

Quiero poder capturar hechos como Bob was born in 2000y Bill's birthday is May 7th.

En ambos ejemplos, solo conocemos parte de la fecha de nacimiento de la persona. En un caso sabemos solo el año; en el otro caso sabemos el mes y el día, pero no el año.

¿Cómo capturo esta información?

Algunos ejemplos de cómo podría funcionar esto:

Imagine una biblioteca como datetime que permitiera a None en los campos representar incógnitas. Podría tener un código como el siguiente:

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

Este es solo un ejemplo de cómo podría comportarse. No necesariamente quiero este comportamiento preciso.

Botones840
fuente
En general, la forma en que maneja cosas como esta es usar, por ejemplo, el año 0001 en .NET para fechas que no tienen un año, y el 1 de enero para años sin mes y día.
Robert Harvey
Edité su pregunta para eliminar las solicitudes de una biblioteca. Tales preguntas están fuera de tema en este sitio.
@RobertHarvey No puedo usar tu sugerencia. Si vemos que Bob nació el 1 de enero de 2000, no sabemos exactamente qué significa esto. No podemos saber si nació el primer día de 2000, o si nació algún día de 2000. Necesitamos saber la diferencia.
Buttons840
@RobertHarvey Sé que es común, pero he visto muchas fallas graves debido a las malas elecciones de tales valores de señal. (Además, no creo que responda la pregunta, ya que el OP necesita tratar solo con algunas fechas desconocidas. Establecer el 1 de enero en tales casos no le permite diferenciar las fechas reales del 1 de enero de las incógnitas.
Gort the Robot
55
@ Buttons840: Entonces tendrás que escribir una clase que encapsule los comportamientos que deseas. Debe ajustar la clase de fecha existente y agregar sus comportamientos deseados.
Robert Harvey

Respuestas:

3

En primer lugar, una vez que comienza a descomponer las fechas en sus componentes constituyentes, ya no son fechas.

De la misma manera que no es posible eliminar la funcionalidad a través de subclases sin romper OOP, no es posible mezclar fechas y fracciones de fecha sin causar confusión (o peor) haciéndolas compatibles como en su ejemplo de código sin romper otra cosa.

Si desea capturar un año, ¿qué tiene de malo un objeto que contenga un entero simple? Si desea capturar un mes y un día, ¿por qué no capturar una enumeración mensual y un día entero? Tal vez incluso los almacene internamente en un objeto de fecha para obtener la verificación de límites adecuada (por ejemplo, el 31 de febrero no tiene sentido). Exponer una interfaz diferente, sin embargo.

¿Por qué querrías comparar una fecha con un año para ver si son iguales, mayores o menores? No tiene sentido: no hay información suficiente para hacer esa comparación. Sin embargo, hay otras comparaciones que podrían tener sentido (este es un pseudocódigo):

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

fuente
2

El segundo comentario de Robert Harvey contiene la respuesta correcta, pero permítanme ampliarlo un poco.

El año de nacimiento de las personas y las fechas de nacimiento de las personas son entidades completamente diferentes, por lo que no es necesario (y en realidad no debería) usar el mismo mecanismo para ambos.

Para las fechas de nacimiento, puede idear un BirthDatetipo de datos (o posiblemente, YearlyRecurringDateaunque no puedo encontrar un nombre decente en este momento) que solo contenga un dateaño constante, como 2000 por convención. El año 2000 es una buena opción porque fue un salto, por lo que no le fallará a las personas cuyo cumpleaños es el 28 de febrero.

Durante años de nacimiento, se puede idear un BirthYeartipo de datos (o posiblemente un ApproximateDatetipo de datos) que contendrá una date, y un indicador de la precisión: Year, Month, Full.

El beneficio de estos enfoques es que, en el fondo de las cosas, aún se mantiene una, datepor lo que aún se puede realizar la aritmética de fechas.

Mike Nakis
fuente
1

Creo que lo que está describiendo sería un reemplazo directo para el datetimemódulo que implementa los datetime.datetimeatributos (año, mes, etc.) como valores con una medición de incertidumbre (en lugar de solo valores).

Los paquetes de Python existen para ayudar con números inciertos (por ejemplo: el paquete de incertidumbres ), y quizás no sería demasiado difícil hacer una bifurcación datetimeque usa incertidumbre en cada atributo. A mí también me gustaría ver uno e incluso podría haberlo usado. Ciertamente, se podría argumentar la inclusión de un udatetimepaquete de incertidumbres anteriormente relacionado.

Sus ejemplos serían algo como:

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??:??:??

Los "valores de señal" tienen muchos problemas, pero además puede representar cosas con incertidumbre que los valores de señal no pueden:

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

Otra consideración es que para ser más precisos, las incertidumbres aquí deberían ser de tipo timedelta. Lo dejo como un ejercicio para que el lector descubra un constructor conciso y completo para udatetimeusar timedeltaincertidumbres.

Así que, en última instancia, diría que lo que usted describe se modela "fácilmente" con incertidumbres, pero la implementación de a udatetimees prácticamente bastante difícil. La mayoría tomará la ruta "fácil" y dividirá la fecha y hora en componentes y rastreará la incertidumbre sobre ellos de manera independiente, pero si se siente ambicioso, el uncertaintiespaquete (u otro) podría estar interesado en una solicitud de extracción udatetime.

7yl4r
fuente
0

¿Por qué no crear una clase "período" que implemente una estructura desde?

"Bob nació en 2000" ->

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

Luego puede implementar varios métodos de búsqueda, entre corchetes desde las fechas. El atributo fuzz proporciona una indicación útil de cuán precisa es la fecha para que pueda especificar fuzz == 1 para coincidencias exactas, o fuzz == 31 para dentro de un mes más o menos.

James Anderson
fuente