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
71 changes: 44 additions & 27 deletions src/main/java/ch/njol/skript/util/Timespan.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,27 @@ public enum TimePeriod {
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();
}

}

private static final List<NonNullPair<Noun, Long>> SIMPLE_VALUES = Arrays.asList(
Expand All @@ -86,6 +96,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 @@ -94,15 +106,16 @@ 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) {
if (value.isEmpty())
return null;

long t = 0;
long totalMillis = 0;
boolean minecraftTime = false;
boolean isMinecraftTimeSet = false;

Expand All @@ -120,19 +133,19 @@ 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+");
for (int i = 0; i < substring.length; i++) {
String sub = substring[i];

if (sub.equals(GeneralWords.and.toString())) {
if (i == 0 || i == substring.length - 1)
return null;
continue;
}

double amount = 1;
if (Noun.isIndefiniteArticle(sub)) {
if (i == substring.length - 1)
Expand All @@ -148,7 +161,7 @@ else if (length == 3 && !hasMs || length == 4) // HH:MM:SS[.ms]
}
sub = substring[++i];
}

if (CollectionUtils.contains(Language.getList("time.real"), sub)) {
if (i == substring.length - 1 || isMinecraftTimeSet && minecraftTime)
return null;
Expand All @@ -159,27 +172,31 @@ else if (length == 3 && !hasMs || length == 4) // HH:MM:SS[.ms]
minecraftTime = true;
sub = substring[++i];
}

if (sub.endsWith(","))
sub = sub.substring(0, sub.length() - 1);

Long d = PARSE_VALUES.get(sub.toLowerCase(Locale.ENGLISH));
if (d == null)
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() {
millis = 0;
}
Expand Down Expand Up @@ -211,7 +228,7 @@ public Timespan(TimePeriod timePeriod, long time) {
* Builds a Timespan from the given long parameter.
*
* @deprecated Use {@link #Timespan(TimePeriod, long)}
*
*
* @param ticks The amount of Minecraft ticks to convert to a timespan.
* @return Timespan based on the provided long.
*/
Expand Down Expand Up @@ -272,15 +289,15 @@ public long getTicks_i() {
public String toString() {
return toString(millis);
}

public String toString(int flags) {
return toString(millis, flags);
}

public static String toString(long millis) {
return toString(millis, 0);
}

@SuppressWarnings("null")
public static String toString(long millis, int flags) {
for (int i = 0; i < SIMPLE_VALUES.size() - 1; i++) {
Expand All @@ -296,7 +313,7 @@ public static String toString(long millis, int flags) {
}
return toString(1. * millis / SIMPLE_VALUES.get(SIMPLE_VALUES.size() - 1).getSecond(), SIMPLE_VALUES.get(SIMPLE_VALUES.size() - 1), flags);
}

private static String toString(double amount, NonNullPair<Noun, Long> pair, int flags) {
return pair.getFirst().withAmount(amount, flags);
}
Expand All @@ -310,15 +327,15 @@ private static String toString(double amount, NonNullPair<Noun, Long> pair, int
public int compareTo(@Nullable Timespan time) {
return Long.compare(millis, time == null ? millis : time.millis);
}

@Override
public int hashCode() {
int prime = 31;
int result = 1;
result = prime * result + (int) (millis / Integer.MAX_VALUE);
return result;
}

@Override
public boolean equals(@Nullable Object obj) {
if (this == obj)
Expand All @@ -330,5 +347,5 @@ public boolean equals(@Nullable Object obj) {

return millis == ((Timespan) obj).millis;
}

}
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 @@
test "timespans":

set {_timespan} to 1 day
assert {_timespan} is a timespan with "Failed to parse '1 day'"
assert {_timespan} is 1 day with "Failed to set variable to '1 day'"

set {_timespan} to 2 hours
assert {_timespan} is a timespan with "Failed to parse '2 hours'"
assert {_timespan} is 2 hours with "Failed to set variable to '2 hours'"

set {_timespan} to 30 minutes
assert {_timespan} is a timespan with "Failed to parse '30 minutes'"
assert {_timespan} is 30 minutes with "Failed to set variable to '2 hours'"

set {_timespan} to 1d
assert {_timespan} is a timespan with "Failed to parse '1d'"
assert {_timespan} is 1d with "Failed to set variable to '1d'"

set {_timespan} to 2h
assert {_timespan} is a timespan with "Failed to parse '2h'"
assert {_timespan} is 2h with "Failed to set variable to '2h'"

set {_timespan} to 30m
assert {_timespan} is a timespan with "Failed to parse '30m'"
assert {_timespan} is 30m with "Failed to set variable to '30m'"

# comparisons with both long and short form
set {_timespan} to 30 minutes and 20s
assert {_timespan} is a timespan with "Failed to parse '30 minutes and 20s'"
assert {_timespan} is 30m and 20 seconds with "Failed to set variable to '30 minutes and 20s'"

set {_timespan} to 1 mo, 30 days, 12w, 20 minutes and 15s
assert {_timespan} is a timespan with "Failed to parse '1 mo, 30 days, 12w, 20 minutes and 15s'"
assert {_timespan} is 1 month, 30 days, 12 weeks, 20 minutes and 15 seconds with "Failed to set variable to '1 mo, 30 days, 12w, 20 minutes and 15s'"

set {_timespan} to 50 minutes and 45 seconds
assert {_timespan} is a timespan with "Failed to parse '50 minutes and 45 seconds'"
assert {_timespan} is 50m and 45s with "Failed to set variable to '50 minutes and 45 seconds'"

# edge cases
set {_timespan} to 0.5d
assert {_timespan} is a timespan with "Failed to parse '0.5d'"
Asleeepp marked this conversation as resolved.
Show resolved Hide resolved
assert {_timespan} is 0.5d with "Failed to set variable to 0.5d"
Asleeepp marked this conversation as resolved.
Show resolved Hide resolved

set {_timespan} to 90m
assert {_timespan} is a timespan with "Failed to parse '90m'"
assert {_timespan} is 90m with "Failed to set variable to 90m"

set {_timespan} to 3600s
assert {_timespan} is a timespan with "Failed to parse '3600s'"
assert {_timespan} is 3600s with "Failed to set variable to '3600s'"