#!/usr/bin/env python3
+"""
+Parse dates in a variety of formats.
+
+"""
+
import datetime
import functools
import holidays # type: ignore
class ParseException(Exception):
"""An exception thrown during parsing because of unrecognized input."""
def __init__(self, message: str) -> None:
- logger.error(message)
self.message = message
def syntaxError(
self, recognizer, offendingSymbol, line, column, msg, e
):
- logger.error(msg)
raise ParseException(msg)
def reportAmbiguity(
This is the main entrypoint to this class for caller code.
"""
+ date_string = date_string.strip()
+ date_string = re.sub('\s+', ' ', date_string)
self._reset()
listener = RaisingErrorListener()
input_stream = antlr4.InputStream(date_string)
self.datetime: Optional[datetime.datetime] = None
self.context: Dict[str, Any] = {}
self.timedelta = datetime.timedelta(seconds=0)
+ self.saw_overt_year = False
@staticmethod
def _normalize_special_day_name(name: str) -> str:
return TimeUnit.MONTHS
txt = orig.lower()[:3]
if txt in self.day_name_to_number:
- return(self.day_name_to_number[txt])
+ return(TimeUnit(self.day_name_to_number[txt]))
elif txt in self.delta_unit_to_constant:
- return(self.delta_unit_to_constant[txt])
+ return(TimeUnit(self.delta_unit_to_constant[txt]))
raise ParseException(f'Invalid date unit: {orig}')
def _figure_out_time_unit(self, orig: str) -> int:
next_last = self.context.get('special_next_last', '')
if next_last == 'next':
year += 1
+ self.saw_overt_year = True
elif next_last == 'last':
year -= 1
+ self.saw_overt_year = True
# Holiday names
if name == 'easte':
raise ParseException('Missing day')
if 'year' not in self.context:
self.context['year'] = self.today.year
+ self.saw_overt_year = False
+ else:
+ self.saw_overt_year = True
# Handling "ides" and "nones" requires both the day and month.
if (
unit = self.context['delta_unit']
dt = n_timeunits_from_base(
count,
- unit,
+ TimeUnit(unit),
date_to_datetime(self.date)
)
self.date = datetime_to_date(dt)
self.context['day'] = self.now_datetime.day
self.context['month'] = self.now_datetime.month
self.context['year'] = self.now_datetime.year
+ self.saw_overt_year = True
elif txt[:4] == 'last':
self.context['delta_int'] = -1
self.context['day'] = self.now_datetime.day
self.context['month'] = self.now_datetime.month
self.context['year'] = self.now_datetime.year
+ self.saw_overt_year = True
else:
raise ParseException(f'Bad next/last: {ctx.getText()}')
except Exception:
raise ParseException(f'Bad year expression: {ctx.getText()}')
else:
+ self.saw_overt_year = True
self.context['year'] = year
def exitSpecialDateMaybeYearExpr(
unit = self._figure_out_date_unit(unit)
d = n_timeunits_from_base(
count,
- unit,
+ TimeUnit(unit),
d)
self.context['year'] = d.year
self.context['month'] = d.month
unit = self._figure_out_date_unit(unit)
d = n_timeunits_from_base(
count,
- unit,
+ TimeUnit(unit),
d)
self.context['year'] = d.year
self.context['month'] = d.month