public DataMutateResult.WithMergedNode unsetNode(DataType dataType, Node node, @Nullable Duration duration) {
        if (node.getExpiry() != null && duration != null) {
            Node otherMatch = getData(dataType).nodesInContext(node.getContexts()).stream()
                    .filter(NodeEqualityPredicate.IGNORE_EXPIRY_TIME_AND_VALUE.equalTo(node))
                    .findFirst().orElse(null);

            if (otherMatch != null && otherMatch.getExpiry() != null) {
                NodeMap data = getData(dataType);

                Instant newExpiry = otherMatch.getExpiry().minus(duration);

                if (newExpiry.isAfter(Instant.now())) {
                    Node newNode = node.toBuilder().expiry(newExpiry).build();

                    // Remove the old Node & add the new one.
                    Difference<Node> changes = data.removeThenAdd(otherMatch, newNode);
                    invalidateCache();
                    this.plugin.getEventDispatcher().dispatchNodeChanges(this, dataType, changes);

                    return new MergedNodeResult(DataMutateResult.SUCCESS, newNode);
                }
            }
        }

        // Fallback to the normal handling.
        return new MergedNodeResult(unsetNode(dataType, node), null);
    }
Removes temporary permissions that have expired

@return true if permissions had expired and were removed
common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java