def parse_specific_time()

in Python/libraries/recognizers-date-time/recognizers_date_time/date_time/base_timeperiod.py [0:0]


    def parse_specific_time(self, source: str, reference: datetime) -> DateTimeResolutionResult:
        result = DateTimeResolutionResult()
        year = reference.year
        month = reference.month
        day = reference.day

        source = source.strip().lower()

        match = regex.search(self.config.specific_time_from_to_regex, source)
        if not match:
            match = regex.search(
                self.config.specific_time_between_and_regex, source)

        if not match or match.start() != 0:
            return result

        # this "from .. to .." pattern is valid if followed by a Date OR "pm"
        valid = False

        time1 = RegExpUtility.get_group(match, "time1")
        time2 = RegExpUtility.get_group(match, "time2")

        # get hours
        hour_group_list = RegExpUtility.get_group_list(match, Constants.HOUR_GROUP_NAME)

        hour_str = hour_group_list[0]
        begin_hour = self.config.numbers.get(hour_str, None)
        if not begin_hour:
            begin_hour = int(hour_str)

        hour_str = hour_group_list[1]
        end_hour = self.config.numbers.get(hour_str, None)
        if not end_hour:
            end_hour = int(hour_str)

        # get minutes
        minute_group_list = RegExpUtility.get_group_list(match, Constants.MINUTE_GROUP_NAME)

        begin_minute = end_minute = -1
        if len(minute_group_list) > 1:
            minute_str = minute_group_list[0]
            begin_minute = self.config.numbers.get(minute_str, None)
            if not begin_minute:
                begin_minute = int(minute_str)
            minute_str = minute_group_list[1]
            end_minute = self.config.numbers.get(minute_str, None)
            if not end_minute:
                end_minute = int(minute_str)
        elif len(minute_group_list) == 1:
            minute_str = minute_group_list[0]
            if minute_str in time1:
                begin_minute = self.config.numbers.get(minute_str, None)
                if not begin_minute:
                    begin_minute = int(minute_str)
            elif minute_str in time2:
                end_minute = self.config.numbers.get(minute_str, None)
                if not end_minute:
                    end_minute = int(minute_str)

        # parse AM/PM
        left_desc: str = RegExpUtility.get_group(match, Constants.LEFT_DESC_GROUP_NAME)
        right_desc: str = RegExpUtility.get_group(match, Constants.RIGHT_DESC_GROUP_NAME)

        desc_capture_list = RegExpUtility.get_group_list(match, Constants.DESC_GROUP_NAME)
        for desc_capture in desc_capture_list:
            if desc_capture in time1 and not left_desc:
                left_desc: str = desc_capture
            elif desc_capture in time2 and not right_desc:
                right_desc: str = desc_capture

        begin_date_time = datetime(year, month, day, hour=begin_hour, minute=begin_minute if begin_minute > 0 else 0)
        end_date_time = datetime(year, month, day, hour=end_hour, minute=end_minute if end_minute > 0 else 0)

        has_left_am = left_desc != '' and left_desc.startswith('a')
        has_left_pm = left_desc != '' and left_desc.startswith('p')
        has_right_am = right_desc != '' and right_desc.startswith('a')
        has_right_pm = right_desc != '' and right_desc.startswith('p')
        has_left = has_left_am or has_left_pm
        has_right = has_right_am or has_right_pm

        # both time point has description like 'am' or 'pm'
        if has_left and has_right:
            if has_left_am:
                if begin_hour >= 12:
                    begin_date_time -= timedelta(hours=12)
            else:
                if begin_hour < 12:
                    begin_date_time += timedelta(hours=12)
            if has_right_am:
                if end_hour > 12:
                    end_date_time -= timedelta(hours=12)
            else:
                if end_hour < 12:
                    end_date_time += timedelta(hours=12)
        # one of the time point has description like 'am' or 'pm'
        elif has_left or has_right:
            if has_left_am:
                if begin_hour >= 12:
                    begin_date_time -= timedelta(hours=12)
                if end_hour < 12:
                    if end_date_time < begin_date_time:
                        end_date_time += timedelta(hours=12)
            elif has_left_pm:
                if begin_hour < 12:
                    begin_date_time += timedelta(hours=12)
                if end_hour < 12:
                    if end_date_time < begin_date_time:
                        span: datetime = begin_date_time - end_date_time
                        end_date_time += timedelta(hours=24) if span >= timedelta(hours=12) else timedelta(hours=12)
            if has_right_am:
                if end_hour >= 12:
                    end_date_time -= timedelta(hours=12)
                if begin_hour < 12:
                    if end_date_time < begin_date_time:
                        begin_date_time -= timedelta(hours=12)
            elif has_right_pm:
                if end_hour < 12:
                    end_date_time += timedelta(hours=12)
                if begin_hour < 12:
                    if end_date_time < begin_date_time:
                        begin_date_time -= timedelta(hours=12)
                    else:
                        span = end_date_time - begin_date_time
                        if span >= timedelta(hours=12):
                            begin_date_time += timedelta(hours=12)
        # no 'am' or 'pm' indicator
        elif begin_hour <= 12 and end_hour <= 12:
            if begin_date_time > end_date_time:
                if begin_hour == 12:
                    begin_date_time -= timedelta(hours=12)
                else:
                    end_date_time += timedelta(hours=12)
            result.comment = Constants.AM_PM_GROUP_NAME

        if end_date_time < begin_date_time:
            end_date_time += timedelta(hours=24)

        if begin_minute >= 0:
            begin = f'T{begin_date_time.hour:02d}:{begin_date_time.minute:02d}'
        else:
            begin = f'T{begin_date_time.hour:02d}'
        if end_minute >= 0:
            end = f'T{end_date_time.hour:02d}:{end_date_time.minute:02d}'
        else:
            end = f'T{end_date_time.hour:02d}'

        difference = datetime(year, month, day) + (end_date_time - begin_date_time)
        if difference.minute != 0 and difference.hour != 0:
            result.timex = f'({begin},{end},PT{difference.hour}H{difference.minute}M)'
        elif difference.minute != 0 and difference.hour == 0:
            result.timex = f'({begin},{end},PT{difference.minute}M)'
        else:
            result.timex = f'({begin},{end},PT{difference.hour}H)'

        result.future_value = ResolutionStartEnd()
        result.past_value = ResolutionStartEnd()
        result.future_value.start = begin_date_time
        result.future_value.end = end_date_time
        result.past_value.start = result.future_value.start
        result.past_value.end = result.future_value.end
        result.success = True

        result.sub_date_time_entities = []

        # in SplitDateAndTime mode, time points will be get from these sub_date_time_entities
        # cases like "from 4 to 5pm", "4" should not be trated as sub_date_time_entities
        if has_left or begin_minute >= 0:
            er = ExtractResult()
            er.start = match.start("time1")
            er.length = match.end("time1") - match.start("time1")
            er.text = time1
            er.type = Constants.SYS_DATETIME_TIME
            pr = self.config.time_parser.parse(er, reference)
            result.sub_date_time_entities.append(pr)

        # cases like "from 4am to 5" "5" should not treated as sub_date_time_entities
        if has_right or end_minute >= 0:
            er = ExtractResult()
            er.start = match.start("time2")
            er.length = match.end("time2") - match.start("time2")
            er.text = time2
            er.type = Constants.SYS_DATETIME_TIME
            pr = self.config.time_parser.parse(er, reference)
            result.sub_date_time_entities.append(pr)

        return result