class LockConfiguration {
    private final Instant createdAt;

    private final String name;

    /**
     * The lock is held until this duration passes, after that it's automatically
     * released (the process holding it has most likely died without releasing the
     * lock) Can be ignored by providers which can detect dead processes (like
     * Zookeeper)
     */
    private final Duration lockAtMostFor;

    /**
     * The lock will be held at least this duration even if the task holding the
     * lock finishes earlier.
     */
    private final Duration lockAtLeastFor;

    /**
     * Creates LockConfiguration. There are two types of lock providers. One that
     * uses "db time" which requires relative values of lockAtMostFor and
     * lockAtLeastFor (currently it's only JdbcTemplateLockProvider). Second type of
     * lock provider uses absolute time calculated from `createdAt`.
     *
     * @param createdAt
     * @param name
     * @param lockAtMostFor
     * @param lockAtLeastFor
     */
    public LockConfiguration(Instant createdAt, String name, Duration lockAtMostFor, Duration lockAtLeastFor) {
        this.createdAt = Objects.requireNonNull(createdAt);
        this.name = Objects.requireNonNull(name);
        this.lockAtMostFor = Objects.requireNonNull(lockAtMostFor);
        this.lockAtLeastFor = Objects.requireNonNull(lockAtLeastFor);
        if (lockAtLeastFor.compareTo(lockAtMostFor) > 0) {
            throw new IllegalArgumentException("lockAtLeastFor is longer than lockAtMostFor for lock '" + name + "'.");
        }
        if (lockAtMostFor.isNegative()) {
            throw new IllegalArgumentException("lockAtMostFor is negative '" + name + "'.");
        }
        if (name.isEmpty()) {
            throw new IllegalArgumentException("lock name can not be empty");
        }
    }

    public String getName() {
        return name;
    }

    public Instant getLockAtMostUntil() {
        return createdAt.plus(lockAtMostFor);
    }

    public Instant getLockAtLeastUntil() {
        return createdAt.plus(lockAtLeastFor);
    }

    /** Returns either now or lockAtLeastUntil whichever is later. */
    public Instant getUnlockTime() {<FILL_FUNCTION_BODY>}

    public Duration getLockAtLeastFor() {
        return lockAtLeastFor;
    }

    public Duration getLockAtMostFor() {
        return lockAtMostFor;
    }

    @Override
    public String toString() {
        return "LockConfiguration{" + "name='" + name + '\'' + ", lockAtMostFor=" + lockAtMostFor + ", lockAtLeastFor="
                + lockAtLeastFor + '}';
    }
}

        Instant now = now();
        Instant lockAtLeastUntil = getLockAtLeastUntil();
        return lockAtLeastUntil.isAfter(now) ? lockAtLeastUntil : now;
    
<no_super_class>