def parse_simple_cases(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.pure_number_from_to_regex, source)
        if not match:
            match = regex.search(self.config.pure_number_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

        # get hours
        hour_group_list = RegExpUtility.get_group_list(match, 'hour')

        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)

        # parse PM
        left_desc: str = RegExpUtility.get_group(match, 'leftDesc')
        right_desc: str = RegExpUtility.get_group(match, 'rightDesc')
        pm_str: str = RegExpUtility.get_group(match, 'pm')
        am_str: str = RegExpUtility.get_group(match, 'am')

        # The "ampm" only occurs in time, don't have to consider it here

        if not left_desc:
            rigth_am_valid: bool = right_desc and regex.search(
                self.config.utility_configuration.am_desc_regex,
                right_desc.lower())
            rigth_pm_valid: bool = right_desc and regex.search(
                self.config.utility_configuration.pm_desc__regex,
                right_desc.lower())

            if am_str or rigth_am_valid:
                if end_hour >= 12:
                    end_hour -= 12

                if begin_hour >= 12 and begin_hour - 12 < end_hour:
                    begin_hour -= 12

                # Resolve case like "11 to 3am"
                if begin_hour < 12 and begin_hour > end_hour:
                    begin_hour += 12

                valid = True
            elif pm_str or rigth_pm_valid:
                if end_hour < 12:
                    end_hour += 12

                # Resolve case like "11 to 3pm"
                if begin_hour + 12 < end_hour:
                    begin_hour += 12

                valid = True
        if not valid:
            return result

        begin = f'T{begin_hour:02d}'
        end = f'T{end_hour:02d}'

        if begin_hour >= end_hour:
            end_hour += 24

        difference = end_hour - begin_hour
        result.timex = f'({begin},{end},PT{difference}H)'
        result.future_value = ResolutionStartEnd()
        result.past_value = ResolutionStartEnd()
        result.future_value.start = datetime(year, month,
                                             day) + timedelta(hours=begin_hour)
        result.future_value.end = datetime(year, month,
                                           day) + timedelta(hours=end_hour)
        result.past_value.start = result.future_value.start
        result.past_value.end = result.future_value.end
        result.success = True

        return result
    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