1
mirror of https://github.com/carm-outsource/TimeReward.git synced 2026-06-04 07:18:16 +08:00

fix(time): 修复可能出现的时间异常问题

This commit is contained in:
2025-05-05 08:15:01 +08:00
parent e155659455
commit 451d6b9fa2
9 changed files with 221 additions and 121 deletions
@@ -14,6 +14,7 @@ import cc.carm.plugin.timereward.listener.UserListener;
import cc.carm.plugin.timereward.manager.RewardManager;
import cc.carm.plugin.timereward.manager.UserManager;
import cc.carm.plugin.timereward.storage.database.MySQLStorage;
import cc.carm.plugin.timereward.util.DateTimeUtils;
import org.bstats.bukkit.Metrics;
import org.bukkit.Bukkit;
import org.bukkit.entity.Entity;
@@ -49,6 +50,8 @@ public class Main extends EasyPlugin {
this.rewardProvider = BukkitConfigFactory.from(new File(getDataFolder(), "rewards.yml")).build();
this.rewardProvider.initialize(RewardsConfig.class);
DateTimeUtils.WEEK_START_DAY = PluginConfig.WEEK_FIRST_DAY::resolve;
}
@Override
@@ -25,7 +25,7 @@ public interface PluginConfig extends Configuration {
"检查更新为异步操作,绝不会影响性能与使用体验。"
})
ConfiguredValue<Boolean> CHECK_UPDATE = ConfiguredValue.of(Boolean.class, true);
@HeaderComments({
"自动保存设定,用于设置自动保存的时间间隔,单位为秒(小于等于0则关闭)。",
"一般来说,玩家会在退出游戏时进行保存。",
@@ -33,6 +33,13 @@ public interface PluginConfig extends Configuration {
})
ConfiguredValue<Long> AUTO_SAVE = ConfiguredValue.of(Long.class, 60L);
@HeaderComments({
"是否在保存时优先使用数据库中的数据。",
"若启用,则会在保存数据时在数据库中重新读取数据计算时间,以确利用最新数据;",
"若关闭,则保存时会直接采用进入服务器时所加载的数据进行计算。"
})
ConfiguredValue<Boolean> USE_STORAGE_DATA = ConfiguredValue.of(Boolean.class, true);
@HeaderComments("周起始日,用于判断周度奖励的结算日期。")
ConfiguredValue<DayOfWeek> WEEK_FIRST_DAY = ConfiguredValue.builderOf(DayOfWeek.class)
.from(Integer.class)
@@ -1,5 +1,6 @@
package cc.carm.plugin.timereward.data;
import cc.carm.plugin.timereward.util.DateTimeUtils;
import org.jetbrains.annotations.NotNull;
import java.time.Duration;
@@ -11,62 +12,64 @@ import java.util.function.Predicate;
public enum IntervalType {
TOTAL(
0, time -> false,
0, Duration.ofSeconds(4294967295L), time -> false,
(timeRecord, join) -> Duration.between(join, LocalDateTime.now()).plus(timeRecord.getTotalTime())
),
DAILY(
1, time -> time.isBefore(TimeRecord.getTodayStart()),
1, Duration.ofDays(1), time -> DateTimeUtils.sameDay(time.toLocalDate()),
(timeRecord, join) -> {
LocalDateTime now = LocalDateTime.now();
if (now.toLocalDate().isEqual(timeRecord.getDate())) {
// 和记录还在同一天
return Duration.between(join, now).plus(timeRecord.getDailyTime());
} else if (now.toLocalDate().isEqual(join.toLocalDate())) {
// 加入的时间和现在的时间在同一天
return Duration.between(join, now);
} else {
if (!now.toLocalDate().isEqual(join.toLocalDate())) {
// 加入的时间和现在的时间不在同一天
return Duration.between(TimeRecord.getTodayStart(), now);
return Duration.between(DateTimeUtils.todayStartTime(), now);
}
if (now.toLocalDate().isEqual(timeRecord.getDate())) { // 和记录还在同一天
return Duration.between(join, now).plus(timeRecord.getDailyTime());
}
return Duration.between(join, now); // 加入的时间和现在的时间在同一天
}
),
WEEKLY(
2, time -> time.isBefore(TimeRecord.getThisWeekStart()),
(timeRecord, join) -> {
2, Duration.ofDays(7),
time -> !DateTimeUtils.sameWeek(time),
(r, join) -> {
LocalDateTime now = LocalDateTime.now();
if (TimeRecord.isSameWeek(timeRecord.getDate(), now)) {
return Duration.between(join, now).plus(timeRecord.getWeeklyTime());
} else if (TimeRecord.isSameWeek(join, now)) {
return Duration.between(join, now);
} else {
return Duration.between(TimeRecord.getThisWeekStart(), now);
if (!DateTimeUtils.sameWeek(join, now)) {
return Duration.between(DateTimeUtils.currentWeekStartTime(), now);
}
if (DateTimeUtils.sameWeek(r.getDate(), now)) {
return Duration.between(join, now).plus(r.getWeeklyTime());
}
return Duration.between(join, now);
}
),
MONTHLY(
3, time -> time.isBefore(TimeRecord.getThisMonthStart()),
(timeRecord, join) -> {
3, Duration.ofDays(31),
time -> !DateTimeUtils.sameMonth(time.toLocalDate()),
(r, join) -> {
LocalDateTime now = LocalDateTime.now();
if (TimeRecord.isSameMonth(timeRecord.getDate(), now.toLocalDate())) {
return Duration.between(join, now).plus(timeRecord.getMonthlyTime());
} else if (TimeRecord.isSameMonth(join.toLocalDate(), now.toLocalDate())) {
return Duration.between(join, now);
} else {
return Duration.between(TimeRecord.getThisMonthStart(), now);
if (!DateTimeUtils.sameMonth(join.toLocalDate(), now.toLocalDate())) {
return Duration.between(DateTimeUtils.currentMonthStartTime(), now);
}
if (DateTimeUtils.sameMonth(r.getDate(), now.toLocalDate())) {
return Duration.between(join, now).plus(r.getMonthlyTime());
}
return Duration.between(join, now);
}
);
private final int id;
private final @NotNull Duration maxDuration;
private final @NotNull Predicate<LocalDateTime> periodChangePredicate;
private final @NotNull BiFunction<TimeRecord, LocalDateTime, Duration> calculator;
IntervalType(int id,
IntervalType(int id, @NotNull Duration maxDuration,
@NotNull Predicate<LocalDateTime> periodChangePredicate,
@NotNull BiFunction<TimeRecord, LocalDateTime, Duration> calculator) {
this.id = id;
this.maxDuration = maxDuration;
this.periodChangePredicate = periodChangePredicate;
this.calculator = calculator;
}
@@ -75,20 +78,26 @@ public enum IntervalType {
return id;
}
public @NotNull BiFunction<TimeRecord, LocalDateTime, Duration> getCalculator() {
public @NotNull Duration getMaxDuration() {
return maxDuration;
}
public @NotNull BiFunction<TimeRecord, LocalDateTime, Duration> calculator() {
return calculator;
}
public @NotNull Predicate<LocalDateTime> getPreiodChangePredicate() {
public @NotNull Predicate<LocalDateTime> changePredicator() {
return periodChangePredicate;
}
public Duration calculate(@NotNull TimeRecord timeRecord, @NotNull LocalDateTime joinTime) {
return calculator.apply(timeRecord, joinTime);
Duration result = calculator.apply(timeRecord, joinTime);
// 如果超过最大值,则返回最大值
return result.compareTo(maxDuration) > 0 ? maxDuration : result;
}
public boolean isPeriodChanged(@NotNull LocalDateTime claimedDate) {
return periodChangePredicate.test(claimedDate);
return changePredicator().test(claimedDate);
}
public static IntervalType parse(String input) {
@@ -1,14 +1,10 @@
package cc.carm.plugin.timereward.data;
import cc.carm.plugin.timereward.conf.PluginConfig;
import cc.carm.plugin.timereward.util.DateTimeUtils;
import org.jetbrains.annotations.NotNull;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalField;
import java.time.temporal.WeekFields;
public class TimeRecord {
@@ -16,7 +12,6 @@ public class TimeRecord {
return new TimeRecord(LocalDate.now(), 0, 0, 0, 0);
}
protected final @NotNull LocalDate date;
protected final @NotNull Duration daily;
@@ -68,37 +63,14 @@ public class TimeRecord {
public boolean isWeekUpdated() {
if (!isDayUpdated()) return false; // Same day always same week
return !isSameWeek(LocalDate.now(), getDate());
return !DateTimeUtils.sameWeek(LocalDate.now(), getDate());
}
public boolean isMonthUpdated() {
if (!isDayUpdated()) return false; // Same day always same month
// Predicate current month is after the month of the date
return LocalDate.now().getMonth().compareTo(getDate().getMonth()) > 0;
return !DateTimeUtils.sameMonth(LocalDate.now(), getDate());
}
public static boolean isSameWeek(Temporal a, Temporal b) {
TemporalField woy = WeekFields.of(PluginConfig.WEEK_FIRST_DAY.getNotNull(), 4).weekOfWeekBasedYear();
return a.get(woy) == b.get(woy);
}
public static boolean isSameMonth(LocalDate a, LocalDate b) {
return a.getMonth().equals(b.getMonth());
}
public static LocalDateTime getTodayStart() {
return LocalDate.now().atTime(0, 0);
}
public static LocalDateTime getThisWeekStart() {
TemporalField woy = WeekFields.of(PluginConfig.WEEK_FIRST_DAY.getNotNull(), 4).weekOfWeekBasedYear();
return LocalDate.now().with(woy, 1).atTime(0, 0);
}
public static LocalDateTime getThisMonthStart() {
return LocalDate.now().withDayOfMonth(1).atTime(0, 0);
}
@Override
public String toString() {
@@ -22,7 +22,7 @@ public enum DatabaseTables implements SQLTable {
table.addColumn("daily_time", "MEDIUMINT UNSIGNED NOT NULL DEFAULT 0"); // 用户日在线时间(秒)
table.addColumn("weekly_time", "MEDIUMINT UNSIGNED NOT NULL DEFAULT 0"); // 用户周在线时间(秒)
table.addColumn("monthly_time", "INT UNSIGNED NOT NULL DEFAULT 0"); // 用户月在线时间(秒)
table.addColumn("monthly_time", "MEDIUMINT UNSIGNED NOT NULL DEFAULT 0"); // 用户月在线时间(秒)
table.addColumn("total_time", "INT UNSIGNED NOT NULL DEFAULT 0"); // 用户总在线时间(秒)
table.addColumn("update",
@@ -0,0 +1,73 @@
package cc.carm.plugin.timereward.util;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAdjusters;
import java.time.temporal.TemporalField;
import java.time.temporal.WeekFields;
import java.util.function.Supplier;
public class DateTimeUtils {
public static Supplier<DayOfWeek> WEEK_START_DAY = () -> DayOfWeek.MONDAY;
public static boolean sameDay(LocalDate a) {
return sameDay(a, LocalDate.now());
}
public static boolean sameDay(LocalDate a, LocalDate b) {
return a.getDayOfYear() == b.getDayOfYear() && a.getYear() == b.getYear();
}
public static boolean sameWeek(Temporal a) {
return sameWeek(WEEK_START_DAY.get(), a, LocalDate.now());
}
public static boolean sameWeek(DayOfWeek weekStart, Temporal a) {
return sameWeek(weekStart, a, LocalDate.now());
}
public static boolean sameWeek(Temporal a, Temporal b) {
return sameWeek(WEEK_START_DAY.get(), a, b);
}
public static boolean sameWeek(DayOfWeek weekStart, Temporal a, Temporal b) {
TemporalField woy = WeekFields.of(weekStart, 4).weekOfWeekBasedYear();
return a.get(woy) == b.get(woy);
}
public static boolean sameMonth(LocalDate a) {
return sameMonth(a, LocalDate.now());
}
public static boolean sameMonth(LocalDate a, LocalDate b) {
return a.getMonth().equals(b.getMonth());
}
public static LocalDateTime todayStartTime() {
return LocalDate.now().atTime(0, 0);
}
public static LocalDate currentWeekStartDay(DayOfWeek weekStart) {
return LocalDate.now().with(TemporalAdjusters.previousOrSame(weekStart));
}
public static LocalDate currentWeekStartDay() {
return currentWeekStartDay(WEEK_START_DAY.get());
}
public static LocalDateTime currentWeekStartTime(DayOfWeek weekStart) {
return currentWeekStartDay(weekStart).atTime(0, 0);
}
public static LocalDateTime currentWeekStartTime() {
return currentWeekStartTime(WEEK_START_DAY.get());
}
public static LocalDateTime currentMonthStartTime() {
return LocalDate.now().withDayOfMonth(1).atTime(0, 0);
}
}
-57
View File
@@ -1,57 +0,0 @@
import org.jetbrains.annotations.NotNull;
import org.junit.Test;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.temporal.TemporalField;
import java.time.temporal.WeekFields;
public class TimeTest {
LocalDate date = LocalDate.of(2023, 8, 30);
@Test
public void test() {
LocalDate date1 = LocalDate.of(2023, 9, 2);
LocalDate date2 = LocalDate.of(2023, 8, 28);
LocalDate date3 = LocalDate.of(2023, 8, 27);
System.out.println(isDayUpdated(date1));
System.out.println(isWeekUpdated(date1));
System.out.println(isMonthUpdated(date1));
System.out.println(isDayUpdated(date2));
System.out.println(isWeekUpdated(date2));
System.out.println(isMonthUpdated(date2));
System.out.println(isDayUpdated(date3));
System.out.println(isWeekUpdated(date3));
System.out.println(isMonthUpdated(date3));
System.out.println(isDayUpdated(date));
}
public @NotNull LocalDate getDate() {
return date;
}
public boolean isDayUpdated(LocalDate d) {
return !d.equals(getDate());
}
public boolean isWeekUpdated(LocalDate d) {
if (!isDayUpdated(d)) return false; // Same day always same week
TemporalField woy = WeekFields.of(DayOfWeek.MONDAY, 4).weekOfWeekBasedYear();
return getDate().get(woy) != d.get(woy);
}
public boolean isMonthUpdated(LocalDate d) {
if (!isDayUpdated(d)) return false; // Same day always same month
// Predicate current month is after the month of the date
return d.getMonth().compareTo(getDate().getMonth()) > 0;
}
}
@@ -0,0 +1,52 @@
package cc.carm.plugin.tests;
import cc.carm.plugin.timereward.data.IntervalType;
import cc.carm.plugin.timereward.data.TimeRecord;
import cc.carm.plugin.timereward.user.UserRewardData;
import cc.carm.plugin.timereward.util.DateTimeUtils;
import org.junit.Test;
import java.time.DayOfWeek;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.UUID;
public class DataTest {
DateTimeFormatter DATETIME = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@Test
public void test() {
LocalDate date = LocalDate.of(2025, 5, 1);
LocalDateTime dateTime = LocalDateTime.now().minusHours(10).minusSeconds(600);
System.out.println("-----------------------------------------");
System.out.println(" DURATION: " + Duration.between(dateTime, LocalDateTime.now()).getSeconds());
System.out.println("TODAY START: " + DATETIME.format(DateTimeUtils.todayStartTime()));
System.out.println("WEEK START: " + DATETIME.format(DateTimeUtils.currentWeekStartTime()));
System.out.println("MONTH START: " + DATETIME.format(DateTimeUtils.currentMonthStartTime()));
System.out.println("-----------------------------------------");
System.out.println("RECORD DATE: " + DateTimeFormatter.ofPattern("yyyy-MM-dd").format(date));
System.out.println(" JOIN TIME: " + DATETIME.format(dateTime));
System.out.println("-----------------------------------------");
UserRewardData data = createData(date, dateTime);
for (IntervalType value : IntervalType.values()) {
System.out.println(value.name() + " = " + data.getOnlineDuration(value).getSeconds());
}
}
static UserRewardData createData(LocalDate date, LocalDateTime join) {
return new UserRewardData(
UUID.randomUUID(), new HashMap<>(),
new TimeRecord(date, 0, 0, 0, 0),
join
);
}
}
@@ -0,0 +1,41 @@
package cc.carm.plugin.tests;
import cc.carm.plugin.timereward.data.IntervalType;
import cc.carm.plugin.timereward.data.TimeRecord;
import org.junit.Test;
import java.time.LocalDate;
public class TimeTest {
LocalDate date = LocalDate.of(2023, 8, 30);
@Test
public void test() {
TimeRecord record1 = new TimeRecord(LocalDate.now().minusMonths(1), 0, 0, 0, 0);
TimeRecord record2 = new TimeRecord(LocalDate.now().minusWeeks(1), 0, 0, 0, 0);
TimeRecord record3 = new TimeRecord(LocalDate.now().minusDays(1), 0, 0, 0, 0);
System.out.println(record1.isDayUpdated());
System.out.println(record1.isWeekUpdated());
System.out.println(record1.isMonthUpdated());
System.out.println(record2.isDayUpdated());
System.out.println(record2.isWeekUpdated());
System.out.println(record2.isMonthUpdated());
System.out.println(record3.isDayUpdated());
System.out.println(record3.isWeekUpdated());
System.out.println(record3.isMonthUpdated());
}
@Test
public void durationTest() {
for (IntervalType value : IntervalType.values()) {
System.out.println(value.name() + " = <MAX> = " + value.getMaxDuration().getSeconds());
}
}
}