diff --git a/src/main/java/ch/njol/skript/classes/data/SkriptClasses.java b/src/main/java/ch/njol/skript/classes/data/SkriptClasses.java index 86c29a9d4fe..c846b00e680 100644 --- a/src/main/java/ch/njol/skript/classes/data/SkriptClasses.java +++ b/src/main/java/ch/njol/skript/classes/data/SkriptClasses.java @@ -275,7 +275,7 @@ public String toVariableNameString(final Time o) { @Nullable public Timespan parse(final String s, final ParseContext context) { try { - return Timespan.parse(s); + return Timespan.parse(s, context); } catch (IllegalArgumentException e) { Skript.error("'" + s + "' is not a valid timespan"); return null; diff --git a/src/main/java/ch/njol/skript/util/Timespan.java b/src/main/java/ch/njol/skript/util/Timespan.java index 37a0bb4b18d..4fa26717c3f 100644 --- a/src/main/java/ch/njol/skript/util/Timespan.java +++ b/src/main/java/ch/njol/skript/util/Timespan.java @@ -19,6 +19,7 @@ package ch.njol.skript.util; import ch.njol.skript.Skript; +import ch.njol.skript.lang.ParseContext; import ch.njol.skript.localization.GeneralWords; import ch.njol.skript.localization.Language; import ch.njol.skript.localization.LanguageChangeListener; @@ -57,10 +58,12 @@ public enum TimePeriod implements TemporalUnit { YEAR(DAY.time * 365L); private final Noun name; + private final Noun shortName; private final long time; TimePeriod(long time) { - this.name = new Noun("time." + this.name().toLowerCase(Locale.ENGLISH)); + this.name = new Noun("time." + this.name().toLowerCase(Locale.ENGLISH) + ".full"); + this.shortName = new Noun("time." + this.name().toLowerCase(Locale.ENGLISH) + ".short"); this.time = time; } @@ -68,6 +71,14 @@ public long getTime() { return time; } + public String getFullForm() { + return name.toString(); + } + + public String getShortForm() { + return shortName.toString(); + } + @Override public Duration getDuration() { return Duration.ofMillis(time); @@ -119,6 +130,8 @@ public void onLanguageChange() { for (TimePeriod time : TimePeriod.values()) { PARSE_VALUES.put(time.name.getSingular().toLowerCase(Locale.ENGLISH), time.getTime()); PARSE_VALUES.put(time.name.getPlural().toLowerCase(Locale.ENGLISH), time.getTime()); + PARSE_VALUES.put(time.shortName.getSingular().toLowerCase(Locale.ENGLISH), time.getTime()); + PARSE_VALUES.put(time.shortName.getPlural().toLowerCase(Locale.ENGLISH), time.getTime()); } } }); @@ -127,15 +140,21 @@ public void onLanguageChange() { private static final Pattern TIMESPAN_PATTERN = Pattern.compile("^(\\d+):(\\d\\d)(:\\d\\d){0,2}(?\\.\\d{1,4})?$"); private static final Pattern TIMESPAN_NUMBER_PATTERN = Pattern.compile("^\\d+(\\.\\d+)?$"); private static final Pattern TIMESPAN_SPLIT_PATTERN = Pattern.compile("[:.]"); + private static final Pattern SHORT_FORM_PATTERN = Pattern.compile("^(\\d+(?:\\.\\d+)?)([a-zA-Z]+)$"); private final long millis; @Nullable public static Timespan parse(String value) { + return parse(value, ParseContext.DEFAULT); + } + + @Nullable + public static Timespan parse(String value, ParseContext context) { if (value.isEmpty()) return null; - long t = 0; + long totalMillis = 0; boolean minecraftTime = false; boolean isMinecraftTimeSet = false; @@ -153,7 +172,7 @@ else if (length == 3 && !hasMs || length == 4) // HH:MM:SS[.ms] offset = 1; for (int i = 0; i < substring.length; i++) { - t += times[offset + i] * Utils.parseLong("" + substring[i]); + totalMillis += times[offset + i] * Utils.parseLong("" + substring[i]); } } else { // minutes/seconds/.. etc String[] substring = value.toLowerCase(Locale.ENGLISH).split("\\s+"); @@ -196,21 +215,27 @@ else if (length == 3 && !hasMs || length == 4) // HH:MM:SS[.ms] if (sub.endsWith(",")) sub = sub.substring(0, sub.length() - 1); - Long d = PARSE_VALUES.get(sub.toLowerCase(Locale.ENGLISH)); - if (d == null) + if (context == ParseContext.COMMAND) { + Matcher shortFormMatcher = SHORT_FORM_PATTERN.matcher(sub); + if (shortFormMatcher.matches()) { + amount = Double.parseDouble(shortFormMatcher.group(1)); + sub = shortFormMatcher.group(2).toLowerCase(Locale.ENGLISH); + } + } + + Long millis = PARSE_VALUES.get(sub.toLowerCase(Locale.ENGLISH)); + if (millis == null) return null; - if (minecraftTime && d != TimePeriod.TICK.time) + if (minecraftTime && millis != TimePeriod.TICK.time) amount /= 72f; - t += Math.round(amount * d); + totalMillis += Math.round(amount * millis); isMinecraftTimeSet = true; - } } - - return new Timespan(t); + return new Timespan(totalMillis); } public Timespan() { diff --git a/src/main/resources/lang/default.lang b/src/main/resources/lang/default.lang index 2770d4d94fb..fa084b8df55 100644 --- a/src/main/resources/lang/default.lang +++ b/src/main/resources/lang/default.lang @@ -343,15 +343,33 @@ tree types: # -- Time -- time: - millisecond: millisecond¦s - tick: tick¦s - second: second¦s - minute: minute¦s - hour: hour¦s - day: day¦s - week: week¦s - month: month¦s - year: year¦s + millisecond: + full: millisecond¦s + short: ms + tick: + full: tick¦s + short: t + second: + full: second¦s + short: s + minute: + full: minute¦s + short: m + hour: + full: hour¦s + short: h + day: + full: day¦s + short: d + week: + full: week¦s + short: w + month: + full: month¦s + short: mo + year: + full: year¦s + short: y real: real, rl, irl minecraft: mc, minecraft diff --git a/src/test/skript/tests/misc/timespans.sk b/src/test/skript/tests/misc/timespans.sk new file mode 100644 index 00000000000..e6a81c60918 --- /dev/null +++ b/src/test/skript/tests/misc/timespans.sk @@ -0,0 +1,50 @@ +command /testtimespan : + trigger: + set {timespan} to arg-1 + +test "timespans": + + execute command "/testtimespan 1 day" + assert {timespan} is "1 day" parsed as timespan with "%{timespan}% doesn't equal 1 day!" + + execute command "/testtimespan 2 hours" + assert {timespan} is "2 hours" parsed as timespan with "%{timespan}% doesn't equal 2 hours!" + + execute command "/testtimespan 30 minutes" + assert {timespan} is "30 minutes" parsed as timespan with "%{timespan}% doesn't equal 30 minutes!" + + execute command "/testtimespan 1d" + assert {timespan} is "1 day" parsed as timespan with "%{timespan}% doesn't equal 1 day!" + + execute command "/testtimespan 2h" + assert {timespan} is "2 hours" parsed as timespan with "%{timespan}% doesn't equal 2 hours!" + + execute command "/testtimespan 30m" + assert {timespan} is "30 minutes" parsed as timespan with "%{timespan}% doesn't equal 30 minutes!" + + execute command "/testtimespan 30m and 20s" + assert {timespan} is "30 minutes and 20 seconds" parsed as timespan with "%{timespan}% doesn't equal 30 minutes and 20 seconds!" + + execute command "/testtimespan 50 minutes and 45 seconds" + assert {timespan} is "50 minutes 45 seconds" parsed as timespan with "%{timespan}% doesn't equal 50 minutes and 45 seconds!" + + execute command "/testtimespan 1d 6h" + assert {timespan} is "1 day 6 hours" parsed as timespan with "%{timespan}% doesn't equal 1 day and 6 hours!" + + execute command "/testtimespan 2 weeks 3d 12 hours" + assert {timespan} is "2 weeks 3 days 12 hours" parsed as timespan with "%{timespan}% doesn't equal 2 weeks, 3 days and 12 hours!" + + execute command "/testtimespan 1.5d" + assert {timespan} is "1 day and 12 hours" parsed as timespan with "%{timespan}% doesn't equal 1 day and 12 hours!" + + execute command "/testtimespan 2.5h" + assert {timespan} is "2 hours and 30 minutes" parsed as timespan with "%{timespan}% doesn't equal 2 hours and 30 minutes!" + + execute command "/testtimespan invalidinput" + assert {timespan} is "invalid" parsed as timespan to fail with "%{timespan}% was incorrectly parsed!" + + execute command "/testtimespan 1mo 2w 3d 4h 5m 6s" + assert {timespan} is "1 month, 2 weeks, 3 days, 4 hours, 5 minutes and 6 seconds" parsed as timespan with "%{timespan}% doesn't equal 1 month, 2 weeks, 3 days, 4 hours, 5 minutes and 6 seconds!" + + execute command "/testtimespan 9999999999d" + assert {timespan} is "9999999999 days" parsed as timespan with "%{timespan}% doesn't equal 9999999999 days!"