generated from mingliqiye/lib-tem
- 重构 AesUtils.kt,使用 Kotlin 标准库的 encode 和 decode 方法替代自定义实现- 删除 Java 版本的 Base64Utils 类,迁移到 Kotlin 实现 - 重命名 ByteUtil.kt 为 ByteUtils.kt,统一命名风格 - 删除 Java 版本的 CloneUtil 类和 Factory 类,使用 Kotlin 实现 - 新增 Kotlin 版本的 SpringBeanUtils 工具类重构项目,迁移到 Kotlin - 将 Java 文件转换为 Kotlin 文件 - 更新项目结构和命名- 优化代码实现 - 添加必要的依赖
553 lines
14 KiB
Java
553 lines
14 KiB
Java
/*
|
||
* Copyright 2025 mingliqiye
|
||
*
|
||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
* you may not use this file except in compliance with the License.
|
||
* You may obtain a copy of the License at
|
||
*
|
||
* http://www.apache.org/licenses/LICENSE-2.0
|
||
*
|
||
* Unless required by applicable law or agreed to in writing, software
|
||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
* See the License for the specific language governing permissions and
|
||
* limitations under the License.
|
||
*
|
||
* ProjectName mingli-utils
|
||
* ModuleName mingli-utils.main
|
||
* CurrentFile DateTime.java
|
||
* LastUpdate 2025-09-14 22:05:19
|
||
* UpdateUser MingLiPro
|
||
*/
|
||
|
||
package com.mingliqiye.utils.time;
|
||
|
||
import com.mingliqiye.utils.jna.WinKernel32Api;
|
||
import com.mingliqiye.utils.jna.WinKernel32ApiFactory;
|
||
import com.mingliqiye.utils.system.SystemUtils;
|
||
import lombok.Getter;
|
||
import lombok.Setter;
|
||
import lombok.val;
|
||
import lombok.var;
|
||
import org.jetbrains.annotations.NotNull;
|
||
import org.slf4j.Logger;
|
||
|
||
import java.io.Serializable;
|
||
import java.time.Instant;
|
||
import java.time.LocalDateTime;
|
||
import java.time.ZoneId;
|
||
import java.time.format.DateTimeFormatter;
|
||
import java.time.temporal.ChronoUnit;
|
||
import java.util.Date;
|
||
|
||
import static com.mingliqiye.utils.jna.WinKernel32ApiFactory.FILETIME_EPOCH_OFFSET;
|
||
import static com.mingliqiye.utils.jna.WinKernel32ApiFactory.NANOS_PER_100NS;
|
||
import static com.mingliqiye.utils.logger.Loggers.getMingLiLoggerFactory;
|
||
|
||
/**
|
||
* 时间类,用于处理日期时间的转换、格式化等操作。
|
||
* 提供了多种静态方法来创建 DateTime 实例,并支持与 Date、LocalDateTime 等类型的互转。
|
||
*<br>
|
||
* windows java 1.8 及以下 使用windows Api 获取高精度时间
|
||
*
|
||
* @author MingLiPro
|
||
* @see java.time
|
||
* @see LocalDateTime
|
||
* @see ChronoUnit
|
||
* @see Date
|
||
* @see DateTimeFormatter
|
||
* @see ZoneId
|
||
* @see Instant
|
||
*/
|
||
@Setter
|
||
public final class DateTime implements Serializable {
|
||
|
||
private static final WinKernel32Api WIN_KERNEL_32_API;
|
||
|
||
static {
|
||
if (SystemUtils.getJavaVersionAsInteger() == 8 && SystemUtils.isWindows()) {
|
||
|
||
final Logger log = getMingLiLoggerFactory().getLogger(
|
||
"mingli-utils DateTime"
|
||
);
|
||
val a = WinKernel32ApiFactory.getWinKernel32Apis();
|
||
|
||
if (a.size() > 1) {
|
||
log.warn(
|
||
"Multiple Size:{} WinKernel32Api implementations found.",
|
||
a.size()
|
||
);
|
||
a.forEach(api ->
|
||
log.warn(
|
||
"Found WinKernel32Api: {}",
|
||
api.getClass().getName()
|
||
)
|
||
);
|
||
}
|
||
|
||
if (a.isEmpty()) {
|
||
WIN_KERNEL_32_API = null;
|
||
log.warn("No WinKernel32Api implementation found. Use Jdk1.8 LocalDateTime");
|
||
} else {
|
||
WIN_KERNEL_32_API = a.get(a.size() - 1);
|
||
log.info("Found and Use WinKernel32Api: {}", WIN_KERNEL_32_API.getClass().getName());
|
||
}
|
||
} else {
|
||
WIN_KERNEL_32_API = null;
|
||
}
|
||
}
|
||
|
||
@Getter
|
||
private ZoneId zoneId = ZoneId.systemDefault();
|
||
|
||
@Getter
|
||
private LocalDateTime localDateTime;
|
||
|
||
/**
|
||
* 私有构造函数,使用指定的 LocalDateTime 初始化实例。
|
||
*
|
||
* @param time LocalDateTime 对象
|
||
*/
|
||
private DateTime(LocalDateTime time) {
|
||
setLocalDateTime(time);
|
||
}
|
||
|
||
/**
|
||
* 私有构造函数,使用当前系统时间初始化实例。
|
||
*/
|
||
private DateTime() {
|
||
setLocalDateTime(LocalDateTime.now());
|
||
}
|
||
|
||
/**
|
||
* 获取当前时间的 DateTime 实例。
|
||
* 如果运行在 Java 1.8 环境下,则通过 WinKernel32 获取高精度时间。
|
||
*
|
||
* @return 返回当前时间的 DateTime 实例
|
||
*/
|
||
public static DateTime now() {
|
||
if (WIN_KERNEL_32_API != null) {
|
||
return DateTime.of(
|
||
WIN_KERNEL_32_API.getTime().atZone(ZoneId.systemDefault()).toLocalDateTime()
|
||
);
|
||
}
|
||
return new DateTime();
|
||
}
|
||
|
||
/**
|
||
* 将 Date 对象转换为 DateTime 实例。
|
||
*
|
||
* @param zoneId 时区信息
|
||
* @param date Date 对象
|
||
* @return 返回对应的 DateTime 实例
|
||
*/
|
||
public static DateTime of(Date date, ZoneId zoneId) {
|
||
return new DateTime(date.toInstant().atZone(zoneId).toLocalDateTime());
|
||
}
|
||
|
||
/**
|
||
* 将 Date 对象转换为 DateTime 实例,使用系统默认时区。
|
||
*
|
||
* @param date Date 对象
|
||
* @return 返回对应的 DateTime 实例
|
||
*/
|
||
public static DateTime of(Date date) {
|
||
return new DateTime(
|
||
date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime()
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 根据 LocalDateTime 创建 DateTime 实例。
|
||
*
|
||
* @param localDateTime LocalDateTime 对象
|
||
* @return 返回对应的 DateTime 实例
|
||
*/
|
||
public static DateTime of(LocalDateTime localDateTime) {
|
||
return new DateTime(localDateTime);
|
||
}
|
||
|
||
/**
|
||
* 解析时间字符串并生成 DateTime 实例。
|
||
*
|
||
* @param timestr 时间字符串
|
||
* @param formatter 格式化模板
|
||
* @param fillZero 是否补零到模板长度
|
||
* @return 返回解析后的 DateTime 实例
|
||
*/
|
||
public static DateTime parse(
|
||
String timestr,
|
||
String formatter,
|
||
boolean fillZero
|
||
) {
|
||
return new DateTime(
|
||
LocalDateTime.parse(
|
||
fillZero ? getFillZeroByLen(timestr, formatter) : timestr,
|
||
DateTimeFormatter.ofPattern(formatter)
|
||
)
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 使用 Formatter 枚举解析时间字符串并生成 DateTime 实例。
|
||
*
|
||
* @param timestr 时间字符串
|
||
* @param formatter 格式化模板枚举
|
||
* @param fillZero 是否补零到模板长度
|
||
* @return 返回解析后的 DateTime 实例
|
||
*/
|
||
public static DateTime parse(
|
||
String timestr,
|
||
Formatter formatter,
|
||
boolean fillZero
|
||
) {
|
||
return parse(timestr, formatter.getValue(), fillZero);
|
||
}
|
||
|
||
/**
|
||
* 使用 Formatter 枚举解析时间字符串并生成 DateTime 实例,默认不补零。
|
||
*
|
||
* @param timestr 时间字符串
|
||
* @param formatter 格式化模板枚举
|
||
* @return 返回解析后的 DateTime 实例
|
||
*/
|
||
public static DateTime parse(String timestr, Formatter formatter) {
|
||
return parse(timestr, formatter.getValue());
|
||
}
|
||
|
||
/**
|
||
* 解析时间字符串并生成 DateTime 实例,默认不补零。
|
||
*
|
||
* @param timestr 时间字符串
|
||
* @param formatter 格式化模板
|
||
* @return 返回解析后的 DateTime 实例
|
||
*/
|
||
public static DateTime parse(String timestr, String formatter) {
|
||
return parse(timestr, formatter, false);
|
||
}
|
||
|
||
/**
|
||
* 补零处理时间字符串以匹配格式化模板长度。
|
||
*
|
||
* @param dstr 原始时间字符串
|
||
* @param formats 格式化模板
|
||
* @return 补零后的时间字符串
|
||
*/
|
||
private static String getFillZeroByLen(String dstr, String formats) {
|
||
if (dstr.length() == formats.length()) {
|
||
return dstr;
|
||
}
|
||
if (formats.length() > dstr.length()) {
|
||
if (dstr.length() == 19) {
|
||
dstr += ".";
|
||
}
|
||
var sb = new StringBuilder(dstr);
|
||
for (int i = 0; i < formats.length() - dstr.length(); i++) {
|
||
sb.append("0");
|
||
}
|
||
return sb.toString();
|
||
}
|
||
throw new IllegalArgumentException(
|
||
String.format(
|
||
"Text: '%s' len %s < %s %s",
|
||
dstr,
|
||
dstr.length(),
|
||
formats,
|
||
formats.length()
|
||
)
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 根据年、月、日创建 DateTime 实例
|
||
*
|
||
* @param year 年份
|
||
* @param month 月份 (1-12)
|
||
* @param day 日期 (1-31)
|
||
* @return 返回指定日期的 DateTime 实例(时间部分为 00:00:00)
|
||
*/
|
||
public static DateTime of(int year, int month, int day) {
|
||
return new DateTime(LocalDateTime.of(year, month, day, 0, 0));
|
||
}
|
||
|
||
/**
|
||
* 根据年、月、日、时、分创建 DateTime 实例
|
||
*
|
||
* @param year 年份
|
||
* @param month 月份 (1-12)
|
||
* @param day 日期 (1-31)
|
||
* @param hour 小时 (0-23)
|
||
* @param minute 分钟 (0-59)
|
||
* @return 返回指定日期时间的 DateTime 实例(秒部分为 00)
|
||
*/
|
||
public static DateTime of(
|
||
int year,
|
||
int month,
|
||
int day,
|
||
int hour,
|
||
int minute
|
||
) {
|
||
return new DateTime(LocalDateTime.of(year, month, day, hour, minute));
|
||
}
|
||
|
||
/**
|
||
* 将 FILETIME 转换为 LocalDateTime。
|
||
*
|
||
* @param fileTime FILETIME 时间戳(100纳秒单位自1601年1月1日起)
|
||
* @return 转换后的 LocalDateTime 实例
|
||
*/
|
||
public static LocalDateTime fileTimeToLocalDateTime(long fileTime) {
|
||
// 1. 将 FILETIME (100ns间隔 since 1601) 转换为 Unix 时间戳 (纳秒 since 1970)
|
||
long unixNanos = (fileTime + FILETIME_EPOCH_OFFSET) * NANOS_PER_100NS;
|
||
|
||
// 2. 从纳秒时间戳创建 Instant
|
||
Instant instant = Instant.ofEpochSecond(
|
||
unixNanos / 1_000_000_000L,
|
||
unixNanos % 1_000_000_000L
|
||
);
|
||
|
||
// 3. 转换为系统默认时区的 LocalDateTime
|
||
return instant.atZone(ZoneId.systemDefault()).toLocalDateTime();
|
||
}
|
||
|
||
/**
|
||
* 根据年、月、日、时、分、秒创建 DateTime 实例
|
||
*
|
||
* @param year 年份
|
||
* @param month 月份 (1-12)
|
||
* @param day 日期 (1-31)
|
||
* @param hour 小时 (0-23)
|
||
* @param minute 分钟 (0-59)
|
||
* @param second 秒 (0-59)
|
||
* @return 返回指定日期时间的 DateTime 实例
|
||
*/
|
||
public static DateTime of(
|
||
int year,
|
||
int month,
|
||
int day,
|
||
int hour,
|
||
int minute,
|
||
int second
|
||
) {
|
||
return new DateTime(
|
||
LocalDateTime.of(year, month, day, hour, minute, second)
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 根据年、月、日、时、分、秒、纳秒创建 DateTime 实例
|
||
*
|
||
* @param year 年份
|
||
* @param month 月份 (1-12)
|
||
* @param day 日期 (1-31)
|
||
* @param hour 小时 (0-23)
|
||
* @param minute 分钟 (0-59)
|
||
* @param second 秒 (0-59)
|
||
* @param nano 纳秒 (0-999,999,999)
|
||
* @return 返回指定日期时间的 DateTime 实例
|
||
*/
|
||
public static DateTime of(
|
||
int year,
|
||
int month,
|
||
int day,
|
||
int hour,
|
||
int minute,
|
||
int second,
|
||
int nano
|
||
) {
|
||
return new DateTime(
|
||
LocalDateTime.of(year, month, day, hour, minute, second, nano)
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 根据毫秒时间戳创建 DateTime 实例
|
||
*
|
||
* @param epochMilli 毫秒时间戳
|
||
* @return 返回对应时间的 DateTime 实例
|
||
*/
|
||
public static DateTime of(long epochMilli) {
|
||
return new DateTime(
|
||
Instant.ofEpochMilli(epochMilli)
|
||
.atZone(ZoneId.systemDefault())
|
||
.toLocalDateTime()
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 根据毫秒时间戳和时区创建 DateTime 实例
|
||
*
|
||
* @param epochMilli 毫秒时间戳
|
||
* @param zoneId 时区信息
|
||
* @return 返回对应时间的 DateTime 实例
|
||
*/
|
||
public static DateTime of(long epochMilli, ZoneId zoneId) {
|
||
return new DateTime(
|
||
Instant.ofEpochMilli(epochMilli).atZone(zoneId).toLocalDateTime()
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 将当前 DateTime 转换为 Date 对象。
|
||
*
|
||
* @return 返回对应的 Date 对象
|
||
*/
|
||
public Date toDate() {
|
||
return Date.from(localDateTime.atZone(getZoneId()).toInstant());
|
||
}
|
||
|
||
/**
|
||
* 获取当前 DateTime 中的 LocalDateTime 实例。
|
||
*
|
||
* @return 返回 LocalDateTime 对象
|
||
*/
|
||
public LocalDateTime toLocalDateTime() {
|
||
return localDateTime;
|
||
}
|
||
|
||
/**
|
||
* 在当前时间基础上增加指定的时间偏移量。
|
||
*
|
||
* @param dateTimeOffset 时间偏移对象
|
||
* @return 返回修改后的 DateTime 实例
|
||
*/
|
||
public DateTime add(DateTimeOffset dateTimeOffset) {
|
||
return new DateTime(
|
||
this.localDateTime.plus(
|
||
dateTimeOffset.getOffset(),
|
||
dateTimeOffset.getOffsetType()
|
||
)
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 在当前时间基础上减少指定的时间偏移量。
|
||
*
|
||
* @param dateTimeOffset 时间偏移对象
|
||
* @return 返回修改后的 DateTime 实例
|
||
*/
|
||
public DateTime sub(DateTimeOffset dateTimeOffset) {
|
||
return new DateTime(
|
||
this.localDateTime.plus(
|
||
-dateTimeOffset.getOffset(),
|
||
dateTimeOffset.getOffsetType()
|
||
)
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 使用指定格式化模板将当前时间格式化为字符串。
|
||
*
|
||
* @param formatter 格式化模板
|
||
* @return 返回格式化后的时间字符串
|
||
*/
|
||
public String format(String formatter) {
|
||
return format(formatter, false);
|
||
}
|
||
|
||
/**
|
||
* 使用 Formatter 枚举将当前时间格式化为字符串。
|
||
*
|
||
* @param formatter 格式化模板枚举
|
||
* @return 返回格式化后的时间字符串
|
||
*/
|
||
public String format(Formatter formatter) {
|
||
return format(formatter.getValue());
|
||
}
|
||
|
||
/**
|
||
* 使用指定格式化模板将当前时间格式化为字符串,并可选择是否去除末尾多余的零。
|
||
*
|
||
* @param formatter 格式化模板
|
||
* @param repcZero 是否去除末尾多余的零
|
||
* @return 返回格式化后的时间字符串
|
||
*/
|
||
public String format(String formatter, boolean repcZero) {
|
||
var formatted = DateTimeFormatter.ofPattern(formatter).format(
|
||
toLocalDateTime()
|
||
);
|
||
if (repcZero) {
|
||
// 处理小数点后多余的0
|
||
formatted = formatted.replaceAll("(\\.\\d*?)0+\\b", "$1");
|
||
formatted = formatted.replaceAll("\\.$", "");
|
||
}
|
||
return formatted;
|
||
}
|
||
|
||
/**
|
||
* 使用 Formatter 枚举将当前时间格式化为字符串,并可选择是否去除末尾多余的零。
|
||
*
|
||
* @param formatter 格式化模板枚举
|
||
* @param repcZero 是否去除末尾多余的零
|
||
* @return 返回格式化后的时间字符串
|
||
*/
|
||
public String format(Formatter formatter, boolean repcZero) {
|
||
return format(formatter.getValue(), repcZero);
|
||
}
|
||
|
||
/**
|
||
* 返回当前时间的标准字符串表示形式。
|
||
*
|
||
* @return 返回标准格式的时间字符串
|
||
*/
|
||
@Override
|
||
public String toString() {
|
||
return String.format(
|
||
"DateTime(%s)",
|
||
format(Formatter.STANDARD_DATETIME_MILLISECOUND7, true)
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 比较当前DateTime对象与指定对象是否相等
|
||
*
|
||
* @param obj 要比较的对象
|
||
* @return 如果对象相等则返回true,否则返回false
|
||
*/
|
||
@Override
|
||
public boolean equals(Object obj) {
|
||
// 检查对象类型是否为DateTime
|
||
if (obj instanceof DateTime) {
|
||
// 比较两个DateTime对象转换为LocalDateTime后的值
|
||
return toLocalDateTime().equals(((DateTime) obj).toLocalDateTime());
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* 将当前 DateTime 转换为 Instant 对象。
|
||
*
|
||
* @return 返回 Instant 对象
|
||
*/
|
||
@NotNull
|
||
public Instant toInstant() {
|
||
return localDateTime.atZone(zoneId).toInstant();
|
||
}
|
||
|
||
/**
|
||
* 判断当前时间是否在指定时间之后。
|
||
*
|
||
* @param dateTime 指定时间
|
||
* @return 如果当前时间在指定时间之后则返回 true,否则返回 false
|
||
*/
|
||
public boolean isAfter(DateTime dateTime) {
|
||
if (dateTime == null) {
|
||
return false;
|
||
}
|
||
return toInstant().isAfter(dateTime.toInstant());
|
||
}
|
||
|
||
/**
|
||
* 判断当前时间是否在指定时间之前。
|
||
*
|
||
* @param dateTime 指定时间
|
||
* @return 如果当前时间在指定时间之前则返回 true,否则返回 false
|
||
*/
|
||
public boolean isBefore(DateTime dateTime) {
|
||
if (dateTime == null) {
|
||
return false;
|
||
}
|
||
return toInstant().isBefore(dateTime.toInstant());
|
||
}
|
||
}
|