private DateTimeResolutionResult parseDuration()

in Java/libraries/recognizers-text-date-time/src/main/java/com/microsoft/recognizers/text/datetime/parsers/BaseDatePeriodParser.java [1091:1244]


    private DateTimeResolutionResult parseDuration(String text, LocalDateTime referenceDate) {
        DateTimeResolutionResult ret = new DateTimeResolutionResult();
        LocalDateTime beginDate = referenceDate;
        LocalDateTime endDate = referenceDate;
        String timex = "";
        boolean restNowSunday = false;
        List<LocalDateTime> dateList = null;

        List<ExtractResult> durationErs = config.getDurationExtractor().extract(text, referenceDate);
        if (durationErs.size() == 1) {
            ParseResult durationPr = config.getDurationParser().parse(durationErs.get(0));
            String beforeStr = text.substring(0, (durationPr.getStart() != null) ? durationPr.getStart() : 0).trim().toLowerCase();
            String afterStr = text.substring(
                    ((durationPr.getStart() != null) ? durationPr.getStart() : 0) + ((durationPr.getLength() != null) ? durationPr.getLength() : 0))
                    .trim().toLowerCase();

            List<ExtractResult> numbersInSuffix = config.getCardinalExtractor().extract(beforeStr);
            List<ExtractResult> numbersInDuration = config.getCardinalExtractor().extract(durationErs.get(0).getText());

            // Handle cases like "2 upcoming days", "5 previous years"
            if (!numbersInSuffix.isEmpty() && numbersInDuration.isEmpty()) {
                ExtractResult numberEr = numbersInSuffix.stream().findFirst().get();
                String numberText = numberEr.getText();
                String durationText = durationErs.get(0).getText();
                String combinedText = String.format("%s %s", numberText, durationText);
                List<ExtractResult> combinedDurationEr = config.getDurationExtractor().extract(combinedText, referenceDate);

                if (!combinedDurationEr.isEmpty()) {
                    durationPr = config.getDurationParser().parse(combinedDurationEr.stream().findFirst().get());
                    int startIndex = numberEr.getStart() + numberEr.getLength();
                    beforeStr = beforeStr.substring(startIndex).trim();
                }
            }

            GetModAndDateResult getModAndDateResult = new GetModAndDateResult();

            if (durationPr.getValue() != null) {
                DateTimeResolutionResult durationResult = (DateTimeResolutionResult)durationPr.getValue();

                if (StringUtility.isNullOrEmpty(durationResult.getTimex())) {
                    return ret;
                }

                Optional<Match> prefixMatch = Arrays.stream(RegExpUtility.getMatches(config.getPastRegex(), beforeStr)).findFirst();
                Optional<Match> suffixMatch = Arrays.stream(RegExpUtility.getMatches(config.getPastRegex(), afterStr)).findFirst();
                if (prefixMatch.isPresent() || suffixMatch.isPresent()) {
                    getModAndDateResult = getModAndDate(beginDate, endDate, referenceDate, durationResult.getTimex(), false);
                    beginDate = getModAndDateResult.beginDate;
                }

                // Handle the "within two weeks" case which means from today to the end of next two weeks
                // Cases like "within 3 days before/after today" is not handled here (4th condition)
                boolean isMatch = false;
                if (RegexExtension.isExactMatch(config.getWithinNextPrefixRegex(), beforeStr, true)) {
                    getModAndDateResult = getModAndDate(beginDate, endDate, referenceDate, durationResult.getTimex(), true);
                    beginDate = getModAndDateResult.beginDate;
                    endDate = getModAndDateResult.endDate;

                    // In GetModAndDate, this "future" resolution will add one day to beginDate/endDate, but for the "within" case it should start from the current day.
                    beginDate = beginDate.minusDays(1);
                    endDate = endDate.minusDays(1);
                    isMatch = true;
                }

                if (RegexExtension.isExactMatch(config.getFutureRegex(), beforeStr, true)) {
                    getModAndDateResult = getModAndDate(beginDate, endDate, referenceDate, durationResult.getTimex(), true);
                    beginDate = getModAndDateResult.beginDate;
                    endDate = getModAndDateResult.endDate;
                    isMatch = true;
                }

                Optional<Match> futureSuffixMatch = Arrays.stream(RegExpUtility.getMatches(config.getFutureSuffixRegex(), afterStr)).findFirst();
                if (futureSuffixMatch.isPresent()) {
                    getModAndDateResult = getModAndDate(beginDate, endDate, referenceDate, durationResult.getTimex(), true);
                    beginDate = getModAndDateResult.beginDate;
                    endDate = getModAndDateResult.endDate;
                }

                // Handle the "in two weeks" case which means the second week
                if (RegexExtension.isExactMatch(config.getInConnectorRegex(), beforeStr, true) &&
                        !DurationParsingUtil.isMultipleDuration(durationResult.getTimex()) && !isMatch) {
                    getModAndDateResult = getModAndDate(beginDate, endDate, referenceDate, durationResult.getTimex(), true);
                    beginDate = getModAndDateResult.beginDate;
                    endDate = getModAndDateResult.endDate;

                    // Change the duration value and the beginDate
                    String unit = durationResult.getTimex().substring(durationResult.getTimex().length() - 1);

                    durationResult.setTimex(String.format("P1%s", unit));
                    beginDate = DurationParsingUtil.shiftDateTime(durationResult.getTimex(), endDate, false);
                }

                if (!StringUtility.isNullOrEmpty(getModAndDateResult.mod)) {
                    ((DateTimeResolutionResult)durationPr.getValue()).setMod(getModAndDateResult.mod);
                }

                timex = durationResult.getTimex();

                List<Object> subDateTimeEntities = new ArrayList<>();
                subDateTimeEntities.add(durationPr);
                ret.setSubDateTimeEntities(subDateTimeEntities);

                if (getModAndDateResult.dateList != null) {
                    ret.setList(getModAndDateResult.dateList.stream().map(e -> (Object)e).collect(Collectors.toList()));
                }
            }
        }

        // Parse "rest of"
        Optional<Match> match = Arrays.stream(RegExpUtility.getMatches(this.config.getRestOfDateRegex(), text)).findFirst();
        if (match.isPresent()) {
            String durationStr = match.get().getGroup("duration").value;
            String durationUnit = this.config.getUnitMap().get(durationStr);
            switch (durationUnit) {
                case "W":
                    int diff = Constants.WeekDayCount - ((beginDate.getDayOfWeek().getValue()) == 0 ? Constants.WeekDayCount : beginDate.getDayOfWeek().getValue());
                    endDate = beginDate.plusDays(diff);
                    timex = String.format("P%s%s", diff, Constants.TimexDay);
                    if (diff == 0) {
                        restNowSunday = true;
                    }
                    break;

                case "MON":
                    endDate = DateUtil.safeCreateFromMinValue(beginDate.getYear(), beginDate.getMonthValue(), 1);
                    endDate = endDate.plusMonths(1).minusDays(1);
                    diff = (int)ChronoUnit.DAYS.between(beginDate, endDate) + 1;
                    timex = String.format("P%s%s", diff, Constants.TimexDay);
                    break;

                case "Y":
                    endDate = DateUtil.safeCreateFromMinValue(beginDate.getYear(), 12, 1);
                    endDate = endDate.plusMonths(1).minusDays(1);
                    diff = (int)ChronoUnit.DAYS.between(beginDate, endDate) + 1;
                    timex = String.format("P%s%s", diff, Constants.TimexDay);
                    break;
                default:
                    break;
            }
        }

        if (!beginDate.equals(endDate) || restNowSunday) {
            endDate = inclusiveEndPeriod ? endDate.minusDays(1) : endDate;

            ret.setTimex(String.format("(%s,%s,%s)", DateTimeFormatUtil.luisDate(beginDate), DateTimeFormatUtil.luisDate(endDate), timex));
            ret.setFutureValue(new Pair<>(beginDate, endDate));
            ret.setPastValue(new Pair<>(beginDate, endDate));
            ret.setSuccess(true);

            return ret;
        }

        return ret;
    }