minglipro 1a9553b55d
All checks were successful
Gitea Actions Build / Build (push) Successful in 1m1s
refactor(utils): 重构工具类并添加新功能
- 重构了 AutoConfiguration 类,更新了组件扫描路径
- 优化了 Collection 类中的 getOrDefault 方法,增加了对非 List 集合的支持- 添加了 DateTime 类的高精度时间处理功能
- 新增了 DateTimeToStringConverter 类,用于将 DateTime 转换为字符串
- 更新了 ForEach 类中的遍历方法,提高了代码可读性和性能
2025-09-11 09:46:03 +08:00

539 lines
14 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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-09 08:37:33
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.time;
import com.mingliqiye.utils.jna.time.WinKernel32;
import com.mingliqiye.utils.system.SystemUtil;
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 lombok.Getter;
import lombok.Setter;
import lombok.var;
import org.jetbrains.annotations.NotNull;
/**
* 时间类,用于处理日期时间的转换、格式化等操作。
* 提供了多种静态方法来创建 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 WinKernel32 WIN_KERNEL_32;
private static final long FILETIME_EPOCH_OFFSET = -116444736000000000L;
private static final long NANOS_PER_100NS = 100;
static {
if (!SystemUtil.isJdk8Plus() && SystemUtil.isWindows()) {
WIN_KERNEL_32 = WinKernel32.load();
} else {
WIN_KERNEL_32 = 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 != null) {
byte[] fileTimeBuffer = new byte[8];
WIN_KERNEL_32.GetSystemTimePreciseAsFileTime(fileTimeBuffer);
long fileTime =
(long) (fileTimeBuffer[0] & 0xFF) |
((long) (fileTimeBuffer[1] & 0xFF) << 8) |
((long) (fileTimeBuffer[2] & 0xFF) << 16) |
((long) (fileTimeBuffer[3] & 0xFF) << 24) |
((long) (fileTimeBuffer[4] & 0xFF) << 32) |
((long) (fileTimeBuffer[5] & 0xFF) << 40) |
((long) (fileTimeBuffer[6] & 0xFF) << 48) |
((long) (fileTimeBuffer[7] & 0xFF) << 56);
long unixNanos =
(fileTime + FILETIME_EPOCH_OFFSET) * NANOS_PER_100NS;
Instant instant = Instant.ofEpochSecond(
unixNanos / 1_000_000_000L,
unixNanos % 1_000_000_000L
);
return DateTime.of(
instant.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());
}
}