7
\$\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?

200_success
146k22 gold badges190 silver badges479 bronze badges
asked Jul 7, 2019 at 20:00
\$\endgroup\$

2 Answers 2

3
\$\begingroup\$
 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 ```

This can be simplified to:

if len(a_date) > 1:
 if (curr_month and a_date[1] > 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 < 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) > 1:
 if (curr_month and a_date[1] > curr_month):
 curr_year -= 1
 curr_month = a_date[1]
 if len(a_date) > 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) > 1:
 if (curr_month and a_date[1] > curr_month):
 curr_year -= 1
 curr_month = a_date[1]
 if len(a_date) > 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"))
Toby Speight
87.9k14 gold badges104 silver badges325 bronze badges
answered Dec 6, 2019 at 0:20
\$\endgroup\$
2
\$\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.
answered Jul 8, 2019 at 1:36
\$\endgroup\$
1
  • \$\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\$ Commented Jul 8, 2019 at 8:44

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.