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

🚀 Add timespan details expression & Improvements #4661

Merged
merged 35 commits into from
Mar 19, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
870e8ff
🚀 Add timespan details expression
AyhamAl-Ali Mar 12, 2022
5f7f92e
Merge branch 'master' into ench/timespan-improve
AyhamAl-Ali Mar 13, 2022
1669cfa
Changes
AyhamAl-Ali Mar 15, 2022
8fe9d0a
Improvements
AyhamAl-Ali Mar 18, 2022
9367856
Changes
AyhamAl-Ali Mar 20, 2022
cf00390
Add DD:HH:MM:SS.ms + improve toString
AyhamAl-Ali Mar 21, 2022
68a2096
Merge branch 'master' into ench/timespan-improve
AyhamAl-Ali Jun 24, 2022
8af2008
Update src/main/java/ch/njol/skript/util/Timespan.java
AyhamAl-Ali Jul 9, 2022
fdd2c9c
Update src/main/java/ch/njol/skript/util/Timespan.java
AyhamAl-Ali Jul 9, 2022
bf52019
Update src/main/java/ch/njol/skript/util/Timespan.java
AyhamAl-Ali Jul 9, 2022
3a4d6c3
Update src/main/java/ch/njol/skript/util/Timespan.java
AyhamAl-Ali Jul 9, 2022
a32ca2a
Testing
AyhamAl-Ali Jul 9, 2022
6372da2
Improvements - Unfinished code
AyhamAl-Ali Jul 16, 2022
0ec45dd
Update enums
AyhamAl-Ali Jan 1, 2023
9f4bbfb
Merge remote-tracking branch 'AyhamAl-Ali/ench/timespan-improve' into…
AyhamAl-Ali Jan 1, 2023
53ea288
Merge branch 'master' into ench/timespan-improve
AyhamAl-Ali Jan 1, 2023
207f2d7
Fix building
AyhamAl-Ali Jan 1, 2023
4745e8e
Merge remote-tracking branch 'AyhamAl-Ali/ench/timespan-improve' into…
AyhamAl-Ali Jan 1, 2023
56a77af
Test build
AyhamAl-Ali Jan 1, 2023
df3d9a9
Test 2
AyhamAl-Ali Jan 1, 2023
78e8b0e
Fix code
AyhamAl-Ali Jan 1, 2023
0ce18e9
Requested Changes & Improvements
AyhamAl-Ali Apr 18, 2023
6c5683d
Requested Changes
AyhamAl-Ali Apr 19, 2023
fcdb1b6
Merge branch 'master' into ench/timespan-improve
AyhamAl-Ali Apr 19, 2023
201b546
Pikachu's quality requested changes 🚀
AyhamAl-Ali Apr 20, 2023
37afcb9
Merge remote-tracking branch 'AyhamAl-Ali/ench/timespan-improve' into…
AyhamAl-Ali Apr 20, 2023
0283bea
Merge branch 'master' into ench/timespan-improve
TheLimeGlass Aug 5, 2023
2160bdb
Merge remote-tracking branch 'origin/dev/feature' into ench/timespan-…
AyhamAl-Ali Oct 5, 2023
63b281f
Apply suggestions
AyhamAl-Ali Oct 5, 2023
a2be2b1
typo
AyhamAl-Ali Oct 5, 2023
6be6300
Update src/main/java/ch/njol/skript/expressions/ExprTimespanDetails.java
AyhamAl-Ali Dec 15, 2023
2ac1b90
Merge branch 'dev/feature' into ench/timespan-improve
AyhamAl-Ali Dec 30, 2023
2b18a04
Add Timespan(TimePeriod, long) and deprecate fromTicks
AyhamAl-Ali Jan 21, 2024
1b845c7
Add getAs(TimePeriod)
AyhamAl-Ali Jan 21, 2024
001c9e3
Merge branch 'dev/feature' into ench/timespan-improve
sovdeeth Mar 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions src/main/java/ch/njol/skript/expressions/ExprTimespanDetails.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/**
* This file is part of Skript.
*
* Skript is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Skript is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Skript. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright Peter Güttinger, SkriptLang team and contributors
*/
package ch.njol.skript.expressions;

