Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shortened Timespans (again) #7001

Merged
merged 13 commits into from
Oct 13, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,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;
Expand Down
45 changes: 35 additions & 10 deletions src/main/java/ch/njol/skript/util/Timespan.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -57,17 +58,27 @@ 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;
}

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);
Expand Down Expand Up @@ -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());
}
}
});
Expand All @@ -127,15 +140,21 @@ public void onLanguageChange() {
private static final Pattern TIMESPAN_PATTERN = Pattern.compile("^(\\d+):(\\d\\d)(:\\d\\d){0,2}(?<ms>\\.\\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;

Expand All @@ -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 { // <number> minutes/seconds/.. etc
String[] substring = value.toLowerCase(Locale.ENGLISH).split("\\s+");
Expand Down Expand Up @@ -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() {
Expand Down
36 changes: 27 additions & 9 deletions src/main/resources/lang/default.lang
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
51 changes: 51 additions & 0 deletions src/test/skript/tests/misc/timespans.sk
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
command /testtimespan <timespan>:
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!"