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;
}