import ch.njol.skript.doc.Description;
import ch.njol.skript.doc.Examples;
import ch.njol.skript.doc.Name;
import ch.njol.skript.doc.Since;
import ch.njol.skript.expressions.base.SimplePropertyExpression;
import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.SkriptParser.ParseResult;
import ch.njol.skript.util.Timespan;
import ch.njol.util.Kleenean;
import org.eclipse.jdt.annotation.Nullable;

@Name("Timespan Details")
@Description("Retrieve specific information of a <a href=\"/classes.html#timespan\">timespan</a> such as hours/minutes/etc.")
@Examples({
"set {_t} to difference between now and {Payouts::players::%uuid of player%::last-date}",
"send \"It has been %days of {_t}% day(s) since last payout.\""
})
@Since("INSERT VERSION")
public class ExprTimespanDetails extends SimplePropertyExpression<Timespan, Long> {

static {
register(ExprTimespanDetails.class, Long.class, "(0:tick[s]|1:second[s]|2:minute[s]|3:hour[s]|4:day[s]|5:week[s]|6:month[s]|7:year[s])", "timespans");
AyhamAl-Ali marked this conversation as resolved.
Show resolved Hide resolved
}

private final int TICKS = 0, SECONDS = 1, MINUTES = 2, HOURS = 3, DAYS = 4, WEEKS = 5, MONTHS = 6, YEARS = 7;
AyhamAl-Ali marked this conversation as resolved.
Show resolved Hide resolved
private int type;

@Override
@SuppressWarnings("unchecked")
public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) {
setExpr((Expression<? extends Timespan>) exprs[0]);
AyhamAl-Ali marked this conversation as resolved.
Show resolved Hide resolved
type = parseResult.mark;
return true;
AyhamAl-Ali marked this conversation as resolved.
Show resolved Hide resolved
}

@Override
@Nullable
public Long convert(Timespan t) {
AyhamAl-Ali marked this conversation as resolved.
Show resolved Hide resolved
if (type == YEARS)
AyhamAl-Ali marked this conversation as resolved.
Show resolved Hide resolved
return t.getYears();
if (type == MONTHS)
return t.getMonths();
if (type == WEEKS)
return t.getWeeks();
if (type == DAYS)
return t.getDays();
if (type == HOURS)
return t.getHours();
if (type == MINUTES)
return t.getMinutes();
if (type == SECONDS)
return t.getSeconds();

return t.getTicks_i();
}

@Override
protected String getPropertyName() {
if (type == YEARS)
AyhamAl-Ali marked this conversation as resolved.
Show resolved Hide resolved
return "years";
if (type == MONTHS)
return "months";
if (type == WEEKS)
return "weeks";
if (type == DAYS)
return "days";
if (type == HOURS)
return "hours";
if (type == MINUTES)
return "minutes";
if (type == SECONDS)
return "seconds";

return "ticks";
}

@Override
public Class<? extends Long> getReturnType() {
AyhamAl-Ali marked this conversation as resolved.
Show resolved Hide resolved
return Long.class;
}

}
140 changes: 95 additions & 45 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 java.util.HashMap;
import java.util.regex.Pattern;

import org.eclipse.jdt.annotation.Nullable;

Expand All @@ -31,30 +32,61 @@
import ch.njol.util.coll.CollectionUtils;
import ch.njol.yggdrasil.YggdrasilSerializable;

