Как мне моделировать частичные даты в Python? Как неизвестный год или неизвестный день месяца?

11

Я хочу быть в состоянии захватить факты, как Bob was born in 2000и Bill's birthday is May 7th.

В обоих примерах мы знаем только часть даты рождения человека. В одном случае мы знаем только год; в другом случае мы знаем месяц и день, но не год.

Как мне получить эту информацию?

Несколько примеров того, как это может работать:

Представьте себе библиотеку типа datetime, которая позволяет None в полях представлять неизвестных. Я мог бы иметь такой код:

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

Это всего лишь пример того, как он может себя вести. Я не обязательно хочу это точное поведение.

Buttons840
источник
В общем случае для решения подобных задач используется, например, год 0001 в .NET для дат, у которых нет года, и 1 января для годов без месяца и дня.
Роберт Харви
Я отредактировал ваш вопрос, чтобы удалить запросы на библиотеку. Такие вопросы не по теме на этом сайте.
@RobertHarvey Я не могу использовать ваше предложение. Если мы увидим, что Боб родился 1 января 2000 года, мы не знаем, что именно это означает. Мы не можем сказать, родился ли он в первый день 2000 года или родился ли он в любой день 2000 года. Нам нужно знать разницу.
Buttons840
@RobertHarvey Я знаю, что это распространено, но я видел много плохих сбоев из-за неправильного выбора таких значений сигнала. (Кроме того , я не думаю , что он отвечает на вопрос , как потребности OP , чтобы иметь дело только с некоторыми датами неизвестности Установки на 1 января в таких случаях не позволяет дифференцировать реальные даты 1 - го Jan от неизвестных..
Горта робота
5
@ Buttons840: Тогда вам нужно написать класс, который будет включать в себя поведение, которое вы хотите. Вы должны обернуть существующий класс дат и добавить желаемое поведение.
Роберт Харви

Ответы:

3

Прежде всего, как только вы начнете разбивать даты на составляющие их компоненты, они больше не будут датами.

Точно так же, как невозможно удалить функциональность через подклассы, не нарушая ООП, невозможно смешивать даты и дробные части, не вызывая путаницы (или того хуже), делая их совместимыми, как в вашем примере кода, не нарушая что-то еще.

Если вы хотите записать год, что не так с объектом, содержащим простое число? Если вы хотите записать месяц и день, почему бы не записать перечисление за месяц и целое число? Может быть, даже хранить их внутри объекта даты, чтобы вы могли правильно проверить границы (например, 31 февраля не имеет смысла). Однако выставьте другой интерфейс.

Почему вы хотите сравнить дату с годом, чтобы увидеть, являются ли они одинаковыми, большими или меньшими? Это не имеет смысла: нет достаточной информации для такого сравнения. Однако есть и другие сравнения, которые могут иметь смысл (это псевдокод):

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

источник
2

Второй комментарий Роберта Харви содержит правильный ответ, но позвольте мне немного подробнее остановиться на нем.

Год рождения и даты рождения людей - это совершенно разные сущности, поэтому вам не нужно (и на самом деле не следует) использовать один и тот же механизм для обоих.

Для дат рождения вы можете разработать BirthDateтип данных (или, возможно, YearlyRecurringDateхотя я не могу придумать приличное имя прямо сейчас), который будет содержать только dateс постоянным годом, как 2000 по соглашению. Год 2000 - хороший выбор, потому что это был скачок, поэтому он не подведет людей, чей день рождения 28 февраля.

За год рождения, вы можете разработать BirthYearтип данных (или , возможно, ApproximateDateтип данных) , который будет содержать date, и показатель точности: Year, Month, Full.

Преимущество этих подходов состоит в том, что в основе вещей, которые вы все еще поддерживаете, dateвы можете выполнять арифметику дат.

Майк Накис
источник
1

Я полагаю, что то, что вы описываете, будет заменой datetimeмодуля, который реализует datetime.datetimeатрибуты (год, месяц и т. Д.) Как значения с измерением неопределенности (а не просто значения).

Пакеты Python существуют, чтобы помочь с неопределенными числами (например, пакет неопределенностей ), и, возможно, было бы не так уж сложно сделать ответвление datetime, использующее неопределенность для каждого атрибута. Я тоже хотел бы увидеть его и, возможно, даже использовать его. Несомненно, можно привести аргумент в пользу включения udatetimeв пакет вышеупомянутых неопределенностей.

Ваши примеры будут примерно такими:

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

«Значения сигнала» имеют много проблем, но кроме того, вы можете представлять вещи с неопределенностью, что значения сигнала не могут:

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

Другое соображение состоит в том, что, если быть более точным, неопределенности здесь должны быть типичными timedelta. Я оставляю читателю в качестве упражнения выяснение краткого и полного конструктора для udatetimeиспользования timedeltaнеопределенностей.

В конечном счете, я бы сказал, что то, что вы описываете, «легко» моделируется неопределенностями, но реализация a udatetimeпрактически довольно сложна. Большинство выберет «легкий» маршрут, разбив дату и время на компоненты и самостоятельно отслеживая неопределенность в них, но если вы чувствуете себя амбициозно, uncertaintiesпакет (или другой) может заинтересовать запрос на извлечение udatetime.

7yl4r
источник
0

Почему бы не создать класс «period», который реализует структуру from.

"Боб родился в 2000 году" ->

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

Затем вы можете реализовать различные методы поиска, например, заключив в квадратные скобки даты. Атрибут fuzz дает полезную информацию о том, насколько точна дата, поэтому вы можете указать fuzz == 1 для точных совпадений или fuzz == 31 в течение месяца или около того.

Джеймс Андерсон
источник