def __init__(self): super().__init__() self._cardinal_extractor = ChineseCardinalExtractor() self._single_date_extractor = ChineseDateExtractor() self._single_time_extractor = ChineseTimeExtractor() self._single_date_time_extractor = ChineseDateTimeExtractor() self._preposition_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.DateTimePeriodPrepositionRegex) self._till_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.DateTimePeriodTillRegex) self._specific_time_of_day_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.SpecificTimeOfDayRegex) self._time_of_day_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.TimeOfDayRegex) self._followed_unit = RegExpUtility.get_safe_reg_exp( ChineseDateTime.DateTimePeriodFollowedUnit) self._time_unit_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.DateTimePeriodUnitRegex) # TODO When the implementation for these properties is added, change the None values to their respective Regexps self._suffix_regex = None self._after_regex = None self._before_regex = None self._prefix_day_regex = None self._pm_desc_regex = None self._am_desc_regex = None self._date_unit_regex = None self._future_suffix_regex = None self._within_next_prefix_regex = None self._token_before_date = None
def __init__(self): super().__init__(ChineseDateTimePeriodParserConfiguration()) self.tmo_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.DateTimePeriodMORegex) self.taf_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.DateTimePeriodAFRegex) self.tev_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.DateTimePeriodEVRegex) self.tni_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.DateTimePeriodNIRegex) self.unit_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.DateTimePeriodUnitRegex) self.time_of_day_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.TimeOfDayRegex) self.cardinal_extractor = ChineseCardinalExtractor() self.cardinal_parser = CJKNumberParser( ChineseNumberParserConfiguration())
def __init__(self): super().__init__() self._past_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.PastRegex) self._future_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.FutureRegex) self._number_combined_with_unit = RegExpUtility.get_safe_reg_exp( ChineseDateTime.NumberCombinedWithUnit) self._this_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.DateThisRegex) self._last_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.DateLastRegex) self._next_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.DateNextRegex) self._zhijian_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.ZhijianRegex) self._hour_num_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.HourNumRegex) self._hour_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.HourRegex) self._cardinal_extractor = ChineseCardinalExtractor() self._single_date_extractor = ChineseDateExtractor() self._single_time_extractor = ChineseTimeExtractor() self._single_date_time_extractor = ChineseDateTimeExtractor() self._preposition_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.DateTimePeriodPrepositionRegex) self._till_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.DateTimePeriodTillRegex) self._specific_time_of_day_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.SpecificTimeOfDayRegex) self._time_of_day_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.TimeOfDayRegex) self._followed_unit = RegExpUtility.get_safe_reg_exp( ChineseDateTime.DateTimePeriodFollowedUnit) self._time_unit_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.DateTimePeriodUnitRegex) # TODO When the implementation for these properties is added, change the None values to their respective Regexps self._check_both_before_after = None self._suffix_regex = None self._after_regex = None self._before_regex = None self._prefix_day_regex = None self._pm_desc_regex = None self._am_desc_regex = None self._date_unit_regex = None self._future_suffix_regex = None self._within_next_prefix_regex = None self._token_before_date = None
def __init__(self): self._cardinal_extractor = ChineseCardinalExtractor() self._single_date_extractor = ChineseDateExtractor() self._single_time_extractor = ChineseTimeExtractor() self._single_date_time_extractor = ChineseDateTimeExtractor() self._preposition_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.DateTimePeriodPrepositionRegex) self._till_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.DateTimePeriodTillRegex) self._specific_time_of_day_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.SpecificTimeOfDayRegex) self._time_of_day_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.TimeOfDayRegex) self._followed_unit = RegExpUtility.get_safe_reg_exp( ChineseDateTime.DateTimePeriodFollowedUnit) self._time_unit_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.DateTimePeriodUnitRegex)
def __init__(self): self._season_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.SeasonRegex ) self._month_suffix_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.MonthSuffixRegex ) self._year_regex_in_number = RegExpUtility.get_safe_reg_exp( ChineseDateTime.YearRegexInNumber ) self._strict_year_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.StrictYearRegex ) self._last_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.DatePeriodLastRegex ) self._next_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.DatePeriodNextRegex ) self._this_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.DatePeriodThisRegex ) self._month_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.MonthRegex ) self._zero_to_nine_integer_regex_chinese = RegExpUtility.get_safe_reg_exp( ChineseDateTime.ZeroToNineIntegerRegexCJK ) self._relative_month_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.RelativeMonthRegex ) self._day_regex_in_chinese = RegExpUtility.get_safe_reg_exp( ChineseDateTime.DatePeriodDayRegexInCJK ) self._day_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.DayRegex ) self._simple_cases_regexes = [ RegExpUtility.get_safe_reg_exp(ChineseDateTime.SimpleCasesRegex), RegExpUtility.get_safe_reg_exp(ChineseDateTime.OneWordPeriodRegex), RegExpUtility.get_safe_reg_exp(ChineseDateTime.StrictYearRegex), RegExpUtility.get_safe_reg_exp(ChineseDateTime.YearToYear), RegExpUtility.get_safe_reg_exp( ChineseDateTime.YearToYearSuffixRequired), RegExpUtility.get_safe_reg_exp(ChineseDateTime.MonthToMonth), RegExpUtility.get_safe_reg_exp(ChineseDateTime.MonthToMonthSuffixRequired), RegExpUtility.get_safe_reg_exp(ChineseDateTime.YearAndMonth), RegExpUtility.get_safe_reg_exp( ChineseDateTime.PureNumYearAndMonth), RegExpUtility.get_safe_reg_exp( ChineseDateTime.DatePeriodYearInCJKRegex), RegExpUtility.get_safe_reg_exp(ChineseDateTime.WeekOfMonthRegex), RegExpUtility.get_safe_reg_exp(ChineseDateTime.SeasonWithYear), RegExpUtility.get_safe_reg_exp(ChineseDateTime.QuarterRegex), RegExpUtility.get_safe_reg_exp(ChineseDateTime.DecadeRegex) ] self._illegal_year_regex = RegExpUtility.get_safe_reg_exp( BaseDateTime.IllegalYearRegex) self._year_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.YearRegex) self._till_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.DatePeriodTillRegex) self._followed_unit = RegExpUtility.get_safe_reg_exp( ChineseDateTime.FollowedUnit) self._number_combined_with_unit = RegExpUtility.get_safe_reg_exp( ChineseDateTime.NumberCombinedWithUnit) self._past_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.PastRegex) self._future_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.FutureRegex) self._date_point_extractor = ChineseDateExtractor() self._integer_extractor = ChineseNumberExtractor() self._number_parser = BaseNumberParser( ChineseNumberParserConfiguration()) self._now_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.NowRegex) self._month_num_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.MonthNumRegex) self._cardinal_extractor = ChineseCardinalExtractor() self._ordinal_extractor = ChineseOrdinalExtractor() # TODO When the implementation for these properties is added, change the None values to their respective Regexps self._previous_prefix_regex = None self._check_both_before_after = None self._century_suffix_regex = None self._year_period_regex = None self._duration_date_restrictions = None self._more_than_regex = None self._less_than_regex = None self._later_regex = None self._ago_regex = None self._future_suffix_regex = None self._within_next_prefix_regex = None self._time_unit_regex = None self._previous_prefix_regex = None
class ChineseDateTimePeriodParser(BaseDateTimePeriodParser): def __init__(self): super().__init__(ChineseDateTimePeriodParserConfiguration()) self.tmo_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.DateTimePeriodMORegex) self.tmi_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.DateTimePeriodMIRegex) self.taf_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.DateTimePeriodAFRegex) self.tev_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.DateTimePeriodEVRegex) self.tni_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.DateTimePeriodNIRegex) self.unit_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.DateTimePeriodUnitRegex) self.time_of_day_regex = RegExpUtility.get_safe_reg_exp( ChineseDateTime.TimeOfDayRegex) self.single_date_extractor = ChineseDateExtractor() self.time_period_extractor = ChineseTimePeriodExtractor() self.cardinal_extractor = ChineseCardinalExtractor() self.cardinal_parser = CJKNumberParser( ChineseNumberParserConfiguration()) def parse(self, source: ExtractResult, reference: datetime = None) -> Optional[DateTimeParseResult]: if reference is None: reference = datetime.now() result = DateTimeParseResult(source) if source.type is self.parser_type_name: source_text = source.text.strip().lower() inner_result = self.merge_date_and_time_periods( source_text, reference) if not inner_result.success: inner_result = self.merge_two_time_points( source_text, reference) if not inner_result.success: inner_result = self.parse_specific_time_of_day( source_text, reference) if not inner_result.success: inner_result = self._parse_number_with_unit( source_text, reference) if inner_result.success: inner_result.future_resolution[ TimeTypeConstants. START_DATETIME] = DateTimeFormatUtil.format_date_time( inner_result.future_value[0]) inner_result.future_resolution[ TimeTypeConstants. END_DATETIME] = DateTimeFormatUtil.format_date_time( inner_result.future_value[1]) inner_result.past_resolution[ TimeTypeConstants. START_DATETIME] = DateTimeFormatUtil.format_date_time( inner_result.past_value[0]) inner_result.past_resolution[ TimeTypeConstants. END_DATETIME] = DateTimeFormatUtil.format_date_time( inner_result.past_value[1]) result.value = inner_result result.timex_str = inner_result.timex if inner_result is not None else '' result.resolution_str = '' return result def merge_date_and_time_periods( self, trimmed_source: str, reference: datetime) -> DateTimeResolutionResult: result = DateTimeResolutionResult() extracted_result1 = self.single_date_extractor.extract( trimmed_source, reference) extracted_result2 = self.time_period_extractor.extract( trimmed_source, reference) if len(extracted_result1) != 1 or len(extracted_result2) != 1: return result parsed_result1 = self.config.date_parser.parse(extracted_result1[0], reference) parsed_result2 = self.config.time_period_parser.parse( extracted_result2[0], reference) time_range = tuple(parsed_result2.value.future_value) begin_time = time_range[0] end_time = time_range[1] future_date = parsed_result1.value.future_value past_date = parsed_result1.value.past_value result.future_value = (DateUtils.safe_create_from_min_value( future_date.year, future_date.month, future_date.day, begin_time.hour, begin_time.minute, begin_time.second), DateUtils.safe_create_from_min_value( future_date.year, future_date.month, future_date.day, end_time.hour, end_time.minute, end_time.second)) result.past_value = (DateUtils.safe_create_from_min_value( past_date.year, past_date.month, past_date.day, begin_time.hour, begin_time.minute, begin_time.second), DateUtils.safe_create_from_min_value( past_date.year, past_date.month, past_date.day, end_time.hour, end_time.minute, end_time.second)) split = parsed_result2.timex_str.split('T') if len(split) != 4: return result date_str = parsed_result1.timex_str result.timex = split[0] + date_str + 'T' + split[ 1] + date_str + 'T' + split[2] + 'T' + split[3] result.success = True return result def merge_two_time_points(self, source: str, reference: datetime) -> DateTimeResolutionResult: result = DateTimeResolutionResult() prs: BeginEnd = None time_ers = self.config.time_extractor.extract(source, reference) datetime_ers = self.config.date_time_extractor.extract( source, reference) both_has_date = False begin_has_date = False end_has_date = False if len(datetime_ers) == 2: prs = self.get_two_points(datetime_ers[0], datetime_ers[1], self.config.date_time_parser, self.config.date_time_parser, reference) both_has_date = True elif len(datetime_ers) == 1 and len(time_ers) == 2: if datetime_ers[0].overlap(time_ers[0]): prs = self.get_two_points(datetime_ers[0], time_ers[1], self.config.date_time_parser, self.config.time_parser, reference) begin_has_date = True else: prs = self.get_two_points(time_ers[0], datetime_ers[0], self.config.time_parser, self.config.date_time_parser, reference) end_has_date = True elif len(datetime_ers) == 1 and len(time_ers) == 1: if time_ers[0].start < datetime_ers[0].start: prs = self.get_two_points(time_ers[0], datetime_ers[0], self.config.time_parser, self.config.date_time_parser, reference) end_has_date = True else: prs = self.get_two_points(datetime_ers[0], time_ers[0], self.config.date_time_parser, self.config.time_parser, reference) begin_has_date = True if prs is None or not prs.begin.value or not prs.end.value: return result begin: DateTimeResolutionResult = prs.begin.value end: DateTimeResolutionResult = prs.end.value future_begin: datetime = begin.future_value future_end: datetime = end.future_value past_begin: datetime = begin.past_value past_end: datetime = end.past_value if future_begin > future_end: future_begin = past_begin if past_end < past_begin: past_end = future_end right_time = DateUtils.safe_create_from_min_value_date_time(reference) left_time = DateUtils.safe_create_from_min_value_date_time(reference) if both_has_date: right_time = DateUtils.safe_create_from_min_value_date_time( future_end) left_time = DateUtils.safe_create_from_min_value_date_time( future_begin) elif begin_has_date: # TODO: Handle "明天下午两点到五点" future_end = self.get_datetime(future_begin, future_end) past_end = self.get_datetime(past_begin, past_end) left_time = DateUtils.safe_create_from_min_value_date_time( future_begin) elif end_has_date: # TODO: Handle "明天下午两点到五点" future_begin = self.get_datetime(future_end, future_begin) past_begin = self.get_datetime(past_end, past_begin) right_time = DateUtils.safe_create_from_min_value_date_time( future_end) left: DateTimeResolutionResult = prs.begin.value right: DateTimeResolutionResult = prs.end.value left_result_time: datetime = left.future_value right_result_time: datetime = right.future_value left_time += timedelta(hours=left_result_time.hour, minutes=left_result_time.minute, seconds=left_result_time.second) right_time += timedelta(hours=right_result_time.hour, minutes=right_result_time.minute, seconds=right_result_time.second) # the right side time contains "ampm", while the left side doesn't if right.comment == 'ampm' and not left.comment and right_time < left_time: right_time += timedelta(hours=12) if right_time < left_time: right_time += timedelta(days=1) result.future_value = [left_time, right_time] result.past_value = [left_time, right_time] fuzzy_timex = 'X' in prs.begin.timex_str or 'X' in prs.end.timex_str left_timex = prs.begin.timex_str if fuzzy_timex else DateTimeFormatUtil.luis_date_time( left_time) right_timex = prs.end.timex_str if fuzzy_timex else DateTimeFormatUtil.luis_date_time( right_time) total_hours = DateUtils.total_hours(left_time, right_time) result.timex = f'({left_timex},{right_timex},PT{total_hours}H)' result.success = True return result def parse_specific_time_of_day( self, source: str, reference: datetime) -> DateTimeResolutionResult: result = DateTimeResolutionResult() trimmed_source = source.strip() begin_hour = end_hour = end_min = 0 # Handle 昨晚,今晨 if RegExpUtility.is_exact_match(self.config.specific_time_of_day_regex, trimmed_source, True): values = self.config.get_matched_time_range(source) if not values: return result swift = values.swift date = reference.date() + timedelta(days=swift) day = date.day month = date.month year = date.year result.timex = DateTimeFormatUtil.format_date( date) + values.time_str result.future_value = result.past_value = [ DateUtils.safe_create_from_min_value(year, month, day, values.begin_hour, 0, 0), DateUtils.safe_create_from_min_value(year, month, day, values.end_hour, values.end_min, values.end_min) ] result.success = True return result # handle morning, afternoon.. if regex.search(self.tmo_regex, source): time_str = 'TMO' begin_hour = 8 end_hour = 12 elif regex.search(self.tmi_regex, source): time_str = 'TMI' begin_hour = 11 end_hour = 13 elif regex.search(self.taf_regex, source): time_str = 'TAF' begin_hour = Constants.HALF_DAY_HOUR_COUNT end_hour = 16 elif regex.search(self.tev_regex, source): time_str = 'TEV' begin_hour = 16 end_hour = 20 elif regex.search(self.tni_regex, source): time_str = 'TNI' begin_hour = 20 end_hour = 23 end_min = 59 else: return result if RegExpUtility.is_exact_match(self.config.specific_time_of_day_regex, trimmed_source, True): swift = 0 if regex.search(self.config.next_regex, trimmed_source): swift = 1 elif regex.search(self.config.last_regex, trimmed_source): swift = -1 date = reference.date() + timedelta(days=swift) day = date.day month = date.month year = date.year result.timex = DateTimeFormatUtil.format_date(date) + time_str result.future_value = result.past_value = [ DateUtils.safe_create_from_min_value(year, month, day, begin_hour, 0, 0), DateUtils.safe_create_from_min_value(year, month, day, end_hour, end_min, end_min) ] result.success = True return result # handle Date followed by morning, afternoon match = regex.search(self.config.time_of_day_regex, trimmed_source) if match: before_str = trimmed_source[0:match.start()].strip() extracted_results = self.single_date_extractor.extract( before_str, reference) if len(extracted_results ) == 0 or extracted_results[0].length != len(before_str): return result parse_result = self.config.date_parser.parse( extracted_results[0], reference) future_date = parse_result.value.future_value past_date = parse_result.value.past_value result.timex = parse_result.timex_str + time_str result.future_value = (DateUtils.safe_create_from_min_value( future_date.year, future_date.month, future_date.day, begin_hour, 0, 0), DateUtils.safe_create_from_min_value( future_date.year, future_date.month, future_date.day, end_hour, end_min, end_min)) result.past_value = (DateUtils.safe_create_from_min_value( past_date.year, past_date.month, past_date.day, begin_hour, 0, 0), DateUtils.safe_create_from_min_value( past_date.year, past_date.month, past_date.day, end_hour, end_min, end_min)) result.success = True return result return result def _parse_number_with_unit( self, source: str, reference: datetime) -> DateTimeResolutionResult: result = DateTimeResolutionResult() ers = self.cardinal_extractor.extract(source) if len(ers) == 1: er = ers[0] pr = self.cardinal_parser.parse(er) source_unit: str = source[er.start + er.length:].strip().lower() if source_unit.startswith('个'): source_unit = source_unit[1:] before_str = source[:er.start].strip().lower() return self.__parse_common_duration_with_unit( before_str, source_unit, pr.resolution_str, float(pr.value), reference) # handle "last hour" match = regex.search(self.unit_regex, source) if match: source_unit = RegExpUtility.get_group(match, 'unit') before_str = source[:match.start()].strip().lower() return self.__parse_common_duration_with_unit( before_str, source_unit, '1', 1, reference) return result def __parse_common_duration_with_unit(self, before: str, unit: str, num: str, swift: float, reference: datetime)\ -> DateTimeResolutionResult: result = DateTimeResolutionResult() if unit not in self.config.unit_map: return result unit_str = self.config.unit_map[unit] past_match = regex.search(self.config.past_regex, before) has_past = past_match and len(past_match.group()) == len(before) future_match = regex.search(self.config.future_regex, before) has_future = future_match and len(future_match.group()) == len(before) if not has_future and not has_past: return result begin_date = reference end_date = reference if unit_str == 'H': if has_past: begin_date += timedelta(hours=-swift) if has_future: end_date += timedelta(hours=swift) elif unit_str == 'M': if has_past: begin_date += timedelta(minutes=-swift) if has_future: end_date += timedelta(minutes=swift) elif unit_str == 'S': if has_past: begin_date += timedelta(seconds=-swift) if has_future: end_date += timedelta(seconds=swift) else: return result begin_timex = DateTimeFormatUtil.luis_date_from_datetime( begin_date) + 'T' + DateTimeFormatUtil.luis_time_from_datetime( begin_date) end_timex = DateTimeFormatUtil.luis_date_from_datetime( end_date) + 'T' + DateTimeFormatUtil.luis_time_from_datetime( end_date) result.timex = f'({begin_timex},{end_timex},PT{num}{unit_str[0]})' result.future_value = [begin_date, end_date] result.past_value = [begin_date, end_date] result.success = True return result