Jak modelować częściowe daty w Pythonie? Jak nieznany rok lub nieznany dzień miesiąca?

11

Chcę być w stanie uchwycić fakty takie jak Bob was born in 2000i Bill's birthday is May 7th.

W obu przykładach znamy tylko część daty urodzenia osoby. W jednym przypadku znamy tylko rok; w innym przypadku znamy miesiąc i dzień, ale nie rok.

Jak przechwycić te informacje?

Kilka przykładów tego, jak to może działać:

Wyobraź sobie bibliotekę podobną do datetime, która pozwoliła None w polach reprezentować nieznane. Mogę mieć kod podobny do następującego:

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

To tylko przykład tego, jak może się zachowywać. Niekoniecznie chcę tego precyzyjnego zachowania.

Przyciski 840
źródło
Ogólnie rzecz biorąc, sposobem radzenia sobie z takimi rzeczami jest użycie na przykład roku 0001 w .NET dla dat, które nie mają roku, i 1 stycznia dla lat bez miesiąca i dnia.
Robert Harvey
Zredagowałem twoje pytanie, aby usunąć prośby o bibliotekę. Takie pytania są nie na temat na tej stronie.
@RobertHarvey Nie mogę skorzystać z Twojej sugestii. Jeśli widzimy, że Bob urodził się 1 stycznia 2000 r., Nie wiemy, co to dokładnie znaczy. Nie możemy stwierdzić, czy urodził się pierwszego dnia 2000 roku, czy też urodził się w jakimkolwiek dniu 2000 roku. Musimy znać różnicę.
Przyciski 840
@RobertHarvey Wiem, że jest to powszechne, ale widziałem wiele złych awarii z powodu złego wyboru takich wartości sygnału. (Plus, nie sądzę, że odpowiada na pytanie, ponieważ OP musi poradzić sobie z tylko niektórymi datami, które są nieznane. Ustawienie 1 stycznia w takich przypadkach nie pozwala odróżnić prawdziwych dat 1 stycznia od nieznanych.
Gort the Robot
5
@ Buttons840: Następnie będziesz musiał napisać klasę, która będzie zawierała pożądane zachowania. Powinieneś zawinąć istniejącą klasę dat i dodać pożądane zachowania.
Robert Harvey

Odpowiedzi:

3

Po pierwsze, kiedy zaczniesz rozkładać daty na ich składniki składowe, nie będą one już datami.

W ten sam sposób, w jaki nie jest możliwe usunięcie funkcji za pośrednictwem podklas bez zerwania OOP, nie można mieszać dat i ułamków dat bez powodowania zamieszania (lub, co gorsza), czyniąc je kompatybilnymi jak w twoim przykładzie kodu, nie psując niczego innego.

Jeśli chcesz uchwycić rok, co jest nie tak z obiektem zawierającym prostą liczbę całkowitą? Jeśli chcesz uchwycić miesiąc i dzień, dlaczego nie uchwycić wyliczenia miesiąca i liczby całkowitej? Może nawet przechowujesz je wewnętrznie w obiekcie daty, aby uzyskać prawidłowe sprawdzanie granic (np. 31 lutego nie ma sensu). Wystaw jednak inny interfejs.

Dlaczego chcesz porównać datę z rokiem, aby sprawdzić, czy są one takie same, większe czy mniejsze? To nie ma sensu: nie ma wystarczających informacji, aby dokonać tego porównania. Istnieją jednak inne porównania, które mogą mieć sens (jest to pseudokod):

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

źródło
2

Drugi komentarz Roberta Harveya zawiera właściwą odpowiedź, ale pozwólcie, że rozwinę ją nieco.

Rok urodzenia i daty narodzin są zupełnie innymi podmiotami, więc nie musisz (a właściwie nie powinieneś) używać tego samego mechanizmu w obu przypadkach.

W przypadku dat urodzenia możesz opracować BirthDatetyp danych (lub być YearlyRecurringDatemoże nie jestem w stanie wymyślić teraz przyzwoitego nazwiska), który zawierałby tylko datestały rok, na przykład 2000 zgodnie z konwencją. Rok 2000 to dobry wybór, ponieważ był to skok, więc nie zawiedzie ludzi, których urodziny przypadają 28 lutego.

Przez wiele lat urodzeń, można opracować BirthYeartyp danych (lub ewentualnie jest ApproximateDatetyp danych), które zawierają date, oraz wskaźnik dokładności: Year, Month, Full.

Zaletą tych podejść jest to, że w centrum rzeczy, które nadal utrzymujesz date, możesz nadal wykonywać arytmetykę dat.

Mike Nakis
źródło
1

Wierzę, że to, co opisujesz, stanowiłoby zastąpienie datetimemodułu, który implementuje datetime.datetimeatrybuty (rok, miesiąc itp.) Jako wartości z pomiarem niepewności (a nie tylko wartości).

Istnieją pakiety Pythona, które pomagają w obliczaniu niepewnych liczb (np. Pakiet niepewności ), i być może nie byłoby zbyt trudne zrobienie rozwidlenia, datetimektóre wykorzystuje niepewność dla każdego atrybutu. Ja też chciałbym je zobaczyć, a może nawet by się przydały. Z pewnością można argumentować za włączeniem a udatetimedo wyżej wspomnianego pakietu niepewności.

Twoje przykłady mogą wyglądać następująco:

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

„Wartości sygnałów” mają wiele problemów, ale dodatkowo można niepewnie przedstawiać rzeczy, których wartości sygnałów nie mogą:

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

Inną kwestią jest to, że aby być bardziej precyzyjnym, niepewności powinny faktycznie być tego rodzaju timedelta. Pozostawiam to jako ćwiczenie dla czytelnika, aby znaleźć zwięzły i kompletny konstruktor do udatetimekorzystania z timedeltaniepewności.

Tak więc ostatecznie powiedziałbym, że to, co opisujesz, jest „łatwe” do modelowania z niepewnością, ale implementacja udatetimejest praktycznie dość trudna. Większość wybierze „łatwą” trasę i rozbije czas na podzespoły i niezależnie śledzi niepewność na nich, ale jeśli czujesz się ambitny, uncertaintiespakiet (lub inny) może być zainteresowany prośbą o ściągnięcie udatetime.

7yl4r
źródło
0

Dlaczego nie stworzyć klasy „kropka”, która implementuje strukturę od do do.

„Bob urodził się w 2000 roku” ->

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

Następnie możesz zaimplementować różne metody wyszukiwania, np. Od nawiasów do dat. Atrybut fuzz daje użyteczne wskazanie, jak dokładna jest data, dzięki czemu można określić fuzz == 1 dla dokładnych dopasowań lub fuzz == 31 w ciągu około miesiąca.

James Anderson
źródło