diff --git a/src/main/java/run/halo/app/service/impl/CommentBlackListServiceImpl.java b/src/main/java/run/halo/app/service/impl/CommentBlackListServiceImpl.java index f01979412..30f80debc 100644 --- a/src/main/java/run/halo/app/service/impl/CommentBlackListServiceImpl.java +++ b/src/main/java/run/halo/app/service/impl/CommentBlackListServiceImpl.java @@ -10,9 +10,9 @@ import run.halo.app.repository.PostCommentRepository; import run.halo.app.service.CommentBlackListService; import run.halo.app.service.OptionService; import run.halo.app.service.base.AbstractCrudService; +import run.halo.app.utils.DateTimeUtils; import java.time.LocalDateTime; -import java.time.ZoneId; import java.util.Date; import java.util.Optional; @@ -26,7 +26,6 @@ import java.util.Optional; @Service @Slf4j public class CommentBlackListServiceImpl extends AbstractCrudService implements CommentBlackListService { - private static final ZoneId ZONE_ID = ZoneId.of("Asia/Shanghai"); private final CommentBlackListRepository commentBlackListRepository; private final PostCommentRepository postCommentRepository; private final OptionService optionService; @@ -50,10 +49,9 @@ public class CommentBlackListServiceImpl extends AbstractCrudService blackList = commentBlackListRepository.findByIpAddress(ipAddress); LocalDateTime now = LocalDateTime.now(); - Date endTime = new Date(now.atZone(ZONE_ID).toInstant().toEpochMilli()); + Date endTime = new Date(DateTimeUtils.toEpochMilli(now)); Integer banTime = optionService.getByPropertyOrDefault(CommentProperties.COMMENT_BAN_TIME, Integer.class, 10); - Date startTime = new Date(now.minusMinutes(banTime) - .atZone(ZONE_ID).toInstant().toEpochMilli()); + Date startTime = new Date(DateTimeUtils.toEpochMilli(now.minusMinutes(banTime))); Integer range = optionService.getByPropertyOrDefault(CommentProperties.COMMENT_RANGE, Integer.class, 30); boolean isPresent = postCommentRepository.countByIpAndTime(ipAddress, startTime, endTime) >= range; if (isPresent && blackList.isPresent()) { @@ -61,10 +59,10 @@ public class CommentBlackListServiceImpl extends AbstractCrudService result <= 0).ifPresent(result -> log.error("更新评论封禁时间失败")); + .filter(result -> result <= 0).ifPresent(result -> log.error("更新评论封禁时间失败")); } private Date getBanTime(LocalDateTime localDateTime, Integer banTime) { - return new Date(localDateTime.plusMinutes(banTime).atZone(ZONE_ID).toInstant().toEpochMilli()); + return new Date(DateTimeUtils.toEpochMilli(localDateTime.plusMinutes(banTime))); } } diff --git a/src/main/java/run/halo/app/utils/DateTimeUtils.java b/src/main/java/run/halo/app/utils/DateTimeUtils.java new file mode 100644 index 000000000..e5c387156 --- /dev/null +++ b/src/main/java/run/halo/app/utils/DateTimeUtils.java @@ -0,0 +1,543 @@ +package run.halo.app.utils; + +import java.time.*; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.temporal.ChronoField; +import java.time.temporal.Temporal; + +import static cn.hutool.core.date.DatePattern.*; + +/** + * 日期工具 + * + * @author LeiXinXin + * @date 2019/12/10 + */ +public class DateTimeUtils { + /** + * 标准日期格式 {@link DateTimeFormatter}:yyyyMMddHHmmssSSS + */ + public static final DateTimeFormatter PURE_DATETIME_MS_FORMATTER = new DateTimeFormatterBuilder() + .appendPattern(PURE_DATETIME_PATTERN) + .appendValue(ChronoField.MILLI_OF_SECOND, 3) + .toFormatter(); + /** + * 标准日期格式 {@link DateTimeFormatter}:yyyyMMdd + */ + public static final DateTimeFormatter PURE_DATE_FORMATTER = DateTimeFormatter.ofPattern(PURE_DATE_PATTERN); + /** + * 标准日期格式 {@link DateTimeFormatter}:yyyy-MM-dd + */ + public static final DateTimeFormatter NORM_DATE_FORMATTER = DateTimeFormatter.ofPattern(NORM_DATE_PATTERN); + /** + * 标准日期格式:HHmm + */ + public final static String TIME_PATTERN = "HHmm"; + /** + * 标准日期格式 {@link DateTimeFormatter} HHmm + */ + public static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern(TIME_PATTERN); + /** + * 标准日期格式:HH:mm + */ + public final static String NORM_TIME_PATTERN = "HH:mm"; + /** + * 标准日期格式 {@link DateTimeFormatter} HH:mm + */ + public static final DateTimeFormatter NORM_TIME_FORMATTER = DateTimeFormatter.ofPattern(NORM_TIME_PATTERN); + /** + * 标准日期时间格式,精确到秒 {@link DateTimeFormatter}:yyyy-MM-dd HH:mm:ss + */ + public static final DateTimeFormatter NORM_DATETIME_FORMATTER = DateTimeFormatter.ofPattern(NORM_DATETIME_PATTERN); + /** + * 上海时区格式 + */ + public static final String CTT = ZoneId.SHORT_IDS.get("CTT"); + /** + * 上海时区 + */ + public static final ZoneId CTT_ZONE_ID = ZoneId.of(CTT); + + + private DateTimeUtils() { + } + + /** + * 获取当前时间,默认为上海时区 + * + * @return Now LocalDateTime + */ + public static LocalDateTime now() { + return now(CTT_ZONE_ID); + } + + /** + * 根据时区获取当前时间 + * + * @param zoneId 时区 + * @return Now LocalDateTime + */ + public static LocalDateTime now(ZoneId zoneId) { + return LocalDateTime.now(zoneId); + } + + /** + * 按 yyyyMMdd 格式化 + * + * @param localDateTime 日期时间 + * @return Result + */ + public static String formatDate(LocalDateTime localDateTime) { + return format(localDateTime, PURE_DATE_FORMATTER); + } + + /** + * 按 yyyyMMdd 格式化 + * + * @param localDate 日期 + * @return Result + */ + public static String formatDate(LocalDate localDate) { + return format(localDate, PURE_DATE_FORMATTER); + } + + /** + * 按 HHmm 格式化 + * + * @param localDateTime 时间 + * @return Result + */ + public static String formatTime(LocalDateTime localDateTime) { + return format(localDateTime, TIME_FORMATTER); + } + + /** + * 按 HHmm 格式化 + * + * @param localTime 时间 + * @return Result + */ + public static String formatTime(LocalTime localTime) { + return format(localTime, TIME_FORMATTER); + } + + /** + * 按 yyyy-MM-dd HH:mm:ss 格式格式化 + * + * @param localDateTime 时间 + * @return Result + */ + public static String formatDateTime(LocalDateTime localDateTime) { + return format(localDateTime, NORM_DATETIME_FORMATTER); + } + + /** + * 根据日期格式化时间 + * + * @param localDateTime 时间 + * @param formatter 时间格式 + * @return Result + */ + public static String format(LocalDateTime localDateTime, DateTimeFormatter formatter) { + return localDateTime.format(formatter); + } + + /** + * 根据时间格式,格式化时间 + * + * @param localTime 时间 + * @param formatter 时间格式 + * @return Result + */ + public static String format(LocalTime localTime, DateTimeFormatter formatter) { + return localTime.format(formatter); + } + + /** + * 根据日期格式,格式化日期 + * + * @param localDate 日期 + * @param formatter 日期格式 + * @return Result + */ + public static String format(LocalDate localDate, DateTimeFormatter formatter) { + return localDate.format(formatter); + } + + /** + * 按照上海时区,解析香港格式的时间 + *

+ * 时间格式 yyyyMMddHHmmssSSS + * + * @param time 时间 + * @return LocalDateTime + */ + public static LocalDateTime parseCttDateTime(String time) { + return parse(time, PURE_DATETIME_MS_FORMATTER); + } + + /** + * 根据日期格式解析时间 + * + * @param formatter 时间格式 + * @param time 时间 + * @return LocalDateTime + */ + public static LocalDateTime parse(String time, DateTimeFormatter formatter) { + return LocalDateTime.parse(time, formatter); + } + + /** + * to instant by default zoneId(Shanghai) + * + * @param localDateTime 时间 + * @return Instant + */ + public static Instant toInstant(LocalDateTime localDateTime) { + return toInstant(localDateTime, CTT_ZONE_ID); + } + + /** + * To instant by zoneId + * + * @param localDateTime 时间 + * @return Instant + */ + public static Instant toInstant(LocalDateTime localDateTime, ZoneId zoneId) { + return localDateTime.atZone(zoneId).toInstant(); + } + + /** + * 将 localDateTime 转为秒 + * + * @param localDateTime 时间 + * @return 秒 + */ + public static long getEpochSecond(LocalDateTime localDateTime) { + return toInstant(localDateTime).getEpochSecond(); + } + + /** + * 将localDateTime 转为时间戳 + * + * @param localDateTime 时间 + * @return 时间戳 + */ + public static long toEpochMilli(LocalDateTime localDateTime) { + return toInstant(localDateTime).toEpochMilli(); + } + + /** + * 将秒和毫秒设为0 + * + * @param localDateTime 需要被设置的时间 + * @return 返回被设置的LocalDateTime + */ + public static LocalDateTime secondAndNanoSetZero(LocalDateTime localDateTime) { + return localDateTime.withSecond(0).withNano(0); + } + + /** + * 增加一天时间 + * + * @param localDateTime 日期时间 + * @return 新增一天后的 LocalDateTime + */ + public static LocalDateTime plusOneDayToDateTime(LocalDateTime localDateTime) { + return plusDays(localDateTime, 1); + } + + /** + * 增加一天时间 + * + * @param localDateTime 日期时间 + * @param localTime 时间 + * @return 新增一天后的 LocalDateTime + */ + public static LocalDateTime plusOneDay(LocalDateTime localDateTime, LocalTime localTime) { + return plusOneDay(localDateTime.toLocalDate(), localTime); + } + + /** + * 增加一天时间 + * + * @param localDate 日期 + * @param localTime 时间 + * @return 新增一天后的 LocalDateTime + */ + public static LocalDateTime plusOneDay(LocalDate localDate, LocalTime localTime) { + final LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime); + return plusDays(localDateTime, 1); + } + + + /** + * 增加一天时间 + * + * @param localDateTime 日期时间 + * @return 新增一天后的 LocalDate + */ + public static LocalDate plusOneDayToDate(LocalDateTime localDateTime) { + return plusDays(localDateTime, 1).toLocalDate(); + } + + /** + * 根据days新增天数 + * + * @param localDateTime 日期时间 + * @param days 天数 + * @return 新增 days 后的 LocalDateTime + */ + public static LocalDateTime plusDays(LocalDateTime localDateTime, long days) { + return localDateTime.plusDays(days); + } + + /** + * 新增一天 + * + * @param localDate 日期 + * @return 新增一天后的 LocalDate + */ + public static LocalDate plusOneDayToDate(LocalDate localDate) { + return plusDays(localDate, 1); + } + + /** + * 根据days新增天数 + * + * @param localDate 日期 + * @param days 新增的天数 + * @return 新增 days 后的 LocalDate + */ + public static LocalDate plusDays(LocalDate localDate, long days) { + return localDate.plusDays(days); + } + + /** + * 增加1分钟 + * + * @param localDateTime 日期时间 + * @return 返回新增的 LocalDateTime + */ + public static LocalDateTime plusOneMinute(LocalDateTime localDateTime, LocalTime localTime) { + return plusOneMinute(localDateTime.toLocalDate(), localTime); + } + + /** + * 增加1分钟 + * + * @param localDate 日期 + * @param localTime 时间 + * @return 返回新增的 LocalDateTime + */ + public static LocalDateTime plusOneMinute(LocalDate localDate, LocalTime localTime) { + final LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime); + return plusMinutes(localDateTime, 1); + } + + + /** + * 增加1分钟 + * + * @param localDateTime 日期时间 + * @return 返回新增的 LocalDateTime + */ + public static LocalDateTime plusOneMinute(LocalDateTime localDateTime) { + return plusMinutes(localDateTime, 1); + } + + /** + * 增加30分钟 + * + * @param localDateTime 日期时间 + * @return 返回新增的 LocalDateTime + */ + public static LocalDateTime plusThirtyMinute(LocalDateTime localDateTime) { + return plusMinutes(localDateTime, 30); + } + + /** + * 新增1分钟 + * + * @param localDateTime 日期时间 + * @return 返回新增的 LocalTime + */ + public static LocalTime plusOneMinuteToTime(LocalDateTime localDateTime) { + return plusMinutes(localDateTime, 1).toLocalTime(); + } + + /** + * 新增1分钟 + * + * @param localTime 日期时间 + * @return 返回新增的 LocalTime + */ + public static LocalTime plusOneMinute(LocalTime localTime) { + return plusMinutes(localTime, 1); + } + + /** + * 增加30分钟 + * + * @param localTime 日期时间 + * @return 返回新增的 LocalTime + */ + public static LocalTime plusThirtyMinute(LocalTime localTime) { + return plusMinutes(localTime, 30); + } + + /** + * 根据 minutes 新增分钟 + * + * @param localDateTime 日期时间 + * @param minutes 分钟数 + * @return 返回新增的 LocalDateTime + */ + public static LocalDateTime plusMinutes(LocalDateTime localDateTime, long minutes) { + return localDateTime.plusMinutes(minutes); + } + + /** + * 根据 minutes 新增分钟 + * + * @param localTime 时间 + * @param minutes 分钟数 + * @return 返回新增的 LocalTime + */ + public static LocalTime plusMinutes(LocalTime localTime, long minutes) { + return localTime.plusMinutes(minutes); + } + + /** + * 减少 1 分钟 + * + * @param localDateTime 日期时间 + * @param localTime 时间 + * @return 返回新增的 LocalTime + */ + public static LocalDateTime minusOneMinutes(LocalDateTime localDateTime, LocalTime localTime) { + return minusOneMinutes(localDateTime.toLocalDate(), localTime); + } + + + /** + * 减少 1 分钟 + * + * @param localDate 日期 + * @param localTime 时间 + * @return 返回新增的 LocalTime + */ + public static LocalDateTime minusOneMinutes(LocalDate localDate, LocalTime localTime) { + final LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime); + return minusMinutes(localDateTime, 1); + } + + /** + * 减少 1 分钟 + * + * @param localDateTime 日期时间 + * @return 返回新增的 LocalTime + */ + public static LocalDateTime minusOneMinutes(LocalDateTime localDateTime) { + return minusMinutes(localDateTime, 1); + } + + /** + * 根据 minutes 减少分钟 + * + * @param localDateTime 日期时间 + * @param minutes 分钟数 + * @return 返回新增的 LocalTime + */ + public static LocalDateTime minusMinutes(LocalDateTime localDateTime, long minutes) { + return localDateTime.minusMinutes(minutes); + } + + /** + * 根据 minutes 减少分钟 + * + * @param localTime 时间 + * @param minutes 分钟数 + * @return 返回新增的 LocalTime + */ + public static LocalTime minusMinutes(LocalTime localTime, long minutes) { + return localTime.minusMinutes(minutes); + } + + /** + * 判断是否是中午 + * + * @param startInclusive Start + * @return boolean + */ + public static boolean isNoon(LocalDateTime startInclusive) { + LocalDateTime noonDateTime = LocalDateTime.of(startInclusive.toLocalDate(), LocalTime.NOON); + return Duration.between(startInclusive, noonDateTime).isZero(); + } + + /** + * 判断是否是中午 + * + * @param startInclusive Start + * @return boolean + */ + public static boolean isNoon(LocalTime startInclusive) { + return Duration.between(startInclusive, LocalTime.NOON).isZero(); + } + + /** + * 是否是负数,startInclusive 大于 endInclusive 就是负数 + * + * @param startInclusive Start + * @param endInclusive end + * @return boolean + */ + public static boolean isNegative(Temporal startInclusive, Temporal endInclusive) { + return Duration.between(startInclusive, endInclusive).isNegative(); + } + + /** + * 相比是否是0,两个时间一致就是0 + * + * @param startInclusive Start + * @param endInclusive end + * @return boolean + */ + public static boolean isZero(Temporal startInclusive, Temporal endInclusive) { + return Duration.between(startInclusive, endInclusive).isZero(); + } + + /** + * endInclusive 大于或等于 startInclusive + * + * @param startInclusive Start + * @param endInclusive end + * @return boolean + */ + public static boolean isGreaterOrEqual(Temporal startInclusive, Temporal endInclusive) { + return Duration.between(startInclusive, endInclusive).toNanos() >= 0; + } + + /** + * endInclusive 大于 startInclusive + * + * @param startInclusive Start + * @param endInclusive end + * @return boolean + */ + public static boolean isGreater(Temporal startInclusive, Temporal endInclusive) { + return Duration.between(startInclusive, endInclusive).toNanos() > 0; + } + + /** + * endInclusive 小或等于 startInclusive + * + * @param startInclusive Start + * @param endInclusive end + * @return boolean + */ + public static boolean isLessThanOrEqual(Temporal startInclusive, Temporal endInclusive) { + return Duration.between(startInclusive, endInclusive).toNanos() <= 0; + } +} diff --git a/src/test/java/run/halo/app/utils/DateTimeUtilsTest.java b/src/test/java/run/halo/app/utils/DateTimeUtilsTest.java new file mode 100644 index 000000000..8f37e5b22 --- /dev/null +++ b/src/test/java/run/halo/app/utils/DateTimeUtilsTest.java @@ -0,0 +1,156 @@ +package run.halo.app.utils; + + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.test.context.junit4.SpringRunner; + +import java.time.*; +import java.time.format.DateTimeFormatter; + +/** + * DateTimeUtils 测试用例 + * + * @author LeiXinXin + * @date 2020/1/9 + */ +@RunWith(SpringRunner.class) +public class DateTimeUtilsTest { + /** + * 获取上海时区的当前时间 + */ + @Test + public void nowTest() { + final LocalDateTime now = DateTimeUtils.now().withNano(0); + Assert.assertNotNull(now); + final LocalDateTime ctt = DateTimeUtils.now(ZoneId.of(ZoneId.SHORT_IDS.get("CTT"))).withNano(0); + Assert.assertNotNull(ctt); + + Assert.assertEquals(Duration.between(now, ctt).toMinutes(), 0); + } + + /** + * 格式化日期时间 + */ + @Test + public void formatTest() { + final LocalDateTime now = LocalDateTime.of(2020, 1, 9, 20, 20, 20); + final String formatDate = DateTimeUtils.formatDate(now); + Assert.assertEquals(formatDate, "20200109"); + + final LocalDate localDate = LocalDate.of(2020, 1, 9); + final String localFormatDate = DateTimeUtils.formatDate(localDate); + Assert.assertEquals(localFormatDate, "20200109"); + + final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd"); + final String formatDateTime1 = DateTimeUtils.format(now, dateTimeFormatter); + Assert.assertEquals(formatDateTime1, "2020/01/09"); + + final LocalTime time1 = LocalTime.of(10, 20); + final String formatTime = DateTimeUtils.formatTime(time1); + Assert.assertEquals(formatTime, "1020"); + + final String formatTime1 = DateTimeUtils.format(time1, DateTimeFormatter.ofPattern("HH:mm")); + Assert.assertEquals(formatTime1, "10:20"); + } + + /** + * 增加时间 + */ + @Test + public void plusTest() { + final LocalDateTime now = LocalDateTime.of(2020, 1, 9, 10, 20); + final LocalDateTime localDateTime = DateTimeUtils.plusOneMinute(now); + Assert.assertEquals(localDateTime.toString(), "2020-01-09T10:21"); + + LocalTime localTime = LocalTime.of(7, 30); + final LocalDateTime localDateTime1 = DateTimeUtils.plusOneMinute(now, localTime); + Assert.assertEquals(localDateTime1.toString(), "2020-01-09T07:31"); + + final LocalDate date = LocalDate.of(2020, 1, 3); + final LocalDateTime localDateTime2 = DateTimeUtils.plusOneMinute(date, localTime); + Assert.assertEquals(localDateTime2.toString(), "2020-01-03T07:31"); + + final LocalTime localTime1 = DateTimeUtils.plusOneMinute(localTime); + Assert.assertEquals(localTime1.toString(), "07:31"); + + final LocalDateTime localDateTime3 = DateTimeUtils.plusDays(now, 10); + Assert.assertEquals(localDateTime3.toString(), "2020-01-19T10:20"); + } + + /** + * 解析时间格式为LocalDateTime + */ + @Test + public void parseTest() { + String time = "20200109135500000"; + final LocalDateTime localDateTime = DateTimeUtils.parseCttDateTime(time); + Assert.assertEquals(localDateTime.toString(), "2020-01-09T13:55"); + + String time2 = "2020/1/9 13:56"; + final LocalDateTime dateTime = DateTimeUtils.parse(time2, DateTimeFormatter.ofPattern("yyyy/M/d HH:mm")); + Assert.assertEquals(dateTime.toString(), "2020-01-09T13:56"); + } + + /** + * 减少日期时间 + */ + @Test + public void minusTest() { + final LocalDateTime now = LocalDateTime.of(2020, 1, 3, 14, 20); + final LocalDateTime localDateTime = DateTimeUtils.minusOneMinutes(now); + Assert.assertEquals(localDateTime.toString(), "2020-01-03T14:19"); + } + + /** + * 转为Instant + */ + @Test + public void toInstantTest() { + final LocalDateTime now = LocalDateTime.of(2020, 1, 3, 14, 20); + + final Instant instant = DateTimeUtils.toInstant(now); + Assert.assertEquals(instant.toString(), "2020-01-03T06:20:00Z"); + + final Instant jst = DateTimeUtils.toInstant(now, ZoneId.of(ZoneId.SHORT_IDS.get("JST"))); + Assert.assertEquals(jst.toString(), "2020-01-03T05:20:00Z"); + } + + /** + * 一些其他的使用方法 + */ + @Test + public void other() { + LocalDateTime startInclusive = LocalDateTime.of(2020, 1, 3, 10, 10, 30); + LocalDateTime endInclusive = LocalDateTime.of(2020, 1, 4, 10, 10, 30); + // End 大于等于 Start + final boolean greaterOrEqual = DateTimeUtils.isGreaterOrEqual(startInclusive, endInclusive); + Assert.assertTrue(greaterOrEqual); + + /* + * 小于等于 + */ + final boolean lessThanOrEqual = DateTimeUtils.isLessThanOrEqual(startInclusive, endInclusive); + Assert.assertFalse(lessThanOrEqual); + + /* + * 两个时间的比较是否为0 + */ + final boolean zero = DateTimeUtils.isZero(startInclusive, endInclusive); + Assert.assertFalse(zero); + + // 是否是负数,startInclusive 大于 endInclusive 就是负数 + final boolean negative = DateTimeUtils.isNegative(startInclusive, endInclusive); + Assert.assertFalse(negative); + + // 是否是中午 + final boolean noon = DateTimeUtils.isNoon(LocalTime.of(12, 0)); + Assert.assertTrue(noon); + + // 把纳秒和秒设置为0 + final LocalDateTime localDateTime = LocalDateTime.of(2020, 1, 5, 6, 40, 30, 999); + final LocalDateTime time = DateTimeUtils.secondAndNanoSetZero(localDateTime); + Assert.assertEquals(time.toString(), "2020-01-05T06:40"); + } +} \ No newline at end of file