Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Datetime parsing does recognize am or pm #1289

Merged
merged 7 commits into from
May 4, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 16 additions & 8 deletions chatterbot/parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
re_year = '(19|20)\d{2}|^(19|20)\d{2}'
re_timeframe = 'this|coming|next|following|previous|last|end\sof\sthe'
re_ordinal = 'st|nd|rd|th|first|second|third|fourth|fourth|' + re_timeframe
re_time = r'(?P<hour>\d{1,2})(\:(?P<minute>\d{1,2})|(?P<convention>am|pm))'
re_time = r'(?P<hour>\d{1,2})(\:(?P<minute>\d{1,2})(\sam|pm)?|\s?(?P<convention>am|pm))'
re_separator = 'of|at|on'

# A list tuple of regular expressions / parser fn to match
Expand Down Expand Up @@ -206,7 +206,7 @@
),
lambda m, base_date: date_from_relative_week_year(
base_date,
m.group('time'),
m.group('time').lower(),
m.group('dmy'),
m.group('number')
) + timedelta(**convert_time_to_hour_minute(
Expand All @@ -227,7 +227,7 @@
),
lambda m, base_date: date_from_relative_day(
base_date,
m.group('time'),
m.group('time').lower(),
m.group('dow')
) + timedelta(**convert_time_to_hour_minute(
m.group('hour'),
Expand Down Expand Up @@ -360,7 +360,7 @@
(
re.compile(
r'''
(%s) # Matches time 12:00
(%s) # Matches time 12:00 am or 12:00 pm
''' % (re_time),
(re.VERBOSE | re.IGNORECASE),
),
Expand Down Expand Up @@ -542,13 +542,14 @@ def date_from_relative_week_year(base_date, time, dow, ordinal=1):
# If there is an ordinal (next 3 weeks) => return a start and end range
# Reset date to start of the day
relative_date = datetime(base_date.year, base_date.month, base_date.day)
ord = convert_string_to_number(ordinal)
if dow in year_variations:
if time == 'this' or time == 'coming':
return datetime(relative_date.year, 1, 1)
elif time == 'last' or time == 'previous':
return datetime(relative_date.year - 1, relative_date.month, 1)
elif time == 'next' or time == 'following':
return relative_date + timedelta(relative_date.year + 1)
return relative_date + timedelta(ord * 365)
elif time == 'end of the':
return datetime(relative_date.year, 12, 31)
elif dow in month_variations:
Expand All @@ -557,7 +558,14 @@ def date_from_relative_week_year(base_date, time, dow, ordinal=1):
elif time == 'last' or time == 'previous':
return datetime(relative_date.year, relative_date.month - 1, relative_date.day)
elif time == 'next' or time == 'following':
return datetime(relative_date.year, relative_date.month + 1, relative_date.day)
if relative_date.month + ord >= 12:
month = relative_date.month - 1 + ord
year = relative_date.year + month // 12
month = month % 12 + 1
day = min(relative_date.day, calendar.monthrange(year, month)[1])
return datetime(year, month, day)
else:
return datetime(relative_date.year, relative_date.month + ord, relative_date.day)
elif time == 'end of the':
return datetime(
relative_date.year,
Expand All @@ -570,7 +578,7 @@ def date_from_relative_week_year(base_date, time, dow, ordinal=1):
elif time == 'last' or time == 'previous':
return relative_date - timedelta(weeks=1)
elif time == 'next' or time == 'following':
return relative_date + timedelta(weeks=1)
return relative_date + timedelta(weeks=ord)
elif time == 'end of the':
day_of_week = base_date.weekday()
return day_of_week + timedelta(days=6 - relative_date.weekday())
Expand All @@ -580,7 +588,7 @@ def date_from_relative_week_year(base_date, time, dow, ordinal=1):
elif time == 'last' or time == 'previous':
return relative_date - timedelta(days=1)
elif time == 'next' or time == 'following':
return relative_date + timedelta(days=1)
return relative_date + timedelta(days=ord)
elif time == 'end of the':
return datetime(relative_date.year, relative_date.month, relative_date.day, 23, 59, 59)

Expand Down
63 changes: 62 additions & 1 deletion tests/test_parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

class DateTimeParsingFunctionIntegrationTestCases(TestCase):
"""
Test the datetime parseing module.
Test the datetime parsing module.

Output of the parser is an array of tuples
[match, value, (start, end)]
Expand Down Expand Up @@ -179,6 +179,51 @@ def test_captured_pattern_last_quarter_of_year(self):
self.assertEqual(parser[0][1][1].strftime('%d-%m-%Y'), '31-12-2015')
self.assertEqual(len(parser), 1)

def test_captured_pattern_is_next_three_weeks(self):
input_text = 'Next 3 weeks'
parser = parsing.datetime_parsing(input_text)
self.assertIn(input_text, parser[0])
self.assertEqual(
parser[0][1].strftime('%d-%m-%Y'),
(datetime.today() + timedelta(weeks=3)).strftime('%d-%m-%Y')
)
self.assertEqual(len(parser), 1)

def test_captured_pattern_is_next_eight_days(self):
input_text = 'Next 8 days'
parser = parsing.datetime_parsing(input_text)
self.assertIn(input_text, parser[0])
self.assertEqual(
parser[0][1].strftime('%d-%m-%Y'),
(datetime.today() + timedelta(days=8)).strftime('%d-%m-%Y')
)
self.assertEqual(len(parser), 1)

def test_captured_pattern_is_next_ten_years(self):
input_text = 'Next 10 years'
parser = parsing.datetime_parsing(input_text)
self.assertIn('Next 10 year', parser[0])
self.assertEqual(
parser[0][1].strftime('%d-%m-%Y'),
(datetime.today() + timedelta(10 * 365)).strftime('%d-%m-%Y')
)
self.assertEqual(len(parser), 1)

def test_captured_pattern_is_next_eleven_months(self):
import calendar
input_text = 'Next 11 months'
parser = parsing.datetime_parsing(input_text)
relative_date = datetime.today()
month = relative_date.month - 1 + 11
year = relative_date.year + month // 12
month = month % 12 + 1
day = min(relative_date.day, calendar.monthrange(year, month)[1])
self.assertIn('Next 11 month', parser[0])
self.assertEqual(
parser[0][1].strftime('%d-%m-%Y'), datetime(year, month, day).strftime('%d-%m-%Y')
)
self.assertEqual(len(parser), 1)

def test_captured_pattern_is_on_day(self):
input_text = 'My birthday is on January 2nd.'
parser = parsing.datetime_parsing(input_text)
Expand All @@ -201,6 +246,22 @@ def test_captured_pattern_is_on_day_of_year_variation2(self):
self.assertEqual(parser[0][1].strftime('%d-%m-%Y'), '02-01-2014')
self.assertEqual(len(parser), 1)

def test_captured_pattern_has_am(self):
input_text = 'You have to woke up at 5 am in the morning'
parser = parsing.datetime_parsing(input_text)
self.assertIn('5 am', parser[0])
self.assertEqual(parser[0][1].strftime('%d'), datetime.today().strftime('%d'))
self.assertEqual(parser[0][1].strftime('%H'), '05')
self.assertEqual(len(parser), 1)

def test_captured_pattern_has_pm(self):
input_text = 'Your dental appointment at 4 pm in the evening.'
parser = parsing.datetime_parsing(input_text)
self.assertIn('4 pm', parser[0])
self.assertEqual(parser[0][1].strftime('%d'), datetime.today().strftime('%d'))
self.assertEqual(parser[0][1].strftime('%H'), '16')
self.assertEqual(len(parser), 1)


class DateTimeParsingTestCases(TestCase):
"""
Expand Down