diff --git a/src/main/java/com/ghostchu/btn/sparkle/module/IndexController.java b/src/main/java/com/ghostchu/btn/sparkle/module/IndexController.java index 7d12a6d..72bfea0 100644 --- a/src/main/java/com/ghostchu/btn/sparkle/module/IndexController.java +++ b/src/main/java/com/ghostchu/btn/sparkle/module/IndexController.java @@ -15,7 +15,7 @@ import org.springframework.web.bind.annotation.GetMapping; import java.io.Serializable; -import java.sql.Timestamp; +import java.time.OffsetDateTime; @Controller public class IndexController extends SparkleController { @@ -62,7 +62,7 @@ public String index(Model model) { return "index"; } - public IndexBtnMetrics btnMetrics(Timestamp from, Timestamp to) { + public IndexBtnMetrics btnMetrics(OffsetDateTime from, OffsetDateTime to) { var banHistory = banHistoryService.getMetrics(from, to); var snapshot = snapshotService.getMetrics(from, to); var clientDiscovery = clientDiscoveryService.getMetrics(from, to); diff --git a/src/main/java/com/ghostchu/btn/sparkle/module/analyse/AnalyseService.java b/src/main/java/com/ghostchu/btn/sparkle/module/analyse/AnalyseService.java index 7ca2caf..b48e89d 100644 --- a/src/main/java/com/ghostchu/btn/sparkle/module/analyse/AnalyseService.java +++ b/src/main/java/com/ghostchu/btn/sparkle/module/analyse/AnalyseService.java @@ -6,6 +6,7 @@ import com.ghostchu.btn.sparkle.util.IPMerger; import com.ghostchu.btn.sparkle.util.IPUtil; import com.ghostchu.btn.sparkle.util.MsgUtil; +import com.ghostchu.btn.sparkle.util.TimeUtil; import inet.ipaddr.IPAddress; import inet.ipaddr.IPAddressString; import jakarta.persistence.EntityManager; @@ -21,6 +22,8 @@ import java.net.InetAddress; import java.sql.Timestamp; +import java.time.OffsetDateTime; +import java.time.temporal.ChronoUnit; import java.util.*; import java.util.stream.Collectors; @@ -57,7 +60,7 @@ public class AnalyseService { @Scheduled(fixedDelayString = "${analyse.overdownload.interval}") public void cronUntrustedIPAddresses() { var list = ipMerger.merge(banHistoryRepository - .generateUntrustedIPAddresses(new Timestamp(System.currentTimeMillis() - untrustedIpAddressGenerateOffset), new Timestamp(System.currentTimeMillis()), untrustedIpAddressGenerateThreshold) + .generateUntrustedIPAddresses(TimeUtil.toUTC(System.currentTimeMillis() - untrustedIpAddressGenerateOffset), new Timestamp(System.currentTimeMillis()), untrustedIpAddressGenerateThreshold) .stream() .map(IPUtil::toString) .collect(Collectors.toList())) @@ -220,11 +223,11 @@ public Collection filterIP(Collection ips) { return list; } - private Timestamp nowTimestamp() { - return new Timestamp(System.currentTimeMillis()); + private OffsetDateTime nowTimestamp() { + return OffsetDateTime.now(); } - private Timestamp pastTimestamp(long offset) { - return new Timestamp(System.currentTimeMillis() - offset); + private OffsetDateTime pastTimestamp(long offset) { + return OffsetDateTime.now().minus(offset, ChronoUnit.MILLIS); } } diff --git a/src/main/java/com/ghostchu/btn/sparkle/module/audit/AuditService.java b/src/main/java/com/ghostchu/btn/sparkle/module/audit/AuditService.java index a9ed033..ca95507 100644 --- a/src/main/java/com/ghostchu/btn/sparkle/module/audit/AuditService.java +++ b/src/main/java/com/ghostchu/btn/sparkle/module/audit/AuditService.java @@ -8,7 +8,7 @@ import org.springframework.stereotype.Service; import org.springframework.util.LinkedMultiValueMap; -import java.sql.Timestamp; +import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -22,7 +22,7 @@ public AuditService(AuditRepository auditRepository) { } public Audit log(HttpServletRequest req, String action, boolean success, Map node) { - var audit = new Audit(null, new Timestamp(System.currentTimeMillis()), IPUtil.toInet(ServletUtil.getIP(req)), action, success, getHeaders(req), node); + var audit = new Audit(null, OffsetDateTime.now(), IPUtil.toInet(ServletUtil.getIP(req)), action, success, getHeaders(req), node); return auditRepository.save(audit); } diff --git a/src/main/java/com/ghostchu/btn/sparkle/module/audit/impl/Audit.java b/src/main/java/com/ghostchu/btn/sparkle/module/audit/impl/Audit.java index f519452..ea120fd 100644 --- a/src/main/java/com/ghostchu/btn/sparkle/module/audit/impl/Audit.java +++ b/src/main/java/com/ghostchu/btn/sparkle/module/audit/impl/Audit.java @@ -9,7 +9,7 @@ import org.hibernate.annotations.Type; import java.net.InetAddress; -import java.sql.Timestamp; +import java.time.OffsetDateTime; import java.util.List; import java.util.Map; @@ -27,7 +27,7 @@ public class Audit { @Column(nullable = false, unique = true) private Long id; @Column(nullable = false) - private Timestamp timestamp; + private OffsetDateTime timestamp; @Column private InetAddress ip; @Column(nullable = false) diff --git a/src/main/java/com/ghostchu/btn/sparkle/module/banhistory/BanHistoryController.java b/src/main/java/com/ghostchu/btn/sparkle/module/banhistory/BanHistoryController.java index d2969e4..58b6c45 100644 --- a/src/main/java/com/ghostchu/btn/sparkle/module/banhistory/BanHistoryController.java +++ b/src/main/java/com/ghostchu/btn/sparkle/module/banhistory/BanHistoryController.java @@ -5,6 +5,7 @@ import com.ghostchu.btn.sparkle.exception.RequestPageSizeTooLargeException; import com.ghostchu.btn.sparkle.module.banhistory.internal.BanHistory; import com.ghostchu.btn.sparkle.module.torrent.internal.Torrent; +import com.ghostchu.btn.sparkle.util.TimeUtil; import com.ghostchu.btn.sparkle.util.compare.NumberCompareMethod; import com.ghostchu.btn.sparkle.util.compare.StringCompareMethod; import com.ghostchu.btn.sparkle.util.paging.SparklePage; @@ -22,7 +23,6 @@ import org.springframework.data.jpa.domain.Specification; import org.springframework.web.bind.annotation.*; -import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; @@ -48,10 +48,10 @@ public BanHistoryController(BanHistoryService banHistoryService) { Specification specification = (root, query, cb) -> { List predicates = new ArrayList<>(); if(q.getTimeFrom() != null){ - predicates.add(cb.greaterThanOrEqualTo(root.get("insertTime"), new Timestamp(q.getTimeFrom()))); + predicates.add(cb.greaterThanOrEqualTo(root.get("insertTime"), TimeUtil.toUTC(q.getTimeFrom()))); } if(q.getTimeTo() != null){ - predicates.add(cb.lessThanOrEqualTo(root.get("insertTime"), new Timestamp(q.getTimeTo()))); + predicates.add(cb.lessThanOrEqualTo(root.get("insertTime"), TimeUtil.toUTC(q.getTimeTo()))); } if (StringUtils.isNotBlank(q.getPeerId())) { predicates.add(q.getPeerIdCompareMethod().criteriaBuilder(cb, root.get("peerId"), q.getPeerId())); diff --git a/src/main/java/com/ghostchu/btn/sparkle/module/banhistory/BanHistoryService.java b/src/main/java/com/ghostchu/btn/sparkle/module/banhistory/BanHistoryService.java index 3a9e078..2b1baf1 100644 --- a/src/main/java/com/ghostchu/btn/sparkle/module/banhistory/BanHistoryService.java +++ b/src/main/java/com/ghostchu/btn/sparkle/module/banhistory/BanHistoryService.java @@ -14,7 +14,7 @@ import org.springframework.stereotype.Service; import java.io.Serializable; -import java.sql.Timestamp; +import java.time.OffsetDateTime; import java.util.List; @Service @@ -47,7 +47,7 @@ public BanHistoryService(BanHistoryRepository banHistoryRepository, } @Cacheable(value = "banHistoryMetrics#1800000", key = "#from+'-'+#to") - public BanHistoryMetrics getMetrics(Timestamp from, Timestamp to) { + public BanHistoryMetrics getMetrics(OffsetDateTime from, OffsetDateTime to) { return new BanHistoryMetrics( banHistoryRepository.count(), banHistoryRepository.countByInsertTimeBetween(from, to) diff --git a/src/main/java/com/ghostchu/btn/sparkle/module/banhistory/internal/BanHistory.java b/src/main/java/com/ghostchu/btn/sparkle/module/banhistory/internal/BanHistory.java index 6078e17..b6a0780 100644 --- a/src/main/java/com/ghostchu/btn/sparkle/module/banhistory/internal/BanHistory.java +++ b/src/main/java/com/ghostchu/btn/sparkle/module/banhistory/internal/BanHistory.java @@ -9,7 +9,7 @@ import org.hibernate.annotations.Type; import java.net.InetAddress; -import java.sql.Timestamp; +import java.time.OffsetDateTime; @Entity @Table(name = "banhistory", indexes = { @@ -32,9 +32,9 @@ public class BanHistory { @Column(nullable = false, unique = true) private Long id; @Column(nullable = false) - private Timestamp insertTime; + private OffsetDateTime insertTime; @Column(nullable = false) - private Timestamp populateTime; + private OffsetDateTime populateTime; @JoinColumn(name = "userApplication") @ManyToOne(fetch = FetchType.LAZY) private UserApplication userApplication; diff --git a/src/main/java/com/ghostchu/btn/sparkle/module/banhistory/internal/BanHistoryRepository.java b/src/main/java/com/ghostchu/btn/sparkle/module/banhistory/internal/BanHistoryRepository.java index 06b86ec..5c5ae15 100644 --- a/src/main/java/com/ghostchu/btn/sparkle/module/banhistory/internal/BanHistoryRepository.java +++ b/src/main/java/com/ghostchu/btn/sparkle/module/banhistory/internal/BanHistoryRepository.java @@ -8,7 +8,7 @@ import org.springframework.stereotype.Repository; import java.net.InetAddress; -import java.sql.Timestamp; +import java.time.OffsetDateTime; import java.util.List; @Repository @@ -22,9 +22,9 @@ public interface BanHistoryRepository extends SparkleCommonRepository= ?3 """) @Transactional - List generateUntrustedIPAddresses(Timestamp from, Timestamp to, int threshold); + List generateUntrustedIPAddresses(OffsetDateTime from, OffsetDateTime to, int threshold); - Page findByInsertTimeBetweenOrderByInsertTimeDesc(Timestamp from, Timestamp to, Pageable pageable); + Page findByInsertTimeBetweenOrderByInsertTimeDesc(OffsetDateTime from, OffsetDateTime to, Pageable pageable); @Query(""" SELECT DISTINCT ban.peerIp FROM BanHistory ban @@ -35,19 +35,19 @@ HAVING COUNT (DISTINCT ban.userApplication.appId) >= ?3 GROUP BY ban.peerIp """) @Transactional - List findByInsertTimeBetweenOrderByInsertTimeDescIPVx(Timestamp from, Timestamp to, int family); + List findByInsertTimeBetweenOrderByInsertTimeDescIPVx(OffsetDateTime from, OffsetDateTime to, int family); - List findByInsertTimeBetweenOrderByInsertTimeDesc(Timestamp from, Timestamp to); + List findByInsertTimeBetweenOrderByInsertTimeDesc(OffsetDateTime from, OffsetDateTime to); - Page findByInsertTimeBetweenAndPeerIdLikeIgnoreCaseOrderByInsertTimeDesc(Timestamp from, Timestamp to, String peerId, Pageable pageable); + Page findByInsertTimeBetweenAndPeerIdLikeIgnoreCaseOrderByInsertTimeDesc(OffsetDateTime from, OffsetDateTime to, String peerId, Pageable pageable); - Page findByInsertTimeBetweenAndPeerClientNameLikeIgnoreCaseOrderByInsertTimeDesc(Timestamp from, Timestamp to, String peerClientName, Pageable pageable); + Page findByInsertTimeBetweenAndPeerClientNameLikeIgnoreCaseOrderByInsertTimeDesc(OffsetDateTime from, OffsetDateTime to, String peerClientName, Pageable pageable); - Page findByInsertTimeBetweenAndPeerIpEqualsOrderByInsertTimeDesc(Timestamp from, Timestamp to, InetAddress peerIp, Pageable pageable); + Page findByInsertTimeBetweenAndPeerIpEqualsOrderByInsertTimeDesc(OffsetDateTime from, OffsetDateTime to, InetAddress peerIp, Pageable pageable); Page findByOrderByInsertTimeDesc(Pageable pageable); - long countByInsertTimeBetween(Timestamp insertTimeStart, Timestamp insertTimeEnd); + long countByInsertTimeBetween(OffsetDateTime insertTimeStart, OffsetDateTime insertTimeEnd); @Query(""" SELECT DISTINCT ban FROM BanHistory ban @@ -56,15 +56,15 @@ HAVING COUNT (DISTINCT ban.userApplication.appId) >= ?3 AND ban.insertTime >= ?3 AND ban.insertTime <= ?4 """) @Transactional - List findDistinctByPeerIdLikeOrPeerClientNameLike(String peerId, String peerClientName, Timestamp from, Timestamp to); + List findDistinctByPeerIdLikeOrPeerClientNameLike(String peerId, String peerClientName, OffsetDateTime from, OffsetDateTime to); - List findDistinctByPeerIdLikeAndInsertTimeBetween(String peerId, Timestamp from, Timestamp to); + List findDistinctByPeerIdLikeAndInsertTimeBetween(String peerId, OffsetDateTime from, OffsetDateTime to); @Query(nativeQuery = true, value = "SELECT * from banhistory ban WHERE ban.insert_time >= ?2 AND ban.insert_time <= ?3 AND host(ban.peer_ip) LIKE ?1") @Transactional - List findByPeerIp(String peerIp,Timestamp insertTimeStart, Timestamp insertTimeEnd); + List findByPeerIp(String peerIp, OffsetDateTime insertTimeStart, OffsetDateTime insertTimeEnd); - List findDistinctByPeerClientNameLikeAndInsertTimeBetween(String peerClientName, Timestamp from, Timestamp to); + List findDistinctByPeerClientNameLikeAndInsertTimeBetween(String peerClientName, OffsetDateTime from, OffsetDateTime to); - List findDistinctByPeerClientNameAndModuleLikeAndInsertTimeBetween(String peerClientName, String module, Timestamp from, Timestamp to); + List findDistinctByPeerClientNameAndModuleLikeAndInsertTimeBetween(String peerClientName, String module, OffsetDateTime from, OffsetDateTime to); } diff --git a/src/main/java/com/ghostchu/btn/sparkle/module/clientdiscovery/ClientDiscoveryService.java b/src/main/java/com/ghostchu/btn/sparkle/module/clientdiscovery/ClientDiscoveryService.java index c14be98..11b3e45 100644 --- a/src/main/java/com/ghostchu/btn/sparkle/module/clientdiscovery/ClientDiscoveryService.java +++ b/src/main/java/com/ghostchu/btn/sparkle/module/clientdiscovery/ClientDiscoveryService.java @@ -16,7 +16,8 @@ import org.springframework.stereotype.Service; import java.io.Serializable; -import java.sql.Timestamp; +import java.time.OffsetDateTime; +import java.time.temporal.ChronoField; import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -34,7 +35,7 @@ public ClientDiscoveryService(ClientDiscoveryRepository clientDiscoveryRepositor @Transactional @Lock(LockModeType.PESSIMISTIC_WRITE) @Async - public void handleIdentities(User user, Timestamp timeForFoundAt, Timestamp timeForLastSeenAt, Set clientIdentities) { + public void handleIdentities(User user, OffsetDateTime timeForFoundAt, OffsetDateTime timeForLastSeenAt, Set clientIdentities) { clientDiscoveryRepository.updateLastSeen(clientIdentities.stream().map(ClientIdentity::hash).toList(), timeForLastSeenAt, user); var found = clientDiscoveryRepository.findAllById(clientIdentities.stream().map(ClientIdentity::hash).toList()); List hashInDatabase = new ArrayList<>(); @@ -50,7 +51,7 @@ public void handleIdentities(User user, Timestamp timeForFoundAt, Timestamp time } @Cacheable(value = "clientDiscoveryMetrics#1800000", key = "#from+'-'+#to") - public ClientDiscoveryMetrics getMetrics(Timestamp from, Timestamp to){ + public ClientDiscoveryMetrics getMetrics(OffsetDateTime from, OffsetDateTime to) { return new ClientDiscoveryMetrics( clientDiscoveryRepository.count(), clientDiscoveryRepository.countByFoundAtBetween(from,to) @@ -62,9 +63,9 @@ public ClientDiscoveryDto toDto(ClientDiscovery clientDiscovery) { .hash(clientDiscovery.getHash()) .clientName(clientDiscovery.getClientName()) .peerId(clientDiscovery.getPeerId()) - .foundAt(clientDiscovery.getFoundAt().getTime()) + .foundAt(clientDiscovery.getFoundAt().getLong(ChronoField.MILLI_OF_DAY)) .foundBy(userService.toDto(clientDiscovery.getFoundBy())) - .lastSeenAt(clientDiscovery.getLastSeenAt().getTime()) + .lastSeenAt(clientDiscovery.getLastSeenAt().getLong(ChronoField.MILLI_OF_DAY)) .lastSeenBy(userService.toDto(clientDiscovery.getLastSeenBy())) .build(); } diff --git a/src/main/java/com/ghostchu/btn/sparkle/module/clientdiscovery/internal/ClientDiscovery.java b/src/main/java/com/ghostchu/btn/sparkle/module/clientdiscovery/internal/ClientDiscovery.java index 2c652c2..32b336a 100644 --- a/src/main/java/com/ghostchu/btn/sparkle/module/clientdiscovery/internal/ClientDiscovery.java +++ b/src/main/java/com/ghostchu/btn/sparkle/module/clientdiscovery/internal/ClientDiscovery.java @@ -5,7 +5,7 @@ import lombok.*; import org.hibernate.annotations.DynamicUpdate; -import java.sql.Timestamp; +import java.time.OffsetDateTime; @Entity @Table(name = "clientdiscovery") @@ -24,12 +24,12 @@ public class ClientDiscovery { @Column(nullable = false) private String peerId; @Column(nullable = false) - private Timestamp foundAt; + private OffsetDateTime foundAt; @JoinColumn(name = "foundBy") @ManyToOne(fetch = FetchType.LAZY) private User foundBy; @Column(nullable = false) - private Timestamp lastSeenAt; + private OffsetDateTime lastSeenAt; @JoinColumn(name = "lastSeenBy") @ManyToOne(fetch = FetchType.LAZY) private User lastSeenBy; diff --git a/src/main/java/com/ghostchu/btn/sparkle/module/clientdiscovery/internal/ClientDiscoveryRepository.java b/src/main/java/com/ghostchu/btn/sparkle/module/clientdiscovery/internal/ClientDiscoveryRepository.java index e4efe50..3c64f50 100644 --- a/src/main/java/com/ghostchu/btn/sparkle/module/clientdiscovery/internal/ClientDiscoveryRepository.java +++ b/src/main/java/com/ghostchu/btn/sparkle/module/clientdiscovery/internal/ClientDiscoveryRepository.java @@ -9,7 +9,7 @@ import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; -import java.sql.Timestamp; +import java.time.OffsetDateTime; import java.util.Collection; @Repository @@ -17,9 +17,9 @@ public interface ClientDiscoveryRepository extends SparkleCommonRepository ids, Timestamp lastSeenAt, User lastSeenBy); + int updateLastSeen(Collection ids, OffsetDateTime lastSeenAt, User lastSeenBy); Page findByOrderByFoundAtDesc(Pageable pageable); - long countByFoundAtBetween(Timestamp foundAtStart, Timestamp foundAtEnd); + long countByFoundAtBetween(OffsetDateTime foundAtStart, OffsetDateTime foundAtEnd); } diff --git a/src/main/java/com/ghostchu/btn/sparkle/module/githubupdate/GithubUpdateService.java b/src/main/java/com/ghostchu/btn/sparkle/module/githubupdate/GithubUpdateService.java index c564a27..93ff1d9 100644 --- a/src/main/java/com/ghostchu/btn/sparkle/module/githubupdate/GithubUpdateService.java +++ b/src/main/java/com/ghostchu/btn/sparkle/module/githubupdate/GithubUpdateService.java @@ -17,7 +17,8 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.sql.Timestamp; +import java.time.OffsetDateTime; +import java.time.temporal.ChronoUnit; import java.util.Arrays; import java.util.Locale; import java.util.StringJoiner; @@ -226,11 +227,11 @@ private String generateOverDownloadIps() { return String.join("\n", analyseService.getOverDownloadIPAddresses().stream().map(AnalysedRule::getIp).toList()); } - private Timestamp nowTimestamp() { - return new Timestamp(System.currentTimeMillis()); + private OffsetDateTime nowTimestamp() { + return OffsetDateTime.now(); } - private Timestamp pastTimestamp() { - return new Timestamp(System.currentTimeMillis() - pastInterval); + private OffsetDateTime pastTimestamp() { + return OffsetDateTime.now().minus(pastInterval, ChronoUnit.MILLIS); } } diff --git a/src/main/java/com/ghostchu/btn/sparkle/module/ping/PingService.java b/src/main/java/com/ghostchu/btn/sparkle/module/ping/PingService.java index f7aee93..ac0c822 100644 --- a/src/main/java/com/ghostchu/btn/sparkle/module/ping/PingService.java +++ b/src/main/java/com/ghostchu/btn/sparkle/module/ping/PingService.java @@ -16,10 +16,7 @@ import com.ghostchu.btn.sparkle.module.torrent.TorrentService; import com.ghostchu.btn.sparkle.module.user.UserService; import com.ghostchu.btn.sparkle.module.userapp.internal.UserApplication; -import com.ghostchu.btn.sparkle.util.ByteUtil; -import com.ghostchu.btn.sparkle.util.IPMerger; -import com.ghostchu.btn.sparkle.util.IPUtil; -import com.ghostchu.btn.sparkle.util.PeerUtil; +import com.ghostchu.btn.sparkle.util.*; import com.ghostchu.btn.sparkle.util.ipdb.GeoIPManager; import jakarta.transaction.Transactional; import lombok.Data; @@ -31,7 +28,7 @@ import org.springframework.stereotype.Service; import java.net.InetAddress; -import java.sql.Timestamp; +import java.time.OffsetDateTime; import java.util.*; import java.util.stream.Collectors; @@ -57,7 +54,7 @@ public class PingService { @Modifying @Transactional public long handlePeers(InetAddress submitterIp, UserApplication userApplication, BtnPeerPing ping) { - Timestamp now = new Timestamp(System.currentTimeMillis()); + OffsetDateTime now = OffsetDateTime.now(); var usr = userApplication.getUser(); usr.setLastAccessAt(now); userService.saveUser(usr); @@ -68,7 +65,7 @@ public long handlePeers(InetAddress submitterIp, UserApplication userApplication try { return Snapshot.builder() .insertTime(now) - .populateTime(new Timestamp(ping.getPopulateTime())) + .populateTime(TimeUtil.toUTC(ping.getPopulateTime())) .userApplication(userApplication) .submitId(UUID.randomUUID().toString()) .peerIp(IPUtil.toInet(peer.getIpAddress())) @@ -101,7 +98,7 @@ public long handlePeers(InetAddress submitterIp, UserApplication userApplication @Modifying @Transactional public long handleBans(InetAddress submitterIp, UserApplication userApplication, BtnBanPing ping) { - Timestamp now = new Timestamp(System.currentTimeMillis()); + OffsetDateTime now = OffsetDateTime.now(); var usr = userApplication.getUser(); usr.setLastAccessAt(now); userService.saveUser(usr); @@ -113,7 +110,7 @@ public long handleBans(InetAddress submitterIp, UserApplication userApplication, try { return BanHistory.builder() .insertTime(now) - .populateTime(new Timestamp(ping.getPopulateTime())) + .populateTime(TimeUtil.toUTC(ping.getPopulateTime())) .userApplication(userApplication) .submitId(UUID.randomUUID().toString()) .peerIp(IPUtil.toInet(peer.getIpAddress())) diff --git a/src/main/java/com/ghostchu/btn/sparkle/module/rule/RuleService.java b/src/main/java/com/ghostchu/btn/sparkle/module/rule/RuleService.java index 6c6421b..e5ea14d 100644 --- a/src/main/java/com/ghostchu/btn/sparkle/module/rule/RuleService.java +++ b/src/main/java/com/ghostchu/btn/sparkle/module/rule/RuleService.java @@ -2,10 +2,12 @@ import com.ghostchu.btn.sparkle.module.rule.internal.Rule; import com.ghostchu.btn.sparkle.module.rule.internal.RuleRepository; +import com.ghostchu.btn.sparkle.util.TimeUtil; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; -import java.sql.Timestamp; +import java.time.OffsetDateTime; +import java.time.temporal.ChronoField; import java.util.ArrayList; import java.util.List; @@ -24,8 +26,7 @@ public RuleService(RuleRepository ruleRepository) { */ @Cacheable({"unexpiredRules#600000"}) public List getUnexpiredRules() { - Timestamp timestamp = new Timestamp(System.currentTimeMillis()); - return ruleRepository.findByExpiredAtGreaterThan(timestamp).stream().map(this::toDto).toList(); + return ruleRepository.findByExpiredAtGreaterThan(OffsetDateTime.now()).stream().map(this::toDto).toList(); } /** @@ -61,8 +62,8 @@ public RuleDto saveRule(RuleDto ruleDto) { rule.setCategory(ruleDto.getCategory()); rule.setType(ruleDto.getType()); rule.setContent(ruleDto.getContent()); - rule.setCreatedAt(new Timestamp(ruleDto.getCreatedAt())); - rule.setExpiredAt(new Timestamp(ruleDto.getExpiredAt())); + rule.setCreatedAt(TimeUtil.toUTC(ruleDto.getCreatedAt())); + rule.setExpiredAt(TimeUtil.toUTC(ruleDto.getExpiredAt())); return toDto(ruleRepository.save(rule)); } @@ -72,8 +73,8 @@ public RuleDto toDto(Rule rule) { .category(rule.getCategory()) .content(rule.getContent()) .type(rule.getType()) - .createdAt(rule.getCreatedAt().getTime()) - .expiredAt(rule.getExpiredAt().getTime()) + .createdAt(rule.getCreatedAt().getLong(ChronoField.MILLI_OF_DAY)) + .expiredAt(rule.getExpiredAt().getLong(ChronoField.MILLI_OF_DAY)) .build(); } } diff --git a/src/main/java/com/ghostchu/btn/sparkle/module/rule/internal/Rule.java b/src/main/java/com/ghostchu/btn/sparkle/module/rule/internal/Rule.java index 9c238c2..80c12c2 100644 --- a/src/main/java/com/ghostchu/btn/sparkle/module/rule/internal/Rule.java +++ b/src/main/java/com/ghostchu/btn/sparkle/module/rule/internal/Rule.java @@ -6,7 +6,7 @@ import lombok.NoArgsConstructor; import lombok.Setter; -import java.sql.Timestamp; +import java.time.OffsetDateTime; @Entity @Table(name = "rule", @@ -27,7 +27,7 @@ public class Rule { @Column(nullable = false) private String type; @Column(nullable = false) - private Timestamp createdAt; + private OffsetDateTime createdAt; @Column(nullable = false) - private Timestamp expiredAt; + private OffsetDateTime expiredAt; } diff --git a/src/main/java/com/ghostchu/btn/sparkle/module/rule/internal/RuleRepository.java b/src/main/java/com/ghostchu/btn/sparkle/module/rule/internal/RuleRepository.java index ea531a7..13e5c2a 100644 --- a/src/main/java/com/ghostchu/btn/sparkle/module/rule/internal/RuleRepository.java +++ b/src/main/java/com/ghostchu/btn/sparkle/module/rule/internal/RuleRepository.java @@ -3,7 +3,7 @@ import com.ghostchu.btn.sparkle.module.repository.SparkleCommonRepository; import org.springframework.stereotype.Repository; -import java.sql.Timestamp; +import java.time.OffsetDateTime; import java.util.List; @Repository @@ -12,7 +12,7 @@ public interface RuleRepository extends SparkleCommonRepository { List findByType(String type); - List findByExpiredAtGreaterThan(Timestamp time); + List findByExpiredAtGreaterThan(OffsetDateTime time); } diff --git a/src/main/java/com/ghostchu/btn/sparkle/module/snapshot/SnapshotController.java b/src/main/java/com/ghostchu/btn/sparkle/module/snapshot/SnapshotController.java index 7742982..965eb10 100644 --- a/src/main/java/com/ghostchu/btn/sparkle/module/snapshot/SnapshotController.java +++ b/src/main/java/com/ghostchu/btn/sparkle/module/snapshot/SnapshotController.java @@ -5,6 +5,7 @@ import com.ghostchu.btn.sparkle.exception.RequestPageSizeTooLargeException; import com.ghostchu.btn.sparkle.module.snapshot.internal.Snapshot; import com.ghostchu.btn.sparkle.module.torrent.internal.Torrent; +import com.ghostchu.btn.sparkle.util.TimeUtil; import com.ghostchu.btn.sparkle.util.compare.NumberCompareMethod; import com.ghostchu.btn.sparkle.util.compare.StringCompareMethod; import com.ghostchu.btn.sparkle.util.paging.SparklePage; @@ -20,7 +21,6 @@ import org.springframework.data.jpa.domain.Specification; import org.springframework.web.bind.annotation.*; -import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; @@ -45,10 +45,10 @@ public StdResp> query(@RequestBody ComplexSnapshotQueryRequest Specification specification = (root, query, cb) -> { List predicates = new ArrayList<>(); if(q.getTimeFrom() != null){ - predicates.add(cb.greaterThanOrEqualTo(root.get("insertTime"), new Timestamp(q.getTimeFrom()))); + predicates.add(cb.greaterThanOrEqualTo(root.get("insertTime"), TimeUtil.toUTC(q.getTimeFrom()))); } if(q.getTimeTo() != null){ - predicates.add(cb.lessThanOrEqualTo(root.get("insertTime"), new Timestamp(q.getTimeTo()))); + predicates.add(cb.lessThanOrEqualTo(root.get("insertTime"), TimeUtil.toUTC(q.getTimeTo()))); } if (StringUtils.isNotBlank(q.getPeerId())) { predicates.add(q.getPeerIdCompareMethod().criteriaBuilder(cb, root.get("peerId"), q.getPeerId())); diff --git a/src/main/java/com/ghostchu/btn/sparkle/module/snapshot/SnapshotService.java b/src/main/java/com/ghostchu/btn/sparkle/module/snapshot/SnapshotService.java index 491ecdb..070c2c8 100644 --- a/src/main/java/com/ghostchu/btn/sparkle/module/snapshot/SnapshotService.java +++ b/src/main/java/com/ghostchu/btn/sparkle/module/snapshot/SnapshotService.java @@ -20,7 +20,7 @@ import java.io.Serializable; import java.net.InetAddress; -import java.sql.Timestamp; +import java.time.OffsetDateTime; import java.util.List; @Service @@ -65,7 +65,7 @@ public void saveSnapshots(List snapshotList) { @Cacheable(value = "snapshotMetrics#1800000", key = "#from+'-'+#to") - public SnapshotMetrics getMetrics(Timestamp from, Timestamp to) { + public SnapshotMetrics getMetrics(OffsetDateTime from, OffsetDateTime to) { return new SnapshotMetrics(snapshotRepository.count(), snapshotRepository.countByInsertTimeBetween(from, to)); } diff --git a/src/main/java/com/ghostchu/btn/sparkle/module/snapshot/internal/Snapshot.java b/src/main/java/com/ghostchu/btn/sparkle/module/snapshot/internal/Snapshot.java index 431967e..4e13553 100644 --- a/src/main/java/com/ghostchu/btn/sparkle/module/snapshot/internal/Snapshot.java +++ b/src/main/java/com/ghostchu/btn/sparkle/module/snapshot/internal/Snapshot.java @@ -6,7 +6,7 @@ import lombok.*; import java.net.InetAddress; -import java.sql.Timestamp; +import java.time.OffsetDateTime; @Entity @Table(name = "snapshot", @@ -28,9 +28,9 @@ public class Snapshot { @Column(nullable = false, unique = true) private Long id; @Column(nullable = false) - private Timestamp insertTime; + private OffsetDateTime insertTime; @Column(nullable = false) - private Timestamp populateTime; + private OffsetDateTime populateTime; @JoinColumn(name = "userApplication") @ManyToOne(fetch = FetchType.LAZY) private UserApplication userApplication; diff --git a/src/main/java/com/ghostchu/btn/sparkle/module/snapshot/internal/SnapshotRepository.java b/src/main/java/com/ghostchu/btn/sparkle/module/snapshot/internal/SnapshotRepository.java index 2d30bbe..0dae7c3 100644 --- a/src/main/java/com/ghostchu/btn/sparkle/module/snapshot/internal/SnapshotRepository.java +++ b/src/main/java/com/ghostchu/btn/sparkle/module/snapshot/internal/SnapshotRepository.java @@ -5,10 +5,11 @@ import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Repository; -import java.sql.Timestamp; +import java.time.OffsetDateTime; @Repository public interface SnapshotRepository extends SparkleCommonRepository { Page findByOrderByInsertTimeDesc(Pageable pageable); - long countByInsertTimeBetween(Timestamp insertTimeStart, Timestamp insertTimeEnd); + + long countByInsertTimeBetween(OffsetDateTime insertTimeStart, OffsetDateTime insertTimeEnd); } diff --git a/src/main/java/com/ghostchu/btn/sparkle/module/tracker/TrackerService.java b/src/main/java/com/ghostchu/btn/sparkle/module/tracker/TrackerService.java index 54d098a..d2b8e94 100644 --- a/src/main/java/com/ghostchu/btn/sparkle/module/tracker/TrackerService.java +++ b/src/main/java/com/ghostchu/btn/sparkle/module/tracker/TrackerService.java @@ -2,6 +2,7 @@ import com.ghostchu.btn.sparkle.module.tracker.internal.*; import com.ghostchu.btn.sparkle.util.ByteUtil; +import com.ghostchu.btn.sparkle.util.TimeUtil; import jakarta.transaction.Transactional; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -14,7 +15,6 @@ import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; -import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; @@ -42,7 +42,7 @@ public TrackerService(TrackedPeerRepository trackedPeerRepository, @Scheduled(fixedDelayString = "${service.tracker.cleanup-interval}") @Transactional public void cleanup() { - var count = trackedPeerRepository.deleteByLastTimeSeenLessThanEqual(new Timestamp(System.currentTimeMillis() - inactiveInterval)); + var count = trackedPeerRepository.deleteByLastTimeSeenLessThanEqual(TimeUtil.toUTC(System.currentTimeMillis() - inactiveInterval)); log.info("已清除 {} 个不活跃的 Peers", count); } @@ -66,8 +66,8 @@ public void executeAnnounce(PeerAnnounce announce) { announce.left(), announce.peerEvent(), announce.userAgent(), - new Timestamp(System.currentTimeMillis()), - new Timestamp(System.currentTimeMillis()) + TimeUtil.toUTC(System.currentTimeMillis()), + TimeUtil.toUTC(System.currentTimeMillis()) )); if (trackedPeer.getDownloadedOffset() > announce.downloaded() || trackedPeer.getUploadedOffset() > announce.uploaded()) { @@ -82,7 +82,7 @@ public void executeAnnounce(PeerAnnounce announce) { trackedPeer.setDownloadedOffset(announce.downloaded()); trackedPeer.setUploadedOffset(announce.uploaded()); trackedPeer.setUserAgent(announce.userAgent()); - trackedPeer.setLastTimeSeen(new Timestamp(System.currentTimeMillis())); + trackedPeer.setLastTimeSeen(TimeUtil.toUTC(System.currentTimeMillis())); trackedPeer.setLeft(announce.left()); trackedPeer.setPeerPort(announce.peerPort()); trackedPeer.setPeerIp(announce.peerIp()); @@ -90,8 +90,8 @@ public void executeAnnounce(PeerAnnounce announce) { var trackedTask = trackedTaskRepository.findByTorrentInfoHash(ByteUtil.bytesToHex(announce.infoHash())).orElse(new TrackedTask( null, ByteUtil.bytesToHex(announce.infoHash()), - new Timestamp(System.currentTimeMillis()), - new Timestamp(System.currentTimeMillis()), + TimeUtil.toUTC(System.currentTimeMillis()), + TimeUtil.toUTC(System.currentTimeMillis()), 0L, 0L )); // 检查 task 属性 diff --git a/src/main/java/com/ghostchu/btn/sparkle/module/tracker/internal/TrackedPeer.java b/src/main/java/com/ghostchu/btn/sparkle/module/tracker/internal/TrackedPeer.java index 77cbdad..0ecc87c 100644 --- a/src/main/java/com/ghostchu/btn/sparkle/module/tracker/internal/TrackedPeer.java +++ b/src/main/java/com/ghostchu/btn/sparkle/module/tracker/internal/TrackedPeer.java @@ -8,7 +8,7 @@ import org.hibernate.annotations.DynamicUpdate; import java.net.InetAddress; -import java.sql.Timestamp; +import java.time.OffsetDateTime; @Entity @Table(name = "tracker_peers", @@ -53,7 +53,7 @@ public class TrackedPeer { @Column(nullable = false) private String userAgent; @Column(nullable = false) - private Timestamp firstTimeSeen; + private OffsetDateTime firstTimeSeen; @Column(nullable = false) - private Timestamp lastTimeSeen; + private OffsetDateTime lastTimeSeen; } diff --git a/src/main/java/com/ghostchu/btn/sparkle/module/tracker/internal/TrackedPeerRepository.java b/src/main/java/com/ghostchu/btn/sparkle/module/tracker/internal/TrackedPeerRepository.java index 0f9204d..0e85248 100644 --- a/src/main/java/com/ghostchu/btn/sparkle/module/tracker/internal/TrackedPeerRepository.java +++ b/src/main/java/com/ghostchu/btn/sparkle/module/tracker/internal/TrackedPeerRepository.java @@ -5,7 +5,7 @@ import org.springframework.stereotype.Repository; import java.net.InetAddress; -import java.sql.Timestamp; +import java.time.OffsetDateTime; import java.util.List; import java.util.Optional; @Repository @@ -28,7 +28,7 @@ order by RANDOM() limit ?2 Optional findByPeerIpAndPeerIdAndTorrentInfoHash(InetAddress peerIp, String peerId, String torrentInfoHash); - long deleteByLastTimeSeenLessThanEqual(Timestamp deleteAllEntireBeforeThisTime); + long deleteByLastTimeSeenLessThanEqual(OffsetDateTime deleteAllEntireBeforeThisTime); long countByTorrentInfoHashAndLeft(String torrentInfoHash, Long left); diff --git a/src/main/java/com/ghostchu/btn/sparkle/module/tracker/internal/TrackedTask.java b/src/main/java/com/ghostchu/btn/sparkle/module/tracker/internal/TrackedTask.java index 7925bda..a3bd138 100644 --- a/src/main/java/com/ghostchu/btn/sparkle/module/tracker/internal/TrackedTask.java +++ b/src/main/java/com/ghostchu/btn/sparkle/module/tracker/internal/TrackedTask.java @@ -7,7 +7,7 @@ import lombok.Setter; import org.hibernate.annotations.DynamicUpdate; -import java.sql.Timestamp; +import java.time.OffsetDateTime; @Entity @Table(name = "tracker_tasks", @@ -26,9 +26,9 @@ public class TrackedTask { @Column(nullable = false) private String torrentInfoHash; @Column(nullable = false) - private Timestamp firstTimeSeen; + private OffsetDateTime firstTimeSeen; @Column(nullable = false) - private Timestamp lastTimeSeen; + private OffsetDateTime lastTimeSeen; @Column(nullable = false) private Long leechCount; @Column(nullable = false) diff --git a/src/main/java/com/ghostchu/btn/sparkle/module/user/UserService.java b/src/main/java/com/ghostchu/btn/sparkle/module/user/UserService.java index 604ebf6..d04a866 100644 --- a/src/main/java/com/ghostchu/btn/sparkle/module/user/UserService.java +++ b/src/main/java/com/ghostchu/btn/sparkle/module/user/UserService.java @@ -9,6 +9,7 @@ import org.springframework.data.jpa.repository.Modifying; import org.springframework.stereotype.Service; +import java.time.temporal.ChronoField; import java.util.Optional; @Service @@ -64,8 +65,8 @@ public UserDto toDto(User user) { .id(user.getId()) .avatar(user.getAvatar()) .nickname(user.getNickname()) - .registerAt(user.getRegisterAt().getTime()) - .lastSeenAt(user.getLastSeenAt().getTime()) + .registerAt(user.getRegisterAt().getLong(ChronoField.MILLI_OF_DAY)) + .lastSeenAt(user.getLastSeenAt().getLong(ChronoField.MILLI_OF_DAY)) .banned(user.getBanned()) .build(); } diff --git a/src/main/java/com/ghostchu/btn/sparkle/module/user/internal/User.java b/src/main/java/com/ghostchu/btn/sparkle/module/user/internal/User.java index e5313cf..0aca0d6 100644 --- a/src/main/java/com/ghostchu/btn/sparkle/module/user/internal/User.java +++ b/src/main/java/com/ghostchu/btn/sparkle/module/user/internal/User.java @@ -7,7 +7,7 @@ import lombok.Setter; import org.hibernate.annotations.DynamicUpdate; -import java.sql.Timestamp; +import java.time.OffsetDateTime; @Entity @Table(name = "user", @@ -30,11 +30,11 @@ public class User { @Column(nullable = false) private String nickname; @Column(nullable = false) - private Timestamp registerAt; + private OffsetDateTime registerAt; @Column(nullable = false) - private Timestamp lastSeenAt; + private OffsetDateTime lastSeenAt; @Column(nullable = false) - private Timestamp lastAccessAt; + private OffsetDateTime lastAccessAt; @Column(nullable = false) private String githubLogin; @Column(nullable = true) diff --git a/src/main/java/com/ghostchu/btn/sparkle/module/userapp/UserApplicationController.java b/src/main/java/com/ghostchu/btn/sparkle/module/userapp/UserApplicationController.java index 18e794a..eb71da0 100644 --- a/src/main/java/com/ghostchu/btn/sparkle/module/userapp/UserApplicationController.java +++ b/src/main/java/com/ghostchu/btn/sparkle/module/userapp/UserApplicationController.java @@ -9,6 +9,7 @@ import com.ghostchu.btn.sparkle.exception.UserNotFoundException; import com.ghostchu.btn.sparkle.module.audit.AuditService; import com.ghostchu.btn.sparkle.module.user.UserService; +import com.ghostchu.btn.sparkle.util.TimeUtil; import com.ghostchu.btn.sparkle.wrapper.StdResp; import jakarta.servlet.http.HttpServletRequest; import lombok.AllArgsConstructor; @@ -107,7 +108,7 @@ public StdResp deleteUserApplication(@PathVariable("appId") String appId) @PutMapping("/userapp") public StdResp createUserApplication(@RequestBody UserApplicationCreateRequest req) throws UserNotFoundException, TooManyUserApplicationException { var user = userService.getUser(StpUtil.getLoginIdAsLong()).orElseThrow(); - var usrApp = userApplicationService.generateUserApplicationForUser(user, req.getComment(), new Timestamp(System.currentTimeMillis())); + var usrApp = userApplicationService.generateUserApplicationForUser(user, req.getComment(), TimeUtil.toUTC(System.currentTimeMillis())); var audit = new LinkedHashMap(); audit.put("appId", usrApp.getAppId()); audit.put("userAppId", usrApp.getAppId()); diff --git a/src/main/java/com/ghostchu/btn/sparkle/module/userapp/UserApplicationService.java b/src/main/java/com/ghostchu/btn/sparkle/module/userapp/UserApplicationService.java index 07a8bcb..a0fb4c8 100644 --- a/src/main/java/com/ghostchu/btn/sparkle/module/userapp/UserApplicationService.java +++ b/src/main/java/com/ghostchu/btn/sparkle/module/userapp/UserApplicationService.java @@ -15,6 +15,8 @@ import org.springframework.stereotype.Service; import java.sql.Timestamp; +import java.time.OffsetDateTime; +import java.time.temporal.ChronoField; import java.util.List; import java.util.Optional; import java.util.UUID; @@ -60,7 +62,7 @@ public List getUserApplications(User user) { @Modifying @Transactional - public UserApplication generateUserApplicationForUser(User user, String comment, Timestamp createdAt) throws TooManyUserApplicationException { + public UserApplication generateUserApplicationForUser(User user, String comment, OffsetDateTime createdAt) throws TooManyUserApplicationException { long userOwnedApps = userApplicationRepository.countByUser(user); if(userOwnedApps >= userMaxApps){ throw new TooManyUserApplicationException(); @@ -116,7 +118,7 @@ public UserApplicationDto toDto(UserApplication userApplication) { .user(userService.toDto(userApplication.getUser())) .comment(userApplication.getComment()) .appId(userApplication.getAppId())/**/ - .createdAt(userApplication.getCreatedAt().getTime()) + .createdAt(userApplication.getCreatedAt().getLong(ChronoField.MILLI_OF_DAY)) .build(); } @@ -128,7 +130,7 @@ public UserApplicationVerboseDto toVerboseDto(UserApplication userApplication) { .comment(userApplication.getComment()) .appId(userApplication.getAppId()) .appSecret(userApplication.getAppSecret()) - .createdAt(userApplication.getCreatedAt().getTime()) + .createdAt(userApplication.getCreatedAt().getLong(ChronoField.MILLI_OF_DAY)) .build(); } } diff --git a/src/main/java/com/ghostchu/btn/sparkle/module/userapp/UserApplicationViewController.java b/src/main/java/com/ghostchu/btn/sparkle/module/userapp/UserApplicationViewController.java index 144b0a3..16cba3b 100644 --- a/src/main/java/com/ghostchu/btn/sparkle/module/userapp/UserApplicationViewController.java +++ b/src/main/java/com/ghostchu/btn/sparkle/module/userapp/UserApplicationViewController.java @@ -6,6 +6,7 @@ import com.ghostchu.btn.sparkle.exception.UserApplicationNotFoundException; import com.ghostchu.btn.sparkle.exception.UserNotFoundException; import com.ghostchu.btn.sparkle.module.user.UserService; +import com.ghostchu.btn.sparkle.util.TimeUtil; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @@ -68,7 +69,7 @@ public String createUserApplication() { @PostMapping("/create") public String createUserApplication(Model model, @RequestParam String comment) throws UserNotFoundException, TooManyUserApplicationException { var user = userService.getUser(StpUtil.getLoginIdAsLong()).orElseThrow(); - var usrApp = userApplicationService.generateUserApplicationForUser(user, comment, new Timestamp(System.currentTimeMillis())); + var usrApp = userApplicationService.generateUserApplicationForUser(user, comment, TimeUtil.toUTC(System.currentTimeMillis())); model.addAttribute("userapp", usrApp); return "modules/userapp/created"; } diff --git a/src/main/java/com/ghostchu/btn/sparkle/module/userapp/internal/UserApplication.java b/src/main/java/com/ghostchu/btn/sparkle/module/userapp/internal/UserApplication.java index 65c6007..2acffba 100644 --- a/src/main/java/com/ghostchu/btn/sparkle/module/userapp/internal/UserApplication.java +++ b/src/main/java/com/ghostchu/btn/sparkle/module/userapp/internal/UserApplication.java @@ -7,7 +7,7 @@ import lombok.NoArgsConstructor; import lombok.Setter; -import java.sql.Timestamp; +import java.time.OffsetDateTime; @Entity @Table(name = "userapp", uniqueConstraints = {@UniqueConstraint(columnNames = "appId")}, @@ -28,7 +28,7 @@ public class UserApplication { @Column(nullable = false) private String comment; @Column(nullable = false) - private Timestamp createdAt; + private OffsetDateTime createdAt; @JoinColumn(nullable = false, name = "user") @ManyToOne(fetch = FetchType.LAZY) private User user; diff --git a/src/main/java/com/ghostchu/btn/sparkle/util/TimeUtil.java b/src/main/java/com/ghostchu/btn/sparkle/util/TimeUtil.java new file mode 100644 index 0000000..0bc3473 --- /dev/null +++ b/src/main/java/com/ghostchu/btn/sparkle/util/TimeUtil.java @@ -0,0 +1,12 @@ +package com.ghostchu.btn.sparkle.util; + +import java.time.Instant; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; + +public class TimeUtil { + public static OffsetDateTime toUTC(long ts) { + var instant = Instant.ofEpochMilli(ts); + return OffsetDateTime.ofInstant(instant, ZoneOffset.UTC); + } +}