/**
* @author Peter Güttinger
* @edited by Mirreducki. Increased maximum timespan.
*/
public class Timespan implements YggdrasilSerializable, Comparable<Timespan> { // REMIND unit

private final static Noun m_tick = new Noun("time.tick");
private final static Noun m_second = new Noun("time.second");
private final static Noun m_minute = new Noun("time.minute");
private final static Noun m_hour = new Noun("time.hour");
private final static Noun m_day = new Noun("time.day");
private final static Noun m_week = new Noun("time.week");
private final static Noun m_month = new Noun("time.month");
private final static Noun m_year = new Noun("time.year");
final static Noun[] names = {m_tick, m_second, m_minute, m_hour, m_day, m_week, m_month, m_year};
final static long[] times = {50L, 1000L, 1000L * 60L, 1000L * 60L * 60L, 1000L * 60L * 60L * 24L, 1000L * 60L * 60L * 24L * 7L, 1000L * 60L * 60L * 24L * 30L, 1000L * 60L * 60L * 24L * 365L};
final static HashMap<String, Long> parseValues = new HashMap<>();

public enum Times {
AyhamAl-Ali marked this conversation as resolved.
Show resolved Hide resolved

TICK(new Noun("time.tick"), 50L),
SECOND(new Noun("time.second"), 1000L),
MINUTE(new Noun("time.minute"), SECOND.getTime() * 60L),
HOUR(new Noun("time.hour"), MINUTE.getTime() * 60L),
DAY(new Noun("time.day"), HOUR.getTime() * 24L),
WEEK(new Noun("time.week"), DAY.getTime() * 7L),
MONTH(new Noun("time.month"), DAY.getTime() * 30L),
YEAR(new Noun("time.year"), DAY.getTime() * 365L);

private final Noun name;
private final long time;

Times(Noun name, long time) {
AyhamAl-Ali marked this conversation as resolved.
Show resolved Hide resolved
this.name = name;
this.time = time;
}

public Noun getName() {
return name;
}

public long getTime() {
return time;
}

}

public static final Pattern TIMESPAN_PATTERN = Pattern.compile("^\\d+:\\d\\d(:\\d\\d)?(\\.\\d{1,4})?$");
public static final Pattern TIMESPAN_NUMBER_PATTERN = Pattern.compile("^\\d+(\\.\\d+)?$");
private static final HashMap<String, Long> parseValues = new HashMap<>();
private final long millis;
AyhamAl-Ali marked this conversation as resolved.
Show resolved Hide resolved

@SuppressWarnings("unchecked")
final static NonNullPair<Noun, Long>[] simpleValues = new NonNullPair[] {
AyhamAl-Ali marked this conversation as resolved.
Show resolved Hide resolved
new NonNullPair<>(Times.YEAR.getName(), Times.YEAR.getTime()),
new NonNullPair<>(Times.MONTH.getName(), Times.MONTH.getTime()),
new NonNullPair<>(Times.WEEK.getName(), Times.WEEK.getTime()),
new NonNullPair<>(Times.DAY.getName(), Times.DAY.getTime()),
new NonNullPair<>(Times.HOUR.getName(), Times.HOUR.getTime()),
new NonNullPair<>(Times.MINUTE.getName(), Times.MINUTE.getTime()),
new NonNullPair<>(Times.SECOND.getName(), Times.SECOND.getTime())
};

static {
Language.addListener(new LanguageChangeListener() {
@Override
public void onLanguageChange() {
for (int i = 0; i < names.length; i++) {
parseValues.put(names[i].getSingular().toLowerCase(), times[i]);
parseValues.put(names[i].getPlural().toLowerCase(), times[i]);
for (Times t : Times.values()) {
AyhamAl-Ali marked this conversation as resolved.
Show resolved Hide resolved
parseValues.put(t.getName().getSingular().toLowerCase(), t.getTime());
parseValues.put(t.getName().getPlural().toLowerCase(), t.getTime());
}
}
});
Expand All @@ -64,16 +96,18 @@ public void onLanguageChange() {
public static Timespan parse(final String s) {
if (s.isEmpty())
return null;

long t = 0;
boolean minecraftTime = false;
boolean isMinecraftTimeSet = false;
if (s.matches("^\\d+:\\d\\d(:\\d\\d)?(\\.\\d{1,4})?$")) { // MM:SS[.ms] or HH:MM:SS[.ms]

if (TIMESPAN_PATTERN.matcher(s).matches()) { // MM:SS[.ms] or HH:MM:SS[.ms]
final String[] ss = s.split("[:.]");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be better to use regex groups for this instead? Would probably simplify the code below quite a bit

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds a little complex and out-of-scope, maybe in another PR

final long[] times = {1000L * 60L * 60L, 1000L * 60L, 1000L, 1L}; // h, m, s, ms
final long[] times = {Times.HOUR.getTime(), Times.MINUTE.getTime(), Times.SECOND.getTime(), 1L}; // h, m, s, ms

final int offset = ss.length == 3 && !s.contains(".") || ss.length == 4 ? 0 : 1;
for (int i = 0; i < ss.length; i++) {
t += times[offset + i] * Utils.parseLong("" + ss[i]);
t += times[offset + i] * Utils.parseLong("" + ss[i]);
}
} else { // <number> minutes/seconds/.. etc
final String[] subs = s.toLowerCase().split("\\s+");
Expand All @@ -90,9 +124,8 @@ public static Timespan parse(final String s) {
if (Noun.isIndefiniteArticle(sub)) {
if (i == subs.length - 1)
return null;
amount = 1;
sub = subs[++i];
} else if (sub.matches("^\\d+(\\.\\d+)?$")) {
} else if (TIMESPAN_NUMBER_PATTERN.matcher(sub).matches()) {
if (i == subs.length - 1)
return null;
try {
Expand Down Expand Up @@ -121,7 +154,7 @@ public static Timespan parse(final String s) {
if (d == null)
return null;

if (minecraftTime && d != times[0]) // times[0] == tick
if (minecraftTime && d != Times.TICK.getTime()) // times[0] == tick
amount /= 72f;

t += Math.round(amount * d);
Expand All @@ -130,11 +163,10 @@ public static Timespan parse(final String s) {

}
}

return new Timespan(t);
}

private final long millis;

public Timespan() {
millis = 0;
}
Expand All @@ -147,34 +179,62 @@ public Timespan(final long millis) {

/**
* @deprecated Use fromTicks_i(long ticks) instead. Since this method limits timespan to 50 * Integer.MAX_VALUE.
* @addon I only keep this to allow for older addons to still work. / Mirre
* Keeping this for older addons to stay working.
*/
@Deprecated
public static Timespan fromTicks(final int ticks) {
return new Timespan(ticks * 50L);
return new Timespan(ticks * Times.TICK.getTime());
}

public static Timespan fromTicks_i(final long ticks) {
return new Timespan(ticks * 50L);
return new Timespan(ticks * Times.TICK.getTime());
}

public long getMilliSeconds() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it's worth adding a default TimePeriod.MILLISECOND option

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure about that, It's just a 1L which doesn't have much use in math/comparison otherwise let me know :)

return millis;
}

public long getTicks_i() {
AyhamAl-Ali marked this conversation as resolved.
Show resolved Hide resolved
return Math.round((millis / 50.0));
return Math.round((millis / 50f));
AyhamAl-Ali marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* @deprecated Use getTicks_i() instead. Since this method limits timespan to Integer.MAX_VALUE.
* @addon I only keep this to allow for older addons to still work. / Mirre
* @Well if need the ticks because of a method that takes a int input it doesn't really matter.
* @deprecated Use getTicks_i() instead. This method limits timespan to Integer.MAX_VALUE.
* Keeping this for older addons to stay working.
* If you need the ticks for a method that takes an int input then it wouldn't matter.
*/
@Deprecated
public int getTicks() {
return Math.round((millis >= Float.MAX_VALUE ? Float.MAX_VALUE : millis) / 50f);
AyhamAl-Ali marked this conversation as resolved.
Show resolved Hide resolved
}

public long getSeconds() {
return millis / Times.SECOND.getTime();
}

public long getMinutes() {
return millis / Times.MINUTE.getTime();
}

public long getHours() {
return millis / Times.HOUR.getTime();
}

public long getDays() {
return millis / Times.DAY.getTime();
}

public long getWeeks() {
return millis / Times.WEEK.getTime();
}

public long getMonths() {
return millis / Times.MONTH.getTime();
}

public long getYears() {
return millis / Times.YEAR.getTime();
}

@Override
public String toString() {
Expand All @@ -185,14 +245,6 @@ public String toString(final int flags) {
return toString(millis, flags);
}

@SuppressWarnings("unchecked")
final static NonNullPair<Noun, Long>[] simpleValues = new NonNullPair[] {
new NonNullPair<>(m_day, 1000L * 60 * 60 * 24),
new NonNullPair<>(m_hour, 1000L * 60 * 60),
new NonNullPair<>(m_minute, 1000L * 60),
new NonNullPair<>(m_second, 1000L)
};

public static String toString(final long millis) {
return toString(millis, 0);
}
Expand Down Expand Up @@ -238,10 +290,8 @@ public boolean equals(final @Nullable Object obj) {
return false;
if (!(obj instanceof Timespan))
return false;
final Timespan other = (Timespan) obj;
if (millis != other.millis)
return false;
return true;

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

}