4
\$\begingroup\$

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"<{cls}(day={self.day!r}, month={self.month!r}, year={self.year!r})>"

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

\$\endgroup\$
0
\$\begingroup\$

Interesting exercise! Some suggestions:

  • It looks like a cleaner design might be to have a function (as opposed to a method) which takes a List[ImplicitDate] and returns List[datetime.datetime].
  • datetime.datetime months are one-offset, so I'm puzzled at the month = future_date.month - 1 line.
\$\endgroup\$
  • \$\begingroup\$ * Actually, using a function was my first idea. Since estimate_date uses self, I prefer using a method. * months are one-offest, so I use: if month == 0: month = 12; year = future_date.year - 1 to go one month backward in the past. \$\endgroup\$ – Laurent LAPORTE Jul 8 at 8:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.