diff --git a/api/src/main/java/net/luckperms/api/node/Node.java b/api/src/main/java/net/luckperms/api/node/Node.java index e1fa62958..e8cd7e7ff 100644 --- a/api/src/main/java/net/luckperms/api/node/Node.java +++ b/api/src/main/java/net/luckperms/api/node/Node.java @@ -40,6 +40,7 @@ import net.luckperms.api.node.types.WeightNode; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import java.time.Duration; import java.time.Instant; import java.util.Collection; import java.util.Optional; @@ -162,8 +163,8 @@ public interface Node { /** * Gets the time when this node will expire. * - * @return the {@link Instant} when this node will expire, or null if it - * doesn't have an expiry time + * @return the {@link Instant} when this node will expire, or + * {@code null} if it doesn't have an expiry time */ @Nullable Instant getExpiry(); @@ -176,6 +177,17 @@ public interface Node { */ boolean hasExpired(); + /** + * Gets the time until this node will expire. + * + *

Returns {@code null} if the node doesn't have an expiry time, + * and a {@link Duration#isNegative() negative} duration if it has already expired.

+ * + * @return the duration until the nodes expiry + * @since 5.1 + */ + @Nullable Duration getExpiryDuration(); + /** * Gets the contexts required for this node to apply. * diff --git a/api/src/main/java/net/luckperms/api/node/NodeBuilder.java b/api/src/main/java/net/luckperms/api/node/NodeBuilder.java index 1462b5850..c9e3e26ef 100644 --- a/api/src/main/java/net/luckperms/api/node/NodeBuilder.java +++ b/api/src/main/java/net/luckperms/api/node/NodeBuilder.java @@ -32,6 +32,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalAmount; import java.util.Objects; import java.util.concurrent.TimeUnit; @@ -85,7 +86,20 @@ public interface NodeBuilder, B extends NodeBuilderThe expiry timestamp is calculated relative to the current + *

The expiry time is calculated relative to the current + * system time.

+ * + * @param duration how long the node should be added for + * @return the builder + * @see Node#getExpiry() + * @since 5.1 + */ + @NonNull B expiry(@Nullable TemporalAmount duration); + + /** + * Sets the time when the node should expire. + * + *

The expiry time is calculated relative to the current * system time.

* * @param duration how long the node should be added for diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitBootstrap.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitBootstrap.java index 9994d90f1..3a431366d 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitBootstrap.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitBootstrap.java @@ -42,6 +42,7 @@ import org.bukkit.plugin.java.JavaPlugin; import java.io.InputStream; import java.nio.file.Path; +import java.time.Instant; import java.util.Optional; import java.util.UUID; import java.util.concurrent.CountDownLatch; @@ -81,7 +82,7 @@ public class LPBukkitBootstrap extends JavaPlugin implements LuckPermsBootstrap /** * The time when the plugin was enabled */ - private long startTime; + private Instant startTime; // load/enable latches private final CountDownLatch loadLatch = new CountDownLatch(1); @@ -149,7 +150,7 @@ public class LPBukkitBootstrap extends JavaPlugin implements LuckPermsBootstrap return; } - this.startTime = System.currentTimeMillis(); + this.startTime = Instant.now(); try { this.plugin.enable(); @@ -192,7 +193,7 @@ public class LPBukkitBootstrap extends JavaPlugin implements LuckPermsBootstrap } @Override - public long getStartupTime() { + public Instant getStartupTime() { return this.startTime; } diff --git a/bungee/src/main/java/me/lucko/luckperms/bungee/LPBungeeBootstrap.java b/bungee/src/main/java/me/lucko/luckperms/bungee/LPBungeeBootstrap.java index d449cc17b..61ab52202 100644 --- a/bungee/src/main/java/me/lucko/luckperms/bungee/LPBungeeBootstrap.java +++ b/bungee/src/main/java/me/lucko/luckperms/bungee/LPBungeeBootstrap.java @@ -39,6 +39,7 @@ import net.md_5.bungee.api.plugin.Plugin; import java.io.InputStream; import java.nio.file.Path; +import java.time.Instant; import java.util.Optional; import java.util.UUID; import java.util.concurrent.CountDownLatch; @@ -72,7 +73,7 @@ public class LPBungeeBootstrap extends Plugin implements LuckPermsBootstrap { /** * The time when the plugin was enabled */ - private long startTime; + private Instant startTime; // load/enable latches private final CountDownLatch loadLatch = new CountDownLatch(1); @@ -119,7 +120,7 @@ public class LPBungeeBootstrap extends Plugin implements LuckPermsBootstrap { @Override public void onEnable() { - this.startTime = System.currentTimeMillis(); + this.startTime = Instant.now(); try { this.plugin.enable(); } finally { @@ -150,7 +151,7 @@ public class LPBungeeBootstrap extends Plugin implements LuckPermsBootstrap { } @Override - public long getStartupTime() { + public Instant getStartupTime() { return this.startTime; } diff --git a/common/src/main/java/me/lucko/luckperms/common/actionlog/LoggedAction.java b/common/src/main/java/me/lucko/luckperms/common/actionlog/LoggedAction.java index 24b499aa4..60af9605b 100644 --- a/common/src/main/java/me/lucko/luckperms/common/actionlog/LoggedAction.java +++ b/common/src/main/java/me/lucko/luckperms/common/actionlog/LoggedAction.java @@ -42,6 +42,7 @@ import net.luckperms.api.context.DefaultContextKeys; import org.checkerframework.checker.nullness.qual.NonNull; +import java.time.Duration; import java.time.Instant; import java.util.ArrayList; import java.util.Comparator; @@ -92,6 +93,10 @@ public class LoggedAction implements Action { return Instant.ofEpochSecond(this.timestamp); } + public Duration getDurationSince() { + return Duration.between(getTimestamp(), Instant.now()); + } + @Override public @NonNull Source getSource() { return this.source; diff --git a/common/src/main/java/me/lucko/luckperms/common/api/implementation/ApiPlatform.java b/common/src/main/java/me/lucko/luckperms/common/api/implementation/ApiPlatform.java index c7053b655..8cebf9273 100644 --- a/common/src/main/java/me/lucko/luckperms/common/api/implementation/ApiPlatform.java +++ b/common/src/main/java/me/lucko/luckperms/common/api/implementation/ApiPlatform.java @@ -72,6 +72,6 @@ public class ApiPlatform implements Platform, PluginMetadata { @Override public @NonNull Instant getStartTime() { - return Instant.ofEpochMilli(this.plugin.getBootstrap().getStartupTime()); + return this.plugin.getBootstrap().getStartupTime(); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/command/utils/ArgumentParser.java b/common/src/main/java/me/lucko/luckperms/common/command/utils/ArgumentParser.java index 627f231a1..0ab799ef6 100644 --- a/common/src/main/java/me/lucko/luckperms/common/command/utils/ArgumentParser.java +++ b/common/src/main/java/me/lucko/luckperms/common/command/utils/ArgumentParser.java @@ -32,13 +32,16 @@ import me.lucko.luckperms.common.context.contextset.MutableContextSetImpl; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.sender.Sender; import me.lucko.luckperms.common.storage.misc.DataConstraints; -import me.lucko.luckperms.common.util.DateParser; +import me.lucko.luckperms.common.util.DurationParser; import net.luckperms.api.context.DefaultContextKeys; import net.luckperms.api.context.ImmutableContextSet; import net.luckperms.api.context.MutableContextSet; import net.luckperms.api.model.data.TemporaryNodeMergeStrategy; +import java.time.Duration; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.List; import java.util.Optional; import java.util.UUID; @@ -100,29 +103,29 @@ public class ArgumentParser { return true; } - public static long parseDuration(int index, List args) throws ArgumentException { - long duration; + public static Duration parseDuration(int index, List args) throws ArgumentException { + String input = args.get(index); + Duration duration; + try { - duration = Long.parseLong(args.get(index)); + long number = Long.parseLong(input); + Instant now = Instant.now().truncatedTo(ChronoUnit.SECONDS); + duration = Duration.between(now, Instant.ofEpochSecond(number)); } catch (NumberFormatException e) { try { - duration = DateParser.parseDate(args.get(index), true); + duration = DurationParser.parseDuration(input); } catch (IllegalArgumentException e1) { - throw new InvalidDateException(args.get(index)); + throw new InvalidDateException(input); } } - if (shouldExpire(duration)) { + if (duration.isNegative()) { throw new PastDateException(); } return duration; } - private static boolean shouldExpire(long unixTime) { - return unixTime < (System.currentTimeMillis() / 1000L); - } - public static TemporaryNodeMergeStrategy parseTemporaryModifier(String s) { switch (s.toLowerCase()) { case "accumulate": diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaAddTempChatMeta.java b/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaAddTempChatMeta.java index 6a6e58fd3..22e956b22 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaAddTempChatMeta.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaAddTempChatMeta.java @@ -55,6 +55,7 @@ import net.luckperms.api.model.data.DataType; import net.luckperms.api.model.data.TemporaryNodeMergeStrategy; import net.luckperms.api.node.ChatMetaType; +import java.time.Duration; import java.util.List; public class MetaAddTempChatMeta extends SharedSubCommand { @@ -80,7 +81,7 @@ public class MetaAddTempChatMeta extends SharedSubCommand { int priority = ArgumentParser.parsePriority(0, args); String meta = ArgumentParser.parseString(1, args); - long duration = ArgumentParser.parseDuration(2, args); + Duration duration = ArgumentParser.parseDuration(2, args); TemporaryNodeMergeStrategy modifier = ArgumentParser.parseTemporaryModifier(3, args).orElseGet(() -> plugin.getConfiguration().get(ConfigKeys.TEMPORARY_ADD_BEHAVIOUR)); MutableContextSet context = ArgumentParser.parseContext(3, args, plugin); @@ -93,9 +94,9 @@ public class MetaAddTempChatMeta extends SharedSubCommand { DataMutateResult.WithMergedNode ret = holder.setNode(DataType.NORMAL, this.type.builder(meta, priority).expiry(duration).withContext(context).build(), modifier); if (ret.getResult().wasSuccessful()) { - duration = ret.getMergedNode().getExpiry().getEpochSecond(); + duration = ret.getMergedNode().getExpiryDuration(); - TextComponent.Builder builder = Message.ADD_TEMP_CHATMETA_SUCCESS.asComponent(plugin.getLocaleManager(), holder.getFormattedDisplayName(), this.type.name().toLowerCase(), meta, priority, DurationFormatter.LONG.formatDateDiff(duration), MessageUtils.contextSetToString(plugin.getLocaleManager(), context)).toBuilder(); + TextComponent.Builder builder = Message.ADD_TEMP_CHATMETA_SUCCESS.asComponent(plugin.getLocaleManager(), holder.getFormattedDisplayName(), this.type.name().toLowerCase(), meta, priority, DurationFormatter.LONG.format(duration), MessageUtils.contextSetToString(plugin.getLocaleManager(), context)).toBuilder(); HoverEvent event = HoverEvent.showText(TextUtils.fromLegacy( "¥3Raw " + this.type.name().toLowerCase() + ": ¥r" + meta, '¥' diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaSetTemp.java b/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaSetTemp.java index 8bd57a0c9..0853b5e65 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaSetTemp.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaSetTemp.java @@ -57,6 +57,7 @@ import net.luckperms.api.node.Node; import net.luckperms.api.node.NodeEqualityPredicate; import net.luckperms.api.node.NodeType; +import java.time.Duration; import java.util.List; public class MetaSetTemp extends SharedSubCommand { @@ -73,7 +74,7 @@ public class MetaSetTemp extends SharedSubCommand { String key = args.get(0); String value = args.get(1); - long duration = ArgumentParser.parseDuration(2, args); + Duration duration = ArgumentParser.parseDuration(2, args); TemporaryNodeMergeStrategy modifier = ArgumentParser.parseTemporaryModifier(3, args).orElseGet(() -> plugin.getConfiguration().get(ConfigKeys.TEMPORARY_ADD_BEHAVIOUR)); MutableContextSet context = ArgumentParser.parseContext(3, args, plugin); @@ -92,9 +93,9 @@ public class MetaSetTemp extends SharedSubCommand { } holder.removeIf(DataType.NORMAL, context, NodeType.META.predicate(n -> n.hasExpiry() && n.getMetaKey().equalsIgnoreCase(key)), false); - duration = holder.setNode(DataType.NORMAL, node, modifier).getMergedNode().getExpiry().getEpochSecond(); + duration = holder.setNode(DataType.NORMAL, node, modifier).getMergedNode().getExpiryDuration(); - TextComponent.Builder builder = Message.SET_META_TEMP_SUCCESS.asComponent(plugin.getLocaleManager(), key, value, holder.getFormattedDisplayName(), DurationFormatter.LONG.formatDateDiff(duration), MessageUtils.contextSetToString(plugin.getLocaleManager(), context)).toBuilder(); + TextComponent.Builder builder = Message.SET_META_TEMP_SUCCESS.asComponent(plugin.getLocaleManager(), key, value, holder.getFormattedDisplayName(), DurationFormatter.LONG.format(duration), MessageUtils.contextSetToString(plugin.getLocaleManager(), context)).toBuilder(); HoverEvent event = HoverEvent.showText(TextUtils.fromLegacy( TextUtils.joinNewline("¥3Raw key: ¥r" + key, "¥3Raw value: ¥r" + value), '¥' diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaSetTempChatMeta.java b/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaSetTempChatMeta.java index 1b191b22c..e0b4459f4 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaSetTempChatMeta.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/generic/meta/MetaSetTempChatMeta.java @@ -58,6 +58,7 @@ import net.luckperms.api.model.data.TemporaryNodeMergeStrategy; import net.luckperms.api.node.ChatMetaType; import net.luckperms.api.query.QueryOptions; +import java.time.Duration; import java.util.List; import java.util.OptionalInt; @@ -84,7 +85,7 @@ public class MetaSetTempChatMeta extends SharedSubCommand { int priority = ArgumentParser.parseIntOrElse(0, args, Integer.MIN_VALUE); String meta; - long duration; + Duration duration; TemporaryNodeMergeStrategy modifier; MutableContextSet context; @@ -133,9 +134,9 @@ public class MetaSetTempChatMeta extends SharedSubCommand { DataMutateResult.WithMergedNode ret = holder.setNode(DataType.NORMAL, this.type.builder(meta, priority).expiry(duration).withContext(context).build(), modifier); if (ret.getResult().wasSuccessful()) { - duration = ret.getMergedNode().getExpiry().getEpochSecond(); + duration = ret.getMergedNode().getExpiryDuration(); - TextComponent.Builder builder = Message.ADD_TEMP_CHATMETA_SUCCESS.asComponent(plugin.getLocaleManager(), holder.getFormattedDisplayName(), this.type.name().toLowerCase(), meta, priority, DurationFormatter.LONG.formatDateDiff(duration), MessageUtils.contextSetToString(plugin.getLocaleManager(), context)).toBuilder(); + TextComponent.Builder builder = Message.ADD_TEMP_CHATMETA_SUCCESS.asComponent(plugin.getLocaleManager(), holder.getFormattedDisplayName(), this.type.name().toLowerCase(), meta, priority, DurationFormatter.LONG.format(duration), MessageUtils.contextSetToString(plugin.getLocaleManager(), context)).toBuilder(); HoverEvent event = HoverEvent.showText(TextUtils.fromLegacy( "¥3Raw " + this.type.name().toLowerCase() + ": ¥r" + meta, '¥' diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/generic/parent/ParentAddTemp.java b/common/src/main/java/me/lucko/luckperms/common/commands/generic/parent/ParentAddTemp.java index d69d63e8a..2c7306633 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/generic/parent/ParentAddTemp.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/generic/parent/ParentAddTemp.java @@ -53,6 +53,7 @@ import net.luckperms.api.model.data.DataMutateResult; import net.luckperms.api.model.data.DataType; import net.luckperms.api.model.data.TemporaryNodeMergeStrategy; +import java.time.Duration; import java.util.List; public class ParentAddTemp extends SharedSubCommand { @@ -68,7 +69,7 @@ public class ParentAddTemp extends SharedSubCommand { } String groupName = ArgumentParser.parseName(0, args); - long duration = ArgumentParser.parseDuration(1, args); + Duration duration = ArgumentParser.parseDuration(1, args); TemporaryNodeMergeStrategy modifier = ArgumentParser.parseTemporaryModifier(2, args).orElseGet(() -> plugin.getConfiguration().get(ConfigKeys.TEMPORARY_ADD_BEHAVIOUR)); MutableContextSet context = ArgumentParser.parseContext(2, args, plugin); @@ -93,8 +94,8 @@ public class ParentAddTemp extends SharedSubCommand { DataMutateResult.WithMergedNode ret = holder.setNode(DataType.NORMAL, Inheritance.builder(group.getName()).expiry(duration).withContext(context).build(), modifier); if (ret.getResult().wasSuccessful()) { - duration = ret.getMergedNode().getExpiry().getEpochSecond(); - Message.SET_TEMP_INHERIT_SUCCESS.send(sender, holder.getFormattedDisplayName(), group.getFormattedDisplayName(), DurationFormatter.LONG.formatDateDiff(duration), MessageUtils.contextSetToString(plugin.getLocaleManager(), context)); + duration = ret.getMergedNode().getExpiryDuration(); + Message.SET_TEMP_INHERIT_SUCCESS.send(sender, holder.getFormattedDisplayName(), group.getFormattedDisplayName(), DurationFormatter.LONG.format(duration), MessageUtils.contextSetToString(plugin.getLocaleManager(), context)); LoggedAction.build().source(sender).target(holder) .description("parent", "addtemp", group.getName(), duration, context) diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/generic/parent/ParentInfo.java b/common/src/main/java/me/lucko/luckperms/common/commands/generic/parent/ParentInfo.java index aacd0771c..9abeb9c13 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/generic/parent/ParentInfo.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/generic/parent/ParentInfo.java @@ -115,7 +115,7 @@ public class ParentInfo extends SharedSubCommand { for (InheritanceNode node : content) { String s = "&3> &a" + node.getGroupName() + MessageUtils.getAppendableNodeContextString(plugin.getLocaleManager(), node); if (node.hasExpiry()) { - s += "\n&2 expires in " + DurationFormatter.LONG.formatDateDiff(node.getExpiry().getEpochSecond()); + s += "\n&2 expires in " + DurationFormatter.LONG.format(node.getExpiryDuration()); } TextComponent message = TextUtils.fromLegacy(s, TextUtils.AMPERSAND_CHAR).toBuilder().applyDeep(makeFancy(holder, label, node)).build(); diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/generic/permission/PermissionInfo.java b/common/src/main/java/me/lucko/luckperms/common/commands/generic/permission/PermissionInfo.java index 509da59e9..773cdd252 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/generic/permission/PermissionInfo.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/generic/permission/PermissionInfo.java @@ -115,7 +115,7 @@ public class PermissionInfo extends SharedSubCommand { for (Node node : content) { String s = "&3> " + (node.getValue() ? "&a" : "&c") + node.getKey() + (sender.isConsole() ? " &7(" + node.getValue() + "&7)" : "") + MessageUtils.getAppendableNodeContextString(plugin.getLocaleManager(), node); if (node.hasExpiry()) { - s += "\n&2- expires in " + DurationFormatter.LONG.formatDateDiff(node.getExpiry().getEpochSecond()); + s += "\n&2- expires in " + DurationFormatter.LONG.format(node.getExpiryDuration()); } TextComponent message = TextUtils.fromLegacy(s, TextUtils.AMPERSAND_CHAR).toBuilder().applyDeep(makeFancy(holder, label, node)).build(); diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/generic/permission/PermissionSetTemp.java b/common/src/main/java/me/lucko/luckperms/common/commands/generic/permission/PermissionSetTemp.java index bbd0f49fc..bcd582eb0 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/generic/permission/PermissionSetTemp.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/generic/permission/PermissionSetTemp.java @@ -54,6 +54,7 @@ import net.luckperms.api.model.data.TemporaryNodeMergeStrategy; import net.luckperms.api.node.Node; import net.luckperms.api.node.types.InheritanceNode; +import java.time.Duration; import java.util.List; public class PermissionSetTemp extends SharedSubCommand { @@ -70,7 +71,7 @@ public class PermissionSetTemp extends SharedSubCommand { String node = ArgumentParser.parseString(0, args); boolean value = ArgumentParser.parseBoolean(1, args); - long duration = ArgumentParser.parseDuration(2, args); + Duration duration = ArgumentParser.parseDuration(2, args); TemporaryNodeMergeStrategy modifier = ArgumentParser.parseTemporaryModifier(3, args).orElseGet(() -> plugin.getConfiguration().get(ConfigKeys.TEMPORARY_ADD_BEHAVIOUR)); MutableContextSet context = ArgumentParser.parseContext(3, args, plugin); @@ -93,8 +94,8 @@ public class PermissionSetTemp extends SharedSubCommand { DataMutateResult.WithMergedNode result = holder.setNode(DataType.NORMAL, builtNode, modifier); if (result.getResult().wasSuccessful()) { - duration = result.getMergedNode().getExpiry().getEpochSecond(); - Message.SETPERMISSION_TEMP_SUCCESS.send(sender, node, value, holder.getFormattedDisplayName(), DurationFormatter.LONG.formatDateDiff(duration), MessageUtils.contextSetToString(plugin.getLocaleManager(), context)); + duration = result.getMergedNode().getExpiryDuration(); + Message.SETPERMISSION_TEMP_SUCCESS.send(sender, node, value, holder.getFormattedDisplayName(), DurationFormatter.LONG.format(duration), MessageUtils.contextSetToString(plugin.getLocaleManager(), context)); LoggedAction.build().source(sender).target(holder) .description("permission", "settemp", node, value, duration, context) diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupInfo.java b/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupInfo.java index 2c6a6aa35..58d21b34c 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupInfo.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupInfo.java @@ -84,7 +84,7 @@ public class GroupInfo extends SubCommand { Message.INFO_TEMP_PARENT_HEADER.send(sender); for (InheritanceNode node : tempParents) { Message.INFO_PARENT_ENTRY.send(sender, node.getGroupName(), MessageUtils.getAppendableNodeContextString(plugin.getLocaleManager(), node)); - Message.INFO_PARENT_ENTRY_EXPIRY.send(sender, DurationFormatter.LONG.formatDateDiff(node.getExpiry().getEpochSecond())); + Message.INFO_PARENT_ENTRY_EXPIRY.send(sender, DurationFormatter.LONG.format(node.getExpiryDuration())); } } return CommandResult.SUCCESS; diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupListMembers.java b/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupListMembers.java index a501ea876..db1fd6505 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupListMembers.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/group/GroupListMembers.java @@ -157,7 +157,7 @@ public class GroupListMembers extends SubCommand { return ""; } - return " &8(&7expires in " + DurationFormatter.LONG.formatDateDiff(node.getExpiry().getEpochSecond()) + "&8)"; + return " &8(&7expires in " + DurationFormatter.LONG.format(node.getExpiryDuration()) + "&8)"; } private static Consumer> makeFancy(String holderName, HolderType holderType, String label, HeldNode perm, LuckPermsPlugin plugin) { diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/log/LogGroupHistory.java b/common/src/main/java/me/lucko/luckperms/common/commands/log/LogGroupHistory.java index 6062098d8..d364962d0 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/log/LogGroupHistory.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/log/LogGroupHistory.java @@ -94,12 +94,10 @@ public class LogGroupHistory extends SubCommand { String name = ((Action) entries.values().stream().findAny().get()).getTarget().getName(); Message.LOG_HISTORY_GROUP_HEADER.send(sender, name, page, maxPage); - long now = System.currentTimeMillis() / 1000L; for (Map.Entry e : entries.entrySet()) { - long time = e.getValue().getTimestamp().getEpochSecond(); Message.LOG_ENTRY.send(sender, e.getKey(), - DurationFormatter.CONCISE_LOW_ACCURACY.format(now - time), + DurationFormatter.CONCISE_LOW_ACCURACY.format(e.getValue().getDurationSince()), e.getValue().getSourceFriendlyString(), Character.toString(LoggedAction.getTypeCharacter(((Action) e.getValue()).getTarget().getType())), e.getValue().getTargetFriendlyString(), diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/log/LogRecent.java b/common/src/main/java/me/lucko/luckperms/common/commands/log/LogRecent.java index 667ed7025..2cd902faf 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/log/LogRecent.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/log/LogRecent.java @@ -106,12 +106,10 @@ public class LogRecent extends SubCommand { Message.LOG_RECENT_HEADER.send(sender, page, maxPage); } - long now = System.currentTimeMillis() / 1000L; for (Map.Entry e : entries.entrySet()) { - long time = e.getValue().getTimestamp().getEpochSecond(); Message.LOG_ENTRY.send(sender, e.getKey(), - DurationFormatter.CONCISE_LOW_ACCURACY.format(now - time), + DurationFormatter.CONCISE_LOW_ACCURACY.format(e.getValue().getDurationSince()), e.getValue().getSourceFriendlyString(), Character.toString(LoggedAction.getTypeCharacter(((Action) e.getValue()).getTarget().getType())), e.getValue().getTargetFriendlyString(), diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/log/LogSearch.java b/common/src/main/java/me/lucko/luckperms/common/commands/log/LogSearch.java index 7d38afd16..8f3bbcf6c 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/log/LogSearch.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/log/LogSearch.java @@ -93,12 +93,10 @@ public class LogSearch extends SubCommand { SortedMap entries = log.getPage(page, ENTRIES_PER_PAGE); Message.LOG_SEARCH_HEADER.send(sender, query, page, maxPage); - long now = System.currentTimeMillis() / 1000L; for (Map.Entry e : entries.entrySet()) { - long time = e.getValue().getTimestamp().getEpochSecond(); Message.LOG_ENTRY.send(sender, e.getKey(), - DurationFormatter.CONCISE_LOW_ACCURACY.format(now - time), + DurationFormatter.CONCISE_LOW_ACCURACY.format(e.getValue().getDurationSince()), e.getValue().getSourceFriendlyString(), Character.toString(LoggedAction.getTypeCharacter(((Action) e.getValue()).getTarget().getType())), e.getValue().getTargetFriendlyString(), diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/log/LogTrackHistory.java b/common/src/main/java/me/lucko/luckperms/common/commands/log/LogTrackHistory.java index 6a5645c14..7ba7e63c7 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/log/LogTrackHistory.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/log/LogTrackHistory.java @@ -94,12 +94,10 @@ public class LogTrackHistory extends SubCommand { String name = ((Action) entries.values().stream().findAny().get()).getTarget().getName(); Message.LOG_HISTORY_TRACK_HEADER.send(sender, name, page, maxPage); - long now = System.currentTimeMillis() / 1000L; for (Map.Entry e : entries.entrySet()) { - long time = e.getValue().getTimestamp().getEpochSecond(); Message.LOG_ENTRY.send(sender, e.getKey(), - DurationFormatter.CONCISE_LOW_ACCURACY.format(now - time), + DurationFormatter.CONCISE_LOW_ACCURACY.format(e.getValue().getDurationSince()), e.getValue().getSourceFriendlyString(), Character.toString(LoggedAction.getTypeCharacter(((Action) e.getValue()).getTarget().getType())), e.getValue().getTargetFriendlyString(), diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/log/LogUserHistory.java b/common/src/main/java/me/lucko/luckperms/common/commands/log/LogUserHistory.java index df418d0e7..e3bf2d5e4 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/log/LogUserHistory.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/log/LogUserHistory.java @@ -87,12 +87,10 @@ public class LogUserHistory extends SubCommand { String name = ((Action) entries.values().stream().findAny().get()).getTarget().getName(); Message.LOG_HISTORY_USER_HEADER.send(sender, name, page, maxPage); - long now = System.currentTimeMillis() / 1000L; for (Map.Entry e : entries.entrySet()) { - long time = e.getValue().getTimestamp().getEpochSecond(); Message.LOG_ENTRY.send(sender, e.getKey(), - DurationFormatter.CONCISE_LOW_ACCURACY.format(now - time), + DurationFormatter.CONCISE_LOW_ACCURACY.format(e.getValue().getDurationSince()), e.getValue().getSourceFriendlyString(), Character.toString(LoggedAction.getTypeCharacter(((Action) e.getValue()).getTarget().getType())), e.getValue().getTargetFriendlyString(), diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/misc/ApplyEditsCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/misc/ApplyEditsCommand.java index adf8512e5..76770ebc5 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/misc/ApplyEditsCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/misc/ApplyEditsCommand.java @@ -312,7 +312,7 @@ public class ApplyEditsCommand extends SingleCommand { private static String formatNode(LocaleManager localeManager, Node n) { return n.getKey() + " &7(" + (n.getValue() ? "&a" : "&c") + n.getValue() + "&7)" + MessageUtils.getAppendableNodeContextString(localeManager, n) + - (n.hasExpiry() ? " &7(" + DurationFormatter.CONCISE.formatDateDiff(n.getExpiry().getEpochSecond()) + ")" : ""); + (n.hasExpiry() ? " &7(" + DurationFormatter.CONCISE.format(n.getExpiryDuration()) + ")" : ""); } private static Map.Entry, Set> diff(Set before, Set after) { diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/misc/InfoCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/misc/InfoCommand.java index 2fea03ea0..566e73ca9 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/misc/InfoCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/misc/InfoCommand.java @@ -41,6 +41,8 @@ import me.lucko.luckperms.common.util.Predicates; import net.luckperms.api.context.ImmutableContextSet; import net.luckperms.api.extension.Extension; +import java.time.Duration; +import java.time.Instant; import java.util.Collection; import java.util.List; import java.util.Map; @@ -80,7 +82,7 @@ public class InfoCommand extends SingleCommand { staticContext.isEmpty() ? "None" : MessageUtils.contextSetToString(plugin.getLocaleManager(), staticContext), plugin.getBootstrap().getPlayerCount(), plugin.getConnectionListener().getUniqueConnections().size(), - DurationFormatter.CONCISE_LOW_ACCURACY.format((System.currentTimeMillis() - plugin.getBootstrap().getStartupTime()) / 1000L), + DurationFormatter.CONCISE_LOW_ACCURACY.format(Duration.between(plugin.getBootstrap().getStartupTime(), Instant.now())), plugin.getUserManager().getAll().size(), plugin.getGroupManager().getAll().size(), plugin.getTrackManager().getAll().size() diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/misc/SearchCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/misc/SearchCommand.java index 4ba8b69bc..c69d6372e 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/misc/SearchCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/misc/SearchCommand.java @@ -165,7 +165,7 @@ public class SearchCommand extends SingleCommand { return ""; } - return " &8(&7expires in " + DurationFormatter.LONG.formatDateDiff(node.getExpiry().getEpochSecond()) + "&8)"; + return " &8(&7expires in " + DurationFormatter.LONG.format(node.getExpiryDuration()) + "&8)"; } private static Consumer> makeFancy(String holderName, HolderType holderType, String label, HeldNode perm, LuckPermsPlugin plugin) { diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/user/UserInfo.java b/common/src/main/java/me/lucko/luckperms/common/commands/user/UserInfo.java index f073d013e..c8450fdc5 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/user/UserInfo.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/user/UserInfo.java @@ -95,7 +95,7 @@ public class UserInfo extends SubCommand { Message.INFO_TEMP_PARENT_HEADER.send(sender); for (InheritanceNode node : tempParents) { Message.INFO_PARENT_ENTRY.send(sender, node.getGroupName(), MessageUtils.getAppendableNodeContextString(plugin.getLocaleManager(), node)); - Message.INFO_PARENT_ENTRY_EXPIRY.send(sender, DurationFormatter.LONG.formatDateDiff(node.getExpiry().getEpochSecond())); + Message.INFO_PARENT_ENTRY_EXPIRY.send(sender, DurationFormatter.LONG.format(node.getExpiryDuration())); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java b/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java index 56c56571e..a41247ed3 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java @@ -440,7 +440,7 @@ public abstract class PermissionHolder { switch (mergeStrategy) { case ADD_NEW_DURATION_TO_EXISTING: { // Create a new Node with the same properties, but add the expiry dates together - long newExpiry = otherMatch.getExpiry().plus(Duration.between(Instant.now(), node.getExpiry())).getEpochSecond(); + Instant newExpiry = otherMatch.getExpiry().plus(Duration.between(Instant.now(), node.getExpiry())); newNode = node.toBuilder().expiry(newExpiry).build(); break; } diff --git a/common/src/main/java/me/lucko/luckperms/common/node/AbstractNode.java b/common/src/main/java/me/lucko/luckperms/common/node/AbstractNode.java index 730e46865..58080a562 100644 --- a/common/src/main/java/me/lucko/luckperms/common/node/AbstractNode.java +++ b/common/src/main/java/me/lucko/luckperms/common/node/AbstractNode.java @@ -40,7 +40,9 @@ import net.luckperms.api.node.types.PermissionNode; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import java.time.Duration; import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.Collection; import java.util.List; import java.util.Map; @@ -113,7 +115,18 @@ public abstract class AbstractNode, B extends NodeBui @Override public boolean hasExpired() { - return hasExpiry() && this.expireAt < System.currentTimeMillis() / 1000L; + Instant expiry = getExpiry(); + return expiry != null && expiry.isBefore(Instant.now()); + } + + @Override + public @Nullable Duration getExpiryDuration() { + Instant expiry = getExpiry(); + if (expiry == null) { + return null; + } + Instant now = Instant.now().truncatedTo(ChronoUnit.SECONDS); + return Duration.between(now, expiry); } @Override diff --git a/common/src/main/java/me/lucko/luckperms/common/node/AbstractNodeBuilder.java b/common/src/main/java/me/lucko/luckperms/common/node/AbstractNodeBuilder.java index 96b2c13ec..fa3d0d151 100644 --- a/common/src/main/java/me/lucko/luckperms/common/node/AbstractNodeBuilder.java +++ b/common/src/main/java/me/lucko/luckperms/common/node/AbstractNodeBuilder.java @@ -37,8 +37,11 @@ import net.luckperms.api.node.metadata.NodeMetadataKey; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import java.time.Instant; import java.time.temporal.ChronoField; +import java.time.temporal.ChronoUnit; import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalAmount; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.Map; @@ -93,6 +96,16 @@ public abstract class AbstractNodeBuilder, B extends return (B) this; } + @Override + public @NonNull B expiry(@Nullable TemporalAmount duration) { + if (duration == null) { + return clearExpiry(); + } + + this.expireAt = Instant.now().truncatedTo(ChronoUnit.SECONDS).plus(duration).getEpochSecond(); + return (B) this; + } + @Override public @NonNull B clearExpiry() { this.expireAt = 0L; diff --git a/common/src/main/java/me/lucko/luckperms/common/node/comparator/NodeComparator.java b/common/src/main/java/me/lucko/luckperms/common/node/comparator/NodeComparator.java index 628821402..d7ad6bfc9 100644 --- a/common/src/main/java/me/lucko/luckperms/common/node/comparator/NodeComparator.java +++ b/common/src/main/java/me/lucko/luckperms/common/node/comparator/NodeComparator.java @@ -28,8 +28,6 @@ package me.lucko.luckperms.common.node.comparator; import net.luckperms.api.node.Node; import net.luckperms.api.node.types.PermissionNode; -import java.time.Instant; -import java.time.temporal.ChronoUnit; import java.util.Comparator; public class NodeComparator implements Comparator { @@ -65,8 +63,7 @@ public class NodeComparator implements Comparator { } if (o1.hasExpiry()) { - // note vvv - result = -Long.compare(ChronoUnit.MILLIS.between(Instant.now(), o1.getExpiry()), ChronoUnit.MILLIS.between(Instant.now(), o2.getExpiry())); + result = o1.getExpiry().compareTo(o2.getExpiry()); if (result != 0) { return result; } diff --git a/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java b/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java index 128437e73..4043b722e 100644 --- a/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java +++ b/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java @@ -61,6 +61,8 @@ import net.luckperms.api.LuckPerms; import okhttp3.OkHttpClient; import java.io.IOException; +import java.time.Duration; +import java.time.Instant; import java.util.EnumSet; import java.util.Optional; import java.util.Set; @@ -198,7 +200,8 @@ public abstract class AbstractLuckPermsPlugin implements LuckPermsPlugin { // perform any platform-specific final setup tasks performFinalSetup(); - getLogger().info("Successfully enabled. (took " + (System.currentTimeMillis() - getBootstrap().getStartupTime()) + "ms)"); + Duration timeTaken = Duration.between(getBootstrap().getStartupTime(), Instant.now()); + getLogger().info("Successfully enabled. (took " + timeTaken.toMillis() + "ms)"); } public final void disable() { diff --git a/common/src/main/java/me/lucko/luckperms/common/plugin/bootstrap/LuckPermsBootstrap.java b/common/src/main/java/me/lucko/luckperms/common/plugin/bootstrap/LuckPermsBootstrap.java index c2a6000fd..479f5286b 100644 --- a/common/src/main/java/me/lucko/luckperms/common/plugin/bootstrap/LuckPermsBootstrap.java +++ b/common/src/main/java/me/lucko/luckperms/common/plugin/bootstrap/LuckPermsBootstrap.java @@ -35,6 +35,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import java.io.InputStream; import java.nio.file.Path; +import java.time.Instant; import java.util.List; import java.util.Optional; import java.util.Set; @@ -99,7 +100,7 @@ public interface LuckPermsBootstrap { * * @return the enable time */ - long getStartupTime(); + Instant getStartupTime(); /** * Gets the platform type this instance of LuckPerms is running on. diff --git a/common/src/main/java/me/lucko/luckperms/common/util/DateParser.java b/common/src/main/java/me/lucko/luckperms/common/util/DateParser.java deleted file mode 100644 index 2c87581a6..000000000 --- a/common/src/main/java/me/lucko/luckperms/common/util/DateParser.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * This file is part of LuckPerms, licensed under the MIT License. - * - * Copyright (c) lucko (Luck) - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package me.lucko.luckperms.common.util; - -import java.util.Calendar; -import java.util.GregorianCalendar; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -/** - * Translates unix timestamps / durations into a readable format - * - * @author khobbits, drtshock, vemacs - * see: https://github.com/drtshock/Essentials/blob/2.x/Essentials/src/com/earth2me/essentials/utils/DateUtil.java - */ -public final class DateParser { - private DateParser() {} - - private static final Pattern TIME_PATTERN = Pattern.compile(Stream.of("y", "mo", "w", "d", "h", "m").map(i -> "(?:([0-9]+)\\s*" + i + "[a-z]*[,\\s]*)?").collect(Collectors.joining()) + "(?:([0-9]+)\\s*(?:s[a-z]*)?)?", Pattern.CASE_INSENSITIVE); - private static final int MAX_YEARS = 100000; - - /** - * Converts a time string to a unix timestamp - * - * @param time the time string - * @param future if the date is in the future, as opposed to the past - * @return a unix timestamp - * @throws IllegalArgumentException if the date input was invalid - */ - public static long parseDate(String time, boolean future) throws IllegalArgumentException { - Matcher matcher = TIME_PATTERN.matcher(time); - int years = 0, months = 0, weeks = 0, days = 0, hours = 0, minutes = 0, seconds = 0; - - boolean found = false; - while (matcher.find()) { - if (matcher.group() == null || matcher.group().isEmpty()) { - continue; - } - for (int i = 0; i < matcher.groupCount(); i++) { - if (matcher.group(i) != null && !matcher.group(i).isEmpty()) { - found = true; - break; - } - } - if (found) { - if (matcher.group(1) != null && !matcher.group(1).isEmpty()) { - years = Integer.parseInt(matcher.group(1)); - } - if (matcher.group(2) != null && !matcher.group(2).isEmpty()) { - months = Integer.parseInt(matcher.group(2)); - } - if (matcher.group(3) != null && !matcher.group(3).isEmpty()) { - weeks = Integer.parseInt(matcher.group(3)); - } - if (matcher.group(4) != null && !matcher.group(4).isEmpty()) { - days = Integer.parseInt(matcher.group(4)); - } - if (matcher.group(5) != null && !matcher.group(5).isEmpty()) { - hours = Integer.parseInt(matcher.group(5)); - } - if (matcher.group(6) != null && !matcher.group(6).isEmpty()) { - minutes = Integer.parseInt(matcher.group(6)); - } - if (matcher.group(7) != null && !matcher.group(7).isEmpty()) { - seconds = Integer.parseInt(matcher.group(7)); - } - break; - } - } - - if (!found) { - throw new IllegalArgumentException(); - } - - Calendar c = new GregorianCalendar(); - if (years > 0) { - if (years > MAX_YEARS) { - years = MAX_YEARS; - } - c.add(Calendar.YEAR, years * (future ? 1 : -1)); - } - if (months > 0) { - c.add(Calendar.MONTH, months * (future ? 1 : -1)); - } - if (weeks > 0) { - c.add(Calendar.WEEK_OF_YEAR, weeks * (future ? 1 : -1)); - } - if (days > 0) { - c.add(Calendar.DAY_OF_MONTH, days * (future ? 1 : -1)); - } - if (hours > 0) { - c.add(Calendar.HOUR_OF_DAY, hours * (future ? 1 : -1)); - } - if (minutes > 0) { - c.add(Calendar.MINUTE, minutes * (future ? 1 : -1)); - } - if (seconds > 0) { - c.add(Calendar.SECOND, seconds * (future ? 1 : -1)); - } - - Calendar max = new GregorianCalendar(); - max.add(Calendar.YEAR, 10); - - if (c.after(max)) { - c = max; - } - return c.getTimeInMillis() / 1000; - } - -} \ No newline at end of file diff --git a/common/src/main/java/me/lucko/luckperms/common/util/DurationFormatter.java b/common/src/main/java/me/lucko/luckperms/common/util/DurationFormatter.java index e287e21a2..a28fdb081 100644 --- a/common/src/main/java/me/lucko/luckperms/common/util/DurationFormatter.java +++ b/common/src/main/java/me/lucko/luckperms/common/util/DurationFormatter.java @@ -25,157 +25,109 @@ package me.lucko.luckperms.common.util; -import java.util.Calendar; -import java.util.GregorianCalendar; +import java.time.Duration; +import java.time.temporal.ChronoUnit; /** * Formats durations to a readable form - * - * @author khobbits, drtshock, vemacs - * see: https://github.com/drtshock/Essentials/blob/2.x/Essentials/src/com/earth2me/essentials/utils/DateUtil.java */ public enum DurationFormatter { - + LONG, CONCISE { - private final String[] names = new String[]{"y", "y", "m", "m", "d", "d", "h", "h", "m", "m", "s", "s"}; + @Override + protected String formatUnitPlural(ChronoUnit unit) { + return String.valueOf(Character.toLowerCase(unit.name().charAt(0))); + } @Override - public String format(Calendar from, Calendar to) { - return dateDiff(from, to, 4, this.names, true); + protected String formatUnitSingular(ChronoUnit unit) { + return formatUnitPlural(unit); } }, - - CONCISE_LOW_ACCURACY { - private final String[] names = new String[]{"y", "y", "m", "m", "d", "d", "h", "h", "m", "m", "s", "s"}; - + CONCISE_LOW_ACCURACY(3) { @Override - public String format(Calendar from, Calendar to) { - return dateDiff(from, to, 2, this.names, true); + protected String formatUnitPlural(ChronoUnit unit) { + return String.valueOf(Character.toLowerCase(unit.name().charAt(0))); } - }, - - LONG { - private final String[] names = new String[]{"year", "years", "month", "months", "day", "days", "hour", "hours", "minute", "minutes", "second", "seconds"}; @Override - public String format(Calendar from, Calendar to) { - return dateDiff(from, to, 4, this.names, false); + protected String formatUnitSingular(ChronoUnit unit) { + return formatUnitPlural(unit); } }; - /** - * The calender type magic numbers to use when formatting - */ - private static final int[] CALENDAR_TYPES = new int[] { - Calendar.YEAR, - Calendar.MONTH, - Calendar.DAY_OF_MONTH, - Calendar.HOUR_OF_DAY, - Calendar.MINUTE, - Calendar.SECOND + private final Unit[] units = new Unit[]{ + new Unit(ChronoUnit.YEARS), + new Unit(ChronoUnit.MONTHS), + new Unit(ChronoUnit.WEEKS), + new Unit(ChronoUnit.DAYS), + new Unit(ChronoUnit.HOURS), + new Unit(ChronoUnit.MINUTES), + new Unit(ChronoUnit.SECONDS) }; - private static final int MAX_YEARS = 100000; + private final int accuracy; - /** - * Formats the difference between two dates - * - * @param from the start date - * @param to the end date - * @param maxAccuracy how accurate the output should be (how many sections it'll have) - * @param names the names to use to format each of the corresponding {@link #CALENDAR_TYPES} - * @return a formatted string - */ - private static String dateDiff(Calendar from, Calendar to, int maxAccuracy, String[] names, boolean concise) { - if (to.equals(from)) { - return "now"; + DurationFormatter() { + this(Integer.MAX_VALUE); + } + + DurationFormatter(int accuracy) { + if (accuracy <= 0) { + throw new IllegalArgumentException("accuracy must be >= 1"); + } + this.accuracy = accuracy; + } + + protected String formatUnitPlural(ChronoUnit unit) { + return " " + unit.name().toLowerCase(); + } + + protected String formatUnitSingular(ChronoUnit unit) { + String s = unit.name().toLowerCase(); + return " " + s.substring(0, s.length() - 1); + } + + private final class Unit { + private final long duration; + private final String stringPlural; + private final String stringSingular; + + Unit(ChronoUnit unit) { + this.duration = unit.getDuration().getSeconds(); + this.stringPlural = formatUnitPlural(unit); + this.stringSingular = formatUnitSingular(unit); } - boolean future = to.after(from); + public String toString(long n) { + return n == 1 ? this.stringSingular : this.stringPlural; + } + } - StringBuilder sb = new StringBuilder(); - int accuracy = 0; - for (int i = 0; i < CALENDAR_TYPES.length; i++) { - if (accuracy > maxAccuracy) { + private String format(long seconds) { + StringBuilder output = new StringBuilder(); + int outputSize = 0; + + for (Unit unit : this.units) { + long n = seconds / unit.duration; + if (n > 0) { + seconds -= unit.duration * n; + output.append(' ').append(n).append(unit.toString(n)); + outputSize++; + } + if (seconds <= 0 || outputSize >= this.accuracy) { break; } - - int diff = dateDiff(CALENDAR_TYPES[i], from, to, future); - if (diff > 0) { - int plural = diff > 1 ? 1 : 0; - String unit = names[i * 2 + plural]; - - sb.append(" "); - sb.append(diff); - if (!concise) { - sb.append(" "); - } - sb.append(unit); - - accuracy++; - } } - if (sb.length() == 0) { - return "now"; + if (output.length() == 0) { + return "0" + this.units[this.units.length - 1].stringPlural; } - - return sb.toString().trim(); + return output.substring(1); } - private static int dateDiff(int type, Calendar fromDate, Calendar toDate, boolean future) { - int fromYear = fromDate.get(Calendar.YEAR); - int toYear = toDate.get(Calendar.YEAR); - if (Math.abs(fromYear - toYear) > MAX_YEARS) { - toDate.set(Calendar.YEAR, fromYear + (future ? MAX_YEARS : -MAX_YEARS)); - } - - int diff = 0; - long savedDate = fromDate.getTimeInMillis(); - while ((future && !fromDate.after(toDate)) || (!future && !fromDate.before(toDate))) { - savedDate = fromDate.getTimeInMillis(); - fromDate.add(type, future ? 1 : -1); - diff++; - } - - diff--; - fromDate.setTimeInMillis(savedDate); - return diff; + public String format(Duration duration) { + return format(duration.getSeconds()); } - /** - * Formats the time difference between two dates - * - * @param from the start date - * @param to the end date - * @return the formatted duration string - */ - public abstract String format(Calendar from, Calendar to); - - /** - * Formats a duration, in seconds - * - * @param seconds the duration - * @return the formatted duration string - */ - public String format(long seconds) { - Calendar from = new GregorianCalendar(); - from.setTimeInMillis(0); - - Calendar to = new GregorianCalendar(); - to.setTimeInMillis(seconds * 1000L); - - return format(from, to); - } - - /** - * Formats the duration between the current time and the given unix timestamp - * - * @param unixTimestamp the timestamp, in seconds - * @return the formatted duration string - */ - public String formatDateDiff(long unixTimestamp) { - long now = System.currentTimeMillis() / 1000L; - return format(unixTimestamp - now); - } } diff --git a/common/src/main/java/me/lucko/luckperms/common/util/DurationParser.java b/common/src/main/java/me/lucko/luckperms/common/util/DurationParser.java new file mode 100644 index 000000000..a25c035e7 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/util/DurationParser.java @@ -0,0 +1,91 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.common.util; + +import java.time.Duration; +import java.time.temporal.ChronoUnit; +import java.util.Arrays; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +/** + * Parses durations from a string format + */ +public final class DurationParser { + private DurationParser() {} + + private static final ChronoUnit[] UNITS = new ChronoUnit[]{ + ChronoUnit.YEARS, + ChronoUnit.MONTHS, + ChronoUnit.WEEKS, + ChronoUnit.DAYS, + ChronoUnit.HOURS, + ChronoUnit.MINUTES, + ChronoUnit.SECONDS + }; + + private static final String PATTERN_STRING = Arrays.stream(UNITS) + .limit(UNITS.length - 1) // skip seconds + .map(unit -> { + if (unit == ChronoUnit.MONTHS) { + return "mo"; + } + // use the first char as the abbreviation + return String.valueOf(Character.toLowerCase(unit.name().charAt(0))); + }) + .map(abbreviation -> "(?:([0-9]+)\\s*" + abbreviation + "[a-z]*[,\\s]*)?") + .collect(Collectors.joining()) + .concat("(?:([0-9]+)\\s*(?:s[a-z]*)?)?"); + + private static final Pattern PATTERN = Pattern.compile(PATTERN_STRING, Pattern.CASE_INSENSITIVE); + + public static Duration parseDuration(String input) throws IllegalArgumentException { + Matcher matcher = PATTERN.matcher(input); + + while (matcher.find()) { + if (matcher.group() == null || matcher.group().isEmpty()) { + continue; + } + + Duration duration = Duration.ZERO; + for (int i = 0; i < UNITS.length; i++) { + ChronoUnit unit = UNITS[i]; + int g = i + 1; + + if (matcher.group(g) != null && !matcher.group(g).isEmpty()) { + int n = Integer.parseInt(matcher.group(g)); + duration = duration.plus(n, unit); + } + } + + return duration; + } + + throw new IllegalArgumentException("unable to parse duration: " + input); + } + +} \ No newline at end of file diff --git a/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseListener.java b/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseListener.java index fd91c2a27..967b569a8 100644 --- a/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseListener.java +++ b/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseListener.java @@ -53,9 +53,10 @@ import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; import java.nio.charset.StandardCharsets; -import java.text.SimpleDateFormat; +import java.time.Duration; +import java.time.Instant; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; -import java.util.Date; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; @@ -65,7 +66,7 @@ import java.util.zip.GZIPOutputStream; * Accepts and processes {@link VerboseEvent}, passed from the {@link VerboseHandler}. */ public class VerboseListener { - private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z"); + private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss z"); // how much data should we store before stopping. private static final int DATA_TRUNCATION = 10000; @@ -97,7 +98,7 @@ public class VerboseListener { .build(); // the time when the listener was first registered - private final long startTime = System.currentTimeMillis(); + private final Instant startTime = Instant.now(); // the sender to notify each time the listener processes a check which passes the filter private final Sender notifiedSender; // the filter @@ -255,10 +256,9 @@ public class VerboseListener { public String uploadPasteData(BytebinClient bytebin) { // retrieve variables long now = System.currentTimeMillis(); - String startDate = DATE_FORMAT.format(new Date(this.startTime)); - String endDate = DATE_FORMAT.format(new Date(now)); - long secondsTaken = (now - this.startTime) / 1000L; - String duration = DurationFormatter.CONCISE.format(secondsTaken); + String startDate = DATE_FORMAT.format(this.startTime); + String endDate = DATE_FORMAT.format(Instant.now()); + String duration = DurationFormatter.CONCISE.format(Duration.between(this.startTime, Instant.now())); boolean truncated = this.matchedCounter.get() > this.results.size(); JObject metadata = new JObject() diff --git a/nukkit/src/main/java/me/lucko/luckperms/nukkit/LPNukkitBootstrap.java b/nukkit/src/main/java/me/lucko/luckperms/nukkit/LPNukkitBootstrap.java index bceba535c..4f7c8f652 100644 --- a/nukkit/src/main/java/me/lucko/luckperms/nukkit/LPNukkitBootstrap.java +++ b/nukkit/src/main/java/me/lucko/luckperms/nukkit/LPNukkitBootstrap.java @@ -37,6 +37,7 @@ import cn.nukkit.plugin.PluginBase; import java.io.InputStream; import java.nio.file.Path; +import java.time.Instant; import java.util.Optional; import java.util.UUID; import java.util.concurrent.CountDownLatch; @@ -70,7 +71,7 @@ public class LPNukkitBootstrap extends PluginBase implements LuckPermsBootstrap /** * The time when the plugin was enabled */ - private long startTime; + private Instant startTime; // load/enable latches private final CountDownLatch loadLatch = new CountDownLatch(1); @@ -117,7 +118,7 @@ public class LPNukkitBootstrap extends PluginBase implements LuckPermsBootstrap @Override public void onEnable() { - this.startTime = System.currentTimeMillis(); + this.startTime = Instant.now(); try { this.plugin.enable(); } finally { @@ -148,7 +149,7 @@ public class LPNukkitBootstrap extends PluginBase implements LuckPermsBootstrap } @Override - public long getStartupTime() { + public Instant getStartupTime() { return this.startTime; } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongeBootstrap.java b/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongeBootstrap.java index f5779db44..33bf9425f 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongeBootstrap.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongeBootstrap.java @@ -57,6 +57,7 @@ import org.spongepowered.api.scheduler.SynchronousExecutor; import java.io.IOException; import java.io.InputStream; import java.nio.file.Path; +import java.time.Instant; import java.util.Optional; import java.util.UUID; import java.util.concurrent.CountDownLatch; @@ -102,7 +103,7 @@ public class LPSpongeBootstrap implements LuckPermsBootstrap { /** * The time when the plugin was enabled */ - private long startTime; + private Instant startTime; // load/enable latches private final CountDownLatch loadLatch = new CountDownLatch(1); @@ -162,7 +163,7 @@ public class LPSpongeBootstrap implements LuckPermsBootstrap { @Listener(order = Order.FIRST) public void onEnable(GamePreInitializationEvent event) { - this.startTime = System.currentTimeMillis(); + this.startTime = Instant.now(); try { this.plugin.load(); } finally { @@ -218,7 +219,7 @@ public class LPSpongeBootstrap implements LuckPermsBootstrap { } @Override - public long getStartupTime() { + public Instant getStartupTime() { return this.startTime; } diff --git a/velocity/src/main/java/me/lucko/luckperms/velocity/LPVelocityBootstrap.java b/velocity/src/main/java/me/lucko/luckperms/velocity/LPVelocityBootstrap.java index 850b8b8fe..ed5ea3f93 100644 --- a/velocity/src/main/java/me/lucko/luckperms/velocity/LPVelocityBootstrap.java +++ b/velocity/src/main/java/me/lucko/luckperms/velocity/LPVelocityBootstrap.java @@ -47,6 +47,7 @@ import org.slf4j.Logger; import java.io.InputStream; import java.nio.file.Path; +import java.time.Instant; import java.util.Optional; import java.util.UUID; import java.util.concurrent.CountDownLatch; @@ -88,7 +89,7 @@ public class LPVelocityBootstrap implements LuckPermsBootstrap { /** * The time when the plugin was enabled */ - private long startTime; + private Instant startTime; // load/enable latches private final CountDownLatch loadLatch = new CountDownLatch(1); @@ -130,7 +131,7 @@ public class LPVelocityBootstrap implements LuckPermsBootstrap { @Subscribe(order = PostOrder.FIRST) public void onEnable(ProxyInitializeEvent e) { - this.startTime = System.currentTimeMillis(); + this.startTime = Instant.now(); try { this.plugin.load(); } finally { @@ -173,7 +174,7 @@ public class LPVelocityBootstrap implements LuckPermsBootstrap { } @Override - public long getStartupTime() { + public Instant getStartupTime() { return this.startTime; }