0
text = "Going out with Sahi to Ikebukuro next week around 4PM or 16:30"

dt_now = datetime.datetime.now()
print('Date and time now:', dt_now.strftime('%Y/%m/%d %H:%M:%S'))
text = re.sub(r'(today)', f'{dt_now.month}/{dt_now.day}', text)
text = re.sub(r'(tomorrow)', f'{dt_now.month}/{dt_now.day + 1}', text)
text = re.sub(r'(the day after tomorrow)', f'{dt_now.month}/{dt_now.day + 2}', text)
text = re.sub(r'(in 2 days)', f'{dt_now.month}/{dt_now.day + 2}', text)
text = re.sub(r'(in 3 days)', f'{dt_now.month}/{dt_now.day + 3}', text)
text = re.sub(r'(yesterday)', f'{dt_now.month}/{dt_now.day - 1}', text)
text = re.sub(r'(next week)', f'{dt_now.month}/{dt_now.day + 7}', text)
text = re.sub(r'(in a month)', f'{dt_now.month + 1}/{dt_now.day}', text)
print(text)

In the code above, I have tried to convert any date-like words directly to absolute dates and hence hard-coded the solution. However, is there a way that I can soft-code it.

An0n1m1ty
  • 448
  • 4
  • 17
Ain
  • 5
  • 2
  • i believe there are no better solution for problem like this, but if there are any ... i really want to know. btw, you could refer to [interpreter pattern](https://en.wikipedia.org/wiki/Interpreter_pattern) for better manage your rules – mtdot Dec 02 '20 at 04:33

2 Answers2

0

The timedelta object from the datetime standard library allows you to express relative times.

You could map each verbal expression to a separate timedelta object, though of course, you still can't resolve expressions like "Thursday the week after next" without knowing today's date.

from datetime import timedelta

reltimes = dict()
for expr, kwargs in (
   ('today', {'days': 0}),
   ('tomorrow', {'days': 1}),
   ('the day after tomorrow', {'days': 2}),
   ('in 2 days', {'days': 2}),
   ('in 3 days', {'days': 3}),     # XXX extend with a regex r"in \d+ days"?
   ('yesterday', {'days': -1}),
   ('next week', {'days': 7}),
   ('in a month', {'days': 30}),   # XX hardcodes 30-day month
   ('in an hour', {'hours': 1})    # extra example
  ):
    reltimes[expr] = timedelta(**kwargs)

def datesub(reldate):
    absdate = datetime.datetime.now() + reldate
    return "%i/%i" % (absdate.month, absdate.day)

for repl in sorted(reltimes.keys(), key=len):
    text = re.sub(repl, lambda x: datesub(reltimes[repl]), text)

Unfortunately, timedelta doesn't easily let you express "this day in another month" so this code would need some elaboration if you are not happy with hardcoding 30-day months. See e.g. How do I calculate the date six months from the current date using the datetime Python module?

As an aside, you have to make sure you substitute the longer strings before the shorter ones. Your code had the bug that "tomorrow" would be substituted before "the day after tomorrow", so the input to the latter would be "the day after 12/2" because "tomorrow" was already replaced.

tripleee
  • 175,061
  • 34
  • 275
  • 318
0

Three recommendations from my side:

  1. A simple improvement to limit hard coding and simplyfy your code can be to use python's regular experssions library to get numbers:
>>> import re
>>> string_to_parse = 'in 2 days'
>>> re.findall(r'\d+', string_to_parse)
['2']
>>> [int(x) for x in re.findall(r'\d+', string_to_parse)]
[2]
  1. Leverage existing work already done like: https://github.com/nltk/nltk_contrib/blob/master/nltk_contrib/timex.py

  2. If you want a highly advanced solution I recommend you to deep dive on Natural Language Toolkit (NLTK), here there is a nice explanation and example on how to tokenize text and extract relationships: https://towardsdatascience.com/named-entity-recognition-with-nltk-and-spacy-8c4a7d88e7da

gilgorio
  • 528
  • 6
  • 10