private Map<Instant, Set<DataRetentionRule>> getEarliestRetainedCreationTimeMap(
            final List<DataRetentionRule> activeRules,
            final Instant now) {

        // Use the rule retention period to work out the earliest meta createTime that
        // would be RETAINED by the rule
        // e.g ruleAge: 30 days, so everything older than (now() minus 30d) will be deleted.
        final Map<Instant, Set<DataRetentionRule>> earliestRetainedCreationTimeMap = activeRules.stream()
                .collect(Collectors.groupingBy(
                        rule -> getEarliestRetainedCreateTime(rule, now),
                        Collectors.toSet()));

        // Ensure we have a min creation time of the epoch to ensure we delete any qualifying data
        // right back as far as the data goes. No rules in the set to retain the data so it gets deleted.
        earliestRetainedCreationTimeMap.computeIfAbsent(Instant.EPOCH, k -> new HashSet<>());

        LOGGER.debug(() -> "earliestRetainedCreationTimeMap:\n" + earliestRetainedCreationTimeMap.entrySet().stream()
                .sorted(Map.Entry.comparingByKey(Comparator.reverseOrder()))
                .map(entry -> "createTime: "
                        + entry.getKey() + " ("
                        + TimeUtils.instantAsAge(entry.getKey(), now)
                        + " ago) => "
                        + entry.getValue().stream()
                        .sorted(Comparator.comparing(DataRetentionRule::getRuleNumber))
                        .collect(Collectors.toList()))
                .collect(Collectors.joining("\n")));

        return earliestRetainedCreationTimeMap;
    }
Pkg private so we can test with a known now time
stroom-data/stroom-data-retention-impl/src/main/java/stroom/data/retention/impl/DataRetentionPolicyExecutor.java