Calculate implicit dates I need help to simplify this code about implicit date calculation.

The context: I have sentences with implicit and explicit dates. For instance:


  "From the 5th to the 7th of September 2017, the 12th, 13th and 15th of June, from the 25th to the 31th December, the 1st and the 2nd of January 2019."


A program (not shown here) can parse implicit and explicit dates, so I can get an ordered list (in chronological order) like that:

    history = [
        [5],
        [7, 9, 2017],
        [12],
        [13],
        [15, 6],
        [25],
        [31, 12],
        [1],
        [2, 1, 2019],
    ]


The purpose of the program below is to calculate the dates.

import datetime


class ImplicitDate(object):
    def __init__(self, day: int, month: int = None, year: int = None):
        self.day = day
        self.month = month
        self.year = year

    def __repr__(self):
        cls = self.__class__.__name__
        return f"&lt;{cls}(day={self.day!r}, month={self.month!r}, year={self.year!r})&gt;"

    def __str__(self):
        s = []
        if self.year:
            s.append(f"{self.year:04d}")
        if self.month:
            s.append(f"{self.month:02d}")
        if self.day:
            s.append(f"{self.day:02d}")
        return "-".join(s)

    @property
    def date(self) -&gt; datetime.date or None:
        if self.year is None or self.month is None or self.day is None:
            return None
        return datetime.date(self.year, self.month, self.day)

    def estimate_date(self, future_date: datetime.date) -&gt; datetime.date:
        if self.day is None:
            # weird
            return future_date
        elif self.month is None:
            day = self.day
            month = future_date.month
            year = future_date.year
            date = datetime.date(year, month, day)
            if date &lt; future_date:
                return date
            else:
                month = future_date.month - 1
                if month == 0:
                    month = 12
                    year = future_date.year - 1
                date = datetime.date(year, month, day)
                return date
        elif self.year is None:
            day = self.day
            month = self.month
            year = future_date.year
            date = datetime.date(year, month, day)
            if date &lt; future_date:
                return date
            else:
                year = future_date.year - 1
                date = datetime.date(year, month, day)
                return date
        else:
            day = self.day
            month = self.month
            year = self.year
            date = datetime.date(year, month, day)
            return date


def demo_implicit_dates():
    history = [
        ImplicitDate(5),
        ImplicitDate(7, 9, 2017),
        ImplicitDate(12),
        ImplicitDate(13),
        ImplicitDate(15, 6),
        ImplicitDate(25),
        ImplicitDate(31, 12),
        ImplicitDate(1),
        ImplicitDate(2, 1, 2019),
    ]

    curr = history.pop()
    dates = [curr.date]
    while history:
        top = dates[-1]
        curr = history.pop()
        estimated = curr.estimate_date(top)
        dates.append(estimated)
    dates.reverse()

    for date in dates:
        print(date.isoformat())

    # 2017-09-05
    # 2017-09-07
    # 2018-06-12
    # 2018-06-13
    # 2018-06-15
    # 2018-12-25
    # 2018-12-31
    # 2019-01-01
    # 2019-01-02


Do you think this program is complex or over-designed? How to simplify it?

Do you think this algorithm is well-implemented? Is there something I am missing?
elif self.month is None:
            day = self.day
            month = future_date.month
            year = future_date.year
            date = datetime.date(year, month, day)
            if date &lt; future_date:
                return date
            else:
                month = future_date.month - 1
                if month == 0:
                    month = 12
                    year = future_date.year - 1
                date = datetime.date(year, month, day)
                return date ```



This can be simplified to:

if len(a_date) &gt; 1:
            if (curr_month and a_date[1] &gt; curr_month):
                curr_year -= 1
            curr_month = a_date[1]


Also,


        elif self.year is None:
            day = self.day
            month = self.month
            year = future_date.year
            date = datetime.date(year, month, day)
            if date &lt; future_date:
                return date
            else:
                year = future_date.year - 1
                date = datetime.date(year, month, day)
                return date ```



Can now use a shared statement such as:

if len(a_date) &gt; 1:
            if (curr_month and a_date[1] &gt; curr_month):
                curr_year -= 1
            curr_month = a_date[1]
        if len(a_date) &gt; 2:
            curr_year = a_date[2]


Then,


if self.year is None or self.month is None or self.day is None



Can be simplified using the any() operator which checks a list of items against a condition (in this case if they are not None) then returns true/false depending on if all items satisfied the condition.


day = self.day
            month = future_date.month
            year = future_date.year
            date = datetime.date(year, month, day)



I would turn this into a function as it is repeated again.

The final code after condensing the logic is:

def implicit_dates(history):
    computed_dates = []
    curr_day = None
    curr_month = None
    curr_year = None
    history = reversed(history)
    for a_date in history:
        curr_day = a_date[0]    
        if len(a_date) &gt; 1:
            if (curr_month and a_date[1] &gt; curr_month):
                curr_year -= 1
            curr_month = a_date[1]
        if len(a_date) &gt; 2:
            curr_year = a_date[2]
        if (curr_day and curr_month and curr_year):
            computed_dates.append(
                datetime.datetime(curr_year, curr_month, curr_day))
    return reversed(computed_dates)


for historical_date in implicit_dates(history):
    print(historical_date.strftime("%Y-%m-%d"))