refactor(time): 重构 DateTime 类并添加新功能

- 从 Java 文件中删除了旧的 DateTime 类
- 新增 Kotlin 版本的 DateTime 类,具有以下改进:
  - 添加了时间格式化枚举类 Formatter
  - 新增时间位移类 DateTimeOffset - 重构了 parse 和 format 方法,支持新的 Formatter 枚举  - 优化了文件时间转换方法,使用纳秒精度
- 删除了旧的 DateTimeJsonConverter 类
This commit is contained in:
Armamem0t 2025-09-15 11:20:08 +08:00
parent dc129c016f
commit 7526b2e787
Signed by: minglipro
GPG Key ID: 5F355A77B22AA93B
50 changed files with 1732 additions and 2705 deletions

View File

@ -16,7 +16,7 @@
* ProjectName mingli-utils * ProjectName mingli-utils
* ModuleName mingli-utils * ModuleName mingli-utils
* CurrentFile build.gradle.kts * CurrentFile build.gradle.kts
* LastUpdate 2025-09-14 22:32:52 * LastUpdate 2025-09-15 11:20:04
* UpdateUser MingLiPro * UpdateUser MingLiPro
*/ */
@ -31,7 +31,6 @@ plugins {
kotlin("jvm") version "2.2.20" kotlin("jvm") version "2.2.20"
id("org.jetbrains.dokka") version "2.0.0" id("org.jetbrains.dokka") version "2.0.0"
} }
val GROUPSID = project.properties["GROUPSID"] as String val GROUPSID = project.properties["GROUPSID"] as String
val VERSIONS = project.properties["VERSIONS"] as String val VERSIONS = project.properties["VERSIONS"] as String
val ARTIFACTID = project.properties["ARTIFACTID"] as String val ARTIFACTID = project.properties["ARTIFACTID"] as String

View File

@ -16,10 +16,10 @@
# ProjectName mingli-utils # ProjectName mingli-utils
# ModuleName mingli-utils # ModuleName mingli-utils
# CurrentFile gradle.properties # CurrentFile gradle.properties
# LastUpdate 2025-09-15 09:25:10 # LastUpdate 2025-09-15 11:19:10
# UpdateUser MingLiPro # UpdateUser MingLiPro
# #
JDKVERSIONS=1.8 JDKVERSIONS=1.8
GROUPSID=com.mingliqiye.utils GROUPSID=com.mingliqiye.utils
ARTIFACTID=mingli-utils ARTIFACTID=mingli-utils
VERSIONS=4.0.0-pre VERSIONS=4.0.1

View File

@ -16,7 +16,7 @@
* ProjectName mingli-utils * ProjectName mingli-utils
* ModuleName mingli-utils.main * ModuleName mingli-utils.main
* CurrentFile Main.java * CurrentFile Main.java
* LastUpdate 2025-09-15 09:24:07 * LastUpdate 2025-09-15 11:18:12
* UpdateUser MingLiPro * UpdateUser MingLiPro
*/ */

View File

@ -1,93 +0,0 @@
/*
* 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 Debouncer.java
* LastUpdate 2025-09-09 08:37:34
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.functions;
import java.util.concurrent.*;
/**
* 防抖器类用于实现防抖功能防止在短时间内重复执行相同任务
*
* @author MingLiPro
*/
public class Debouncer {
private final ScheduledExecutorService scheduler =
Executors.newSingleThreadScheduledExecutor();
private final ConcurrentHashMap<Object, Future<?>> delayedMap =
new ConcurrentHashMap<>();
private final long delay;
/**
* 构造函数创建一个防抖器实例
*
* @param delay 延迟时间
* @param unit 时间单位
*/
public Debouncer(long delay, TimeUnit unit) {
this.delay = unit.toMillis(delay);
}
/**
* 执行防抖操作如果在指定延迟时间内再次调用相同key的任务则取消之前的任务并重新计时
*
* @param key 任务的唯一标识符用于区分不同任务
* @param task 要执行的任务
*/
public void debounce(final Object key, final Runnable task) {
// 提交新任务并获取之前可能存在的任务
final Future<?> prev = delayedMap.put(
key,
scheduler.schedule(
() -> {
try {
task.run();
} finally {
// 任务执行完成后从映射中移除
delayedMap.remove(key);
}
},
delay,
TimeUnit.MILLISECONDS
)
);
// 如果之前存在任务则取消它
if (prev != null) {
prev.cancel(true);
}
}
/**
* 关闭防抖器取消所有待执行的任务并关闭调度器
*/
public void shutdown() {
// 先取消所有延迟任务
for (Future<?> future : delayedMap.values()) {
future.cancel(true);
}
delayedMap.clear();
// 再关闭调度器
scheduler.shutdownNow();
}
}

View File

@ -1,39 +0,0 @@
/*
* 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 P10Function.java
* LastUpdate 2025-09-09 08:37:33
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.functions;
@FunctionalInterface
public interface P10Function<P, P1, P2, P3, P4, P5, P6, P7, P8, P9> {
void call(
P p,
P1 p1,
P2 p2,
P3 p3,
P4 p4,
P5 p5,
P6 p6,
P7 p7,
P8 p8,
P9 p9
);
}

View File

@ -1,28 +0,0 @@
/*
* 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 P10RFunction.java
* LastUpdate 2025-09-09 08:37:33
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.functions;
@FunctionalInterface
public interface P10RFunction<P, P1, P2, P3, P4, P5, P6, P7, P8, P9, R> {
R call(P p, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8, P9 p9);
}

View File

@ -1,28 +0,0 @@
/*
* 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 P1Function.java
* LastUpdate 2025-09-09 08:37:33
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.functions;
@FunctionalInterface
public interface P1Function<P> {
void call(P p);
}

View File

@ -1,28 +0,0 @@
/*
* 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 P1RFunction.java
* LastUpdate 2025-09-09 08:37:34
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.functions;
@FunctionalInterface
public interface P1RFunction<P, R> {
R call(P p);
}

View File

@ -1,28 +0,0 @@
/*
* 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 P2Function.java
* LastUpdate 2025-09-09 08:37:33
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.functions;
@FunctionalInterface
public interface P2Function<P, P1> {
void call(P p, P1 p1);
}

View File

@ -1,28 +0,0 @@
/*
* 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 P2RFunction.java
* LastUpdate 2025-09-09 08:37:34
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.functions;
@FunctionalInterface
public interface P2RFunction<P, P1, R> {
R call(P p, P1 p1);
}

View File

@ -1,28 +0,0 @@
/*
* 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 P3Function.java
* LastUpdate 2025-09-09 08:37:33
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.functions;
@FunctionalInterface
public interface P3Function<P, P1, P2> {
void call(P p, P1 p1, P2 p2);
}

View File

@ -1,28 +0,0 @@
/*
* 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 P3RFunction.java
* LastUpdate 2025-09-09 08:37:33
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.functions;
@FunctionalInterface
public interface P3RFunction<P, P1, P2, R> {
R call(P p, P1 p1, P2 p2);
}

View File

@ -1,28 +0,0 @@
/*
* 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 P4Function.java
* LastUpdate 2025-09-09 08:37:33
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.functions;
@FunctionalInterface
public interface P4Function<P, P1, P2, P3> {
void call(P p, P1 p1, P2 p2, P3 p3);
}

View File

@ -1,28 +0,0 @@
/*
* 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 P4RFunction.java
* LastUpdate 2025-09-09 08:37:34
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.functions;
@FunctionalInterface
public interface P4RFunction<P, P1, P2, P3, R> {
R call(P p, P1 p1, P2 p2, P3 p3);
}

View File

@ -1,28 +0,0 @@
/*
* 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 P5Function.java
* LastUpdate 2025-09-09 08:37:33
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.functions;
@FunctionalInterface
public interface P5Function<P, P1, P2, P3, P4> {
void call(P p, P1 p1, P2 p2, P3 p3, P4 p4);
}

View File

@ -1,28 +0,0 @@
/*
* 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 P5RFunction.java
* LastUpdate 2025-09-09 08:37:33
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.functions;
@FunctionalInterface
public interface P5RFunction<P, P1, P2, P3, P4, R> {
R call(P p, P1 p1, P2 p2, P3 p3, P4 p4);
}

View File

@ -1,28 +0,0 @@
/*
* 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 P6Function.java
* LastUpdate 2025-09-09 08:37:33
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.functions;
@FunctionalInterface
public interface P6Function<P, P1, P2, P3, P4, P5> {
void call(P p, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5);
}

View File

@ -1,28 +0,0 @@
/*
* 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 P6RFunction.java
* LastUpdate 2025-09-09 08:37:33
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.functions;
@FunctionalInterface
public interface P6RFunction<P, P1, P2, P3, P4, P5, R> {
R call(P p, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5);
}

View File

@ -1,28 +0,0 @@
/*
* 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 P7Function.java
* LastUpdate 2025-09-09 08:37:34
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.functions;
@FunctionalInterface
public interface P7Function<P, P1, P2, P3, P4, P5, P6> {
void call(P p, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6);
}

View File

@ -1,28 +0,0 @@
/*
* 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 P7RFunction.java
* LastUpdate 2025-09-09 08:37:33
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.functions;
@FunctionalInterface
public interface P7RFunction<P, P1, P2, P3, P4, P5, P6, R> {
R call(P p, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6);
}

View File

@ -1,28 +0,0 @@
/*
* 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 P8Function.java
* LastUpdate 2025-09-09 08:37:34
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.functions;
@FunctionalInterface
public interface P8Function<P, P1, P2, P3, P4, P5, P6, P7> {
void call(P p, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7);
}

View File

@ -1,28 +0,0 @@
/*
* 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 P9Function.java
* LastUpdate 2025-09-09 08:37:33
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.functions;
@FunctionalInterface
public interface P9Function<P, P1, P2, P3, P4, P5, P6, P7, P8> {
void call(P p, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8);
}

View File

@ -1,28 +0,0 @@
/*
* 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 P9RFunction.java
* LastUpdate 2025-09-09 08:37:33
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.functions;
@FunctionalInterface
public interface P9RFunction<P, P1, P2, P3, P4, P5, P6, P7, P8, R> {
R call(P p, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8);
}

View File

@ -1,114 +0,0 @@
/*
* 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 HashUtils.java
* LastUpdate 2025-09-09 08:37:33
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.hash;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.mindrot.jbcrypt.BCrypt;
/**
* 提供常用的哈希计算工具方法包括文件哈希值计算BCrypt 加密等
*
* @author MingLiPro
*/
public class HashUtils {
static {
Security.addProvider(new BouncyCastleProvider());
}
/**
* 计算指定文件的哈希值
*
* @param file 要计算哈希值的文件对象
* @param algorithm 使用的哈希算法名称 SHA-256MD5
* @return 文件的十六进制格式哈希值字符串
* @throws IOException 当文件不存在或读取过程中发生 I/O 错误时抛出
* @throws NoSuchAlgorithmException 当指定的哈希算法不可用时抛出
*/
public static String calculateFileHash(File file, String algorithm)
throws IOException, NoSuchAlgorithmException {
// 检查文件是否存在
if (!file.exists()) {
throw new IOException("File not found: " + file.getAbsolutePath());
}
MessageDigest digest = MessageDigest.getInstance(algorithm);
try (FileInputStream fis = new FileInputStream(file)) {
byte[] buffer = new byte[8192];
int bytesRead;
// 分块读取文件内容并更新摘要
while ((bytesRead = fis.read(buffer)) != -1) {
digest.update(buffer, 0, bytesRead);
}
}
return bytesToHex(digest.digest());
}
/**
* 将字节数组转换为十六进制字符串表示
*
* @param bytes 输入的字节数组
* @return 对应的十六进制字符串
*/
private static String bytesToHex(byte[] bytes) {
StringBuilder hexString = new StringBuilder(2 * bytes.length);
for (byte b : bytes) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
}
/**
* 使用 BCrypt 算法对字符串进行加密
*
* @param string 需要加密的明文字符串
* @return 加密后的 BCrypt 哈希字符串
*/
public static String bcrypt(String string) {
return BCrypt.hashpw(string, BCrypt.gensalt());
}
/**
* 验证给定字符串与 BCrypt 哈希是否匹配
*
* @param string 明文字符串
* @param bcrypted 已经使用 BCrypt 加密的哈希字符串
* @return 如果匹配返回 true否则返回 false
*/
public static boolean checkBcrypt(String string, String bcrypted) {
return BCrypt.checkpw(string, bcrypted);
}
}

View File

@ -1,227 +0,0 @@
/*
* 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 Range.java
* LastUpdate 2025-09-15 09:22:02
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.iterator;
import kotlin.ranges.ClosedRange;
import kotlin.ranges.OpenEndRange;
import lombok.Getter;
import lombok.val;
import org.jetbrains.annotations.NotNull;
import java.util.Iterator;
/**
* 范围 Range<br>
* Iterable 可遍历对象<br>
* 类似 KT的 {@code 0..10 = Range.of(0,10)} {@code 0..10 step 2 = Range.of(0,10,2)}
* @author MingLiPro
* @since 3.2.6
*/
@Getter
public class Range
implements Iterable<Integer>, ClosedRange<Integer>, OpenEndRange<Integer> {
private final int start;
private final int end;
private final int step;
private int current;
/**
* 创建一个范围 <br>
* 最大值{@code Integer.MAX_VALUE = 2147483647 } <br>
* 最小值{@code Integer.MIN_VALUE = -2147483648} <br>
* @param start 开始 (包含)
* @param end 完毕 (包含)
* @see Integer
*/
public Range(int start, int end) {
this(start, end, 1);
}
/**
* 创建一个范围 <br>
* 最大值{@code Integer.MAX_VALUE = 2147483647 } <br>
* 最小值{@code Integer.MIN_VALUE = -2147483648} <br>
* @param start 开始 (包含)
* @param end 完毕 (包含)
* @param step 步长
* @see Integer
*/
public Range(int start, int end, int step) {
this.start = start;
this.current = start;
this.step = step;
this.end = end + 1;
}
/**
* 创建一个范围 {@code 0 - range}<br>
* 最大值{@code Integer.MAX_VALUE = 2147483647 } <br>
* 最小值{@code Integer.MIN_VALUE = -2147483648} <br>
* @param range 完毕 (包含)
* @see Integer
*/
public Range(int range) {
this(0, range);
}
/**
* 创建一个范围 {@code 0 - range}<br>
* 最大值{@code Integer.MAX_VALUE = 2147483647 } <br>
* 最小值{@code Integer.MIN_VALUE = -2147483648} <br>
* @param range 完毕 (包含)
* @see Integer
* @return Range 对象
*/
public static Range of(int range) {
return new Range(range);
}
/**
* 创建一个范围 <br>
* 最大值{@code Integer.MAX_VALUE = 2147483647 } <br>
* 最小值{@code Integer.MIN_VALUE = -2147483648} <br>
* @param start 开始 (包含)
* @param end 完毕 (包含)
* @see Integer
* @return Range 对象
*/
public static Range of(int start, int end) {
return new Range(start, end);
}
/**
* 创建一个范围 <br>
* 最大值{@code Integer.MAX_VALUE = 2147483647 } <br>
* 最小值{@code Integer.MIN_VALUE = -2147483648} <br>
* @param start 开始 (包含)
* @param end 完毕 (包含)
* @param step 步长
* @see Integer
*/
public static Range of(int start, int end, int step) {
return new Range(start, end, step);
}
/**
* 创建一个范围 <br>
* 最大值{@code Integer.MAX_VALUE = 2147483647 } <br>
* 最小值{@code Integer.MIN_VALUE = -2147483648} <br>
* @param start 开始 (包含)
* @param end 完毕 (包含)
* @param step 步长
* @see Integer
*/
public static Range range(int start, int end, int step) {
return new Range(start, end, step);
}
/**
* 创建一个范围 {@code 0 - range}<br>
* 最大值{@code Integer.MAX_VALUE = 2147483647 } <br>
* 最小值{@code Integer.MIN_VALUE = -2147483648} <br>
* @param range 完毕 (包含)
* @see Integer
* @return Range 对象
*/
public static Range range(int range) {
return new Range(range);
}
/**
* 创建一个范围 <br>
* 最大值{@code Integer.MAX_VALUE = 2147483647 } <br>
* 最小值{@code Integer.MIN_VALUE = -2147483648} <br>
* @param start 开始 (包含)
* @param end 完毕 (包含)
* @see Integer
* @return Range 对象
*/
public static Range range(int start, int end) {
return new Range(start, end);
}
/**
* 获取迭代器
* @return 迭代器
*/
@Override
public @NotNull Iterator<Integer> iterator() {
return new Iterator<Integer>() {
@Override
public boolean hasNext() {
return current < end;
}
@Override
public Integer next() {
if (current >= end) {
return null;
}
try {
return current;
} finally {
current = current + step;
}
}
};
}
@Override
public boolean isEmpty() {
return current < end;
}
@Override
public @NotNull Integer getEndInclusive() {
val va = end - step;
return Math.max(va, 0);
}
@Override
public boolean contains(@NotNull Integer integer) {
if (step == 0) return false;
if (step > 0) {
if (integer < start || integer > end) return false;
} else {
if (integer > start || integer < end) return false;
}
return (integer - start) % step == 0;
}
@Override
public @NotNull Integer getEndExclusive() {
return end;
}
@Override
public @NotNull Integer getStart() {
return start;
}
@Override
public String toString() {
return String.format("Range(start=%s,end=%s,step=%s)", start, end, step);
}
}

View File

@ -1,13 +1,38 @@
/*
* 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 GsonJsonApi.java
* LastUpdate 2025-09-15 11:20:04
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.json; package com.mingliqiye.utils.json;
import com.google.gson.*; import com.google.gson.*;
import com.mingliqiye.utils.json.converters.JsonConverter;
import com.mingliqiye.utils.json.converters.JsonStringConverter;
import org.jetbrains.annotations.NotNull;
public class GsonJsonApi implements JsonApi { public class GsonJsonApi implements JsonApi {
private final Gson gson; private Gson gsonUnicode;
private final Gson gsonUnicode; private Gson gsonPretty;
private final Gson gsonPretty; private Gson gsonPrettyUnicode;
private final Gson gsonPrettyUnicode; private Gson gson;
public GsonJsonApi() { public GsonJsonApi() {
gson = new GsonBuilder() gson = new GsonBuilder()
@ -184,4 +209,41 @@ public class GsonJsonApi implements JsonApi {
String json = gson.toJson(source); String json = gson.toJson(source);
return gson.fromJson(json, destinationType.getType()); return gson.fromJson(json, destinationType.getType());
} }
@Override
public void addJsonConverter(@NotNull JsonConverter<?, ?> c) {
gson = gson
.newBuilder()
.registerTypeAdapter(
c.getTClass(),
c.getStringConverter().getGsonJsonStringConverterAdapter()
)
.create();
gsonUnicode = gsonUnicode
.newBuilder()
.registerTypeAdapter(
c.getTClass(),
c.getStringConverter().getGsonJsonStringConverterAdapter()
)
.create();
gsonPretty = gsonPretty
.newBuilder()
.registerTypeAdapter(
c.getTClass(),
c.getStringConverter().getGsonJsonStringConverterAdapter()
)
.create();
gsonPrettyUnicode = gsonPrettyUnicode
.newBuilder()
.registerTypeAdapter(
c.getTClass(),
c.getStringConverter().getGsonJsonStringConverterAdapter()
)
.create();
}
@Override
public void addJsonStringConverter(@NotNull JsonStringConverter<?> c) {
addJsonConverter(c);
}
} }

View File

@ -16,7 +16,7 @@
* ProjectName mingli-utils * ProjectName mingli-utils
* ModuleName mingli-utils.main * ModuleName mingli-utils.main
* CurrentFile JacksonJsonApi.java * CurrentFile JacksonJsonApi.java
* LastUpdate 2025-09-09 09:31:31 * LastUpdate 2025-09-15 11:16:53
* UpdateUser MingLiPro * UpdateUser MingLiPro
*/ */
@ -28,6 +28,10 @@ import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader; import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
import com.mingliqiye.utils.json.converters.JsonConverter;
import com.mingliqiye.utils.json.converters.JsonStringConverter;
import org.jetbrains.annotations.NotNull;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -338,4 +342,14 @@ public class JacksonJsonApi implements JsonApi {
objectMapper.constructType(destinationType.getType()) objectMapper.constructType(destinationType.getType())
); );
} }
@Override
public void addJsonConverter(@NotNull JsonConverter<?, ?> c) {
objectMapper.registerModule(c.getStringConverter().getJacksonJsonStringConverterAdapter().getJacksonModule());
}
@Override
public void addJsonStringConverter(@NotNull JsonStringConverter<?> c) {
addJsonConverter(c);
}
} }

View File

@ -1,390 +0,0 @@
/*
* 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 JsonApi.java
* LastUpdate 2025-09-09 09:22:02
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.json;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
/**
* JSON处理接口提供JSON字符串与Java对象之间的相互转换功能
*/
public interface JsonApi {
/**
* 将JSON字符串解析为指定类型的对象
*
* @param json 待解析的JSON字符串
* @param clazz 目标对象的Class类型
* @param <T> 泛型参数表示目标对象的类型
* @return 解析后的对象实例
*/
<T> T parse(String json, Class<T> clazz);
/**
* 将JSON字符串解析为指定泛型类型对象
*
* @param json 待解析的JSON字符串
* @param type 目标对象的Type类型支持泛型
* @param <T> 泛型参数表示目标对象的类型
* @return 解析后的对象实例
*/
<T> T parse(String json, JsonTypeReference<T> type);
/**
* 将对象格式化为JSON字符串
*
* @param object 待格式化的对象
* @return 格式化后的JSON字符串
*/
String format(Object object);
String formatUnicode(Object object);
default <T> T parseFrom(String path, Class<T> clazz) throws IOException {
return parseFrom(Paths.get(path), clazz);
}
default <T> T parseFrom(Path path, Class<T> clazz) throws IOException {
return parseFrom(path.toFile(), clazz);
}
default <T> T parseFrom(File file, Class<T> clazz) throws IOException {
try (InputStream inputStream = Files.newInputStream(file.toPath())) {
return parseFrom(inputStream, clazz);
}
}
default <T> T parseFrom(InputStream inputStream, Class<T> clazz)
throws IOException {
if (inputStream == null) {
throw new IllegalArgumentException("inputStream cannot be null");
}
if (clazz == null) {
throw new IllegalArgumentException("clazz cannot be null");
}
byte[] bytes = new byte[1024];
try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
int readlength;
while ((readlength = inputStream.read(bytes)) != -1) {
bos.write(bytes, 0, readlength);
}
return parse(bos.toByteArray(), clazz);
}
}
default <T> T parseFrom(String path, JsonTypeReference<T> type)
throws IOException {
return parseFrom(Paths.get(path), type);
}
default <T> T parseFrom(Path path, JsonTypeReference<T> type)
throws IOException {
return parseFrom(path.toFile(), type);
}
default <T> T parseFrom(File file, JsonTypeReference<T> type)
throws IOException {
try (InputStream inputStream = Files.newInputStream(file.toPath())) {
return parseFrom(inputStream, type);
}
}
default <T> T parseFrom(InputStream inputStream, JsonTypeReference<T> type)
throws IOException {
if (inputStream == null) {
throw new IllegalArgumentException("inputStream cannot be null");
}
if (type == null) {
throw new IllegalArgumentException("type cannot be null");
}
byte[] bytes = new byte[1024];
try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
int readlength;
while ((readlength = inputStream.read(bytes)) != -1) {
bos.write(bytes, 0, readlength);
}
return parse(bos.toByteArray(), type);
}
}
/**
* 将字节数组形式的JSON解析为指定类型的对象
*
* @param json 待解析的JSON字节数组
* @param clazz 目标对象的Class类型
* @param <T> 泛型参数表示目标对象的类型
* @return 解析后的对象实例
*/
default <T> T parse(byte[] json, Class<T> clazz) {
return parse(new String(json), clazz);
}
/**
* 将字节数组形式的JSON解析为指定泛型类型对象
*
* @param json 待解析的JSON字节数组
* @param type 目标对象的Type类型支持泛型
* @param <T> 泛型参数表示目标对象的类型
* @return 解析后的对象实例
*/
default <T> T parse(byte[] json, JsonTypeReference<T> type) {
return parse(new String(json), type);
}
/**
* 将JSON字符串解析为指定类型的对象解析失败时返回默认值
*
* @param json 待解析的JSON字符串
* @param clazz 目标对象的Class类型
* @param defaultValue 解析失败时返回的默认值
* @param <T> 泛型参数表示目标对象的类型
* @return 解析后的对象实例或默认值
*/
default <T> T parse(String json, Class<T> clazz, T defaultValue) {
try {
return parse(json, clazz);
} catch (Exception e) {
return defaultValue;
}
}
/**
* 将JSON字符串解析为指定泛型类型对象解析失败时返回默认值
*
* @param json 待解析的JSON字符串
* @param type 目标对象的Type类型支持泛型
* @param defaultValue 解析失败时返回的默认值
* @param <T> 泛型参数表示目标对象的类型
* @return 解析后的对象实例或默认值
*/
default <T> T parse(
String json,
JsonTypeReference<T> type,
T defaultValue
) {
try {
return parse(json, type);
} catch (Exception e) {
return defaultValue;
}
}
/**
* 将对象格式化为美化格式的JSON字符串带缩进和换行
*
* @param object 待格式化的对象
* @return 格式化后的美化JSON字符串
*/
String formatPretty(Object object);
default byte[] formatPrettyBytes(Object object) {
return formatPretty(object).getBytes();
}
String formatPrettyUnicode(Object object);
default byte[] formatPrettyUnicodeBytes(Object object) {
return formatPrettyUnicode(object).getBytes();
}
default void formatPretty(Object object, String file) throws IOException {
formatPretty(object, Paths.get(file));
}
default void formatPretty(Object object, Path file) throws IOException {
formatPretty(object, file.toFile());
}
default void formatPretty(Object object, File file) throws IOException {
try (FileOutputStream fos = new FileOutputStream(file)) {
formatPretty(object, fos);
}
}
default void formatPretty(Object object, OutputStream stream)
throws IOException {
stream.write(formatPrettyBytes(object));
}
default void formatPrettyUnicode(Object object, String file)
throws IOException {
formatPrettyUnicode(object, Paths.get(file));
}
default void formatPrettyUnicode(Object object, Path file)
throws IOException {
formatPrettyUnicode(object, file.toFile());
}
default void formatPrettyUnicode(Object object, File file)
throws IOException {
try (FileOutputStream fos = new FileOutputStream(file)) {
formatPrettyUnicode(object, fos);
}
}
default void formatPrettyUnicode(Object object, OutputStream stream)
throws IOException {
stream.write(formatPrettyUnicodeBytes(object));
}
default byte[] formatBytes(Object object) {
return format(object).getBytes();
}
default byte[] formatUnicodeBytes(Object object) {
return formatUnicode(object).getBytes();
}
default void format(Object object, String file) throws IOException {
format(object, Paths.get(file));
}
default void format(Object object, Path file) throws IOException {
format(object, file.toFile());
}
default void format(Object object, File file) throws IOException {
try (FileOutputStream fos = new FileOutputStream(file)) {
format(object, fos);
}
}
default void format(Object object, OutputStream stream) throws IOException {
stream.write(formatPrettyBytes(object));
}
default void formatUnicode(Object object, String file) throws IOException {
formatUnicode(object, Paths.get(file));
}
default void formatUnicode(Object object, Path file) throws IOException {
formatUnicode(object, file.toFile());
}
default void formatUnicode(Object object, File file) throws IOException {
try (FileOutputStream fos = new FileOutputStream(file)) {
formatUnicode(object, fos);
}
}
default void formatUnicode(Object object, OutputStream stream)
throws IOException {
stream.write(formatPrettyUnicodeBytes(object));
}
/**
* 将JSON字符串解析为指定元素类型的List集合
*
* @param json 待解析的JSON字符串
* @param elementType List中元素的类型
* @param <T> 泛型参数表示List中元素的类型
* @return 解析后的List集合
*/
default <T> List<T> parseList(String json, Class<T> elementType) {
return parse(json, JsonTypeUtils.listType(elementType));
}
/**
* 将JSON字符串解析为指定键值类型的Map集合
*
* @param json 待解析的JSON字符串
* @param keyType Map中键的类型
* @param valueType Map中值的类型
* @param <K> 泛型参数表示Map中键的类型
* @param <V> 泛型参数表示Map中值的类型
* @return 解析后的Map集合
*/
default <K, V> Map<K, V> parseMap(
String json,
Class<K> keyType,
Class<V> valueType
) {
JsonTypeReference<Map<K, V>> mapType = new JsonTypeReference<
Map<K, V>
>() {};
return parse(json, mapType);
}
/**
* 验证字符串是否为有效的JSON格式
*
* @param json 待验证的字符串
* @return 如果是有效的JSON格式返回true否则返回false
*/
boolean isValidJson(String json);
/**
* 将对象转换为JSON字节数组
*
* @param object 待转换的对象
* @return 转换后的JSON字节数组
*/
default byte[] toBytes(Object object) {
return format(object).getBytes();
}
/**
* 将对象转换为美化格式的JSON字节数组
*
* @param object 待转换的对象
* @return 转换后的美化格式JSON字节数组
*/
default byte[] toBytesPretty(Object object) {
return formatPretty(object).getBytes();
}
/**
* 合并多个JSON字符串为一个JSON对象
*
* @param jsons 待合并的JSON字符串数组
* @return 合并后的JSON字符串
*/
String merge(String... jsons);
/**
* 获取JSON字符串中指定路径节点的值
*
* @param json JSON字符串
* @param path 节点路径"user.name"
* @return 节点值的字符串表示
*/
String getNodeValue(String json, String path);
/**
* 更新JSON字符串中指定路径节点的值
*
* @param json 原始JSON字符串
* @param path 节点路径"user.name"
* @param newValue 新的节点值
* @return 更新后的JSON字符串
*/
String updateNodeValue(String json, String path, Object newValue);
<T, D> D convert(T source, Class<D> destinationClass);
<T, D> D convert(T source, JsonTypeReference<D> destinationType);
}

View File

@ -1,32 +0,0 @@
package com.mingliqiye.utils.json.converters;
import com.mingliqiye.utils.time.DateTime;
import com.mingliqiye.utils.time.Formatter;
public class DateTimeJsonConverter extends JsonStringConverter<DateTime> {
@Override
public Class<DateTime> getTClass() {
return DateTime.class;
}
@Override
public String convert(DateTime obj) {
if (obj == null) {
return null;
}
return obj.format(Formatter.STANDARD_DATETIME);
}
@Override
public DateTime deConvert(String string) {
if (string == null) {
return null;
}
return DateTime.parse(
string,
Formatter.STANDARD_DATETIME_MILLISECOUND7,
true
);
}
}

View File

@ -1,88 +0,0 @@
/*
* 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 FastjsonJsonStringConverterAdapter.java
* LastUpdate 2025-09-14 22:12:16
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.json.converters;
import com.alibaba.fastjson2.JSONReader;
import com.alibaba.fastjson2.JSONWriter;
import com.alibaba.fastjson2.reader.ObjectReader;
import com.alibaba.fastjson2.writer.ObjectWriter;
import java.lang.reflect.Type;
public class FastjsonJsonStringConverterAdapter<
T extends JsonStringConverter<TT>,
TT
> {
private final JsonStringConverter<TT> jsonStringConverter;
public FastjsonJsonStringConverterAdapter(T jsonStringConverter) {
this.jsonStringConverter = jsonStringConverter;
}
public static <
T extends JsonStringConverter<TT>,
TT
> FastjsonJsonStringConverterAdapter<T, TT> of(T t) {
return new FastjsonJsonStringConverterAdapter<>(t);
}
/**
* 获取FastJson对象写入器
*
* @return FastJson的ObjectWriter实例
*/
public ObjectWriter<T> getFastJsonObjectWriter() {
return (
JSONWriter writer,
Object object,
Object fieldName,
Type fieldType,
long features
) -> {
// 如果对象为null则写入null
if (object == null) {
writer.writeNull();
return;
}
writer.writeString(jsonStringConverter.convert((TT) object));
};
}
/**
* 获取FastJson对象读取器
*
* @return FastJson的ObjectReader实例
*/
public ObjectReader<TT> getFastJsonObjectReader() {
return (
JSONReader reader,
Type fieldType,
Object fieldName,
long features
) -> {
String value = reader.readString();
return jsonStringConverter.deConvert(value);
};
}
}

View File

@ -1,72 +0,0 @@
/*
* 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 GsonJsonStringConverterAdapter.java
* LastUpdate 2025-09-14 22:12:16
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.json.converters;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
public class GsonJsonStringConverterAdapter<
T extends JsonStringConverter<TT>,
TT
> {
private final JsonStringConverter<TT> jsonStringConverter;
public GsonJsonStringConverterAdapter(T jsonStringConverter) {
this.jsonStringConverter = jsonStringConverter;
}
public static <
T extends JsonStringConverter<TT>,
TT
> GsonJsonStringConverterAdapter<T, TT> of(T t) {
return new GsonJsonStringConverterAdapter<>(t);
}
/**
* 获取Gson类型适配器
*
* @return Gson的TypeAdapter实例
*/
public TypeAdapter<TT> getGsonTypeAdapter() {
return new TypeAdapter<TT>() {
@Override
public void write(JsonWriter out, TT value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
out.value(jsonStringConverter.convert(value));
}
@Override
public TT read(JsonReader in) throws IOException {
String value = in.nextString();
return jsonStringConverter.deConvert(value);
}
};
}
}

View File

@ -1,91 +0,0 @@
package com.mingliqiye.utils.json.converters;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.module.SimpleModule;
import java.io.IOException;
/**
* JSON转换器的适配器
* @param <T> JSON转换器
* @param <TT> JSON转换器的泛型
* @author MingLiPro
*/
public class JacksonJsonStringConverterAdapter<
T extends JsonStringConverter<TT>,
TT
> {
private final JsonStringConverter<TT> jsonStringConverter;
private JacksonJsonStringConverterAdapter(T jsonStringConverter) {
this.jsonStringConverter = jsonStringConverter;
}
/**
*
* @param t JSON转换器实例
* @return JSON转换器的适配器
* @param <T> JSON转换器
* @param <TT> JSON转换器的泛型
*/
public static <
T extends JsonStringConverter<TT>,
TT
> JacksonJsonStringConverterAdapter<T, TT> of(T t) {
return new JacksonJsonStringConverterAdapter<>(t);
}
/**
* 获取Jackson反序列化器
*
* @return Jackson的JsonDeserializer实例
*/
public JsonDeserializer<TT> getJacksonJsonDeserializer() {
return new JsonDeserializer<TT>() {
@Override
public TT deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException {
if (p.isNaN()) return null;
return jsonStringConverter.deConvert(p.getValueAsString());
}
};
}
/**
* 获取Jackson序列化器
*
* @return Jackson的JsonSerializer实例
*/
public JsonSerializer<TT> getJacksonJsonSerializer() {
return new JsonSerializer<TT>() {
@Override
public void serialize(
TT value,
JsonGenerator gen,
SerializerProvider serializers
) throws IOException {
if (value == null) {
gen.writeNull();
return;
}
gen.writeString(jsonStringConverter.convert(value));
}
};
}
/**
*
* 获取 Jackson 的格式化模块
*
* @return 格式化模块
*/
public Module getJacksonModule() {
Class<TT> tClass = jsonStringConverter.getTClass();
SimpleModule m = new SimpleModule(tClass.getSimpleName());
m.addSerializer(tClass, getJacksonJsonSerializer());
m.addDeserializer(tClass, getJacksonJsonDeserializer());
return m;
}
}

View File

@ -1,60 +0,0 @@
package com.mingliqiye.utils.json.converters;
/**
* JSON转换器接口提供对象与字符串之间的相互转换功能并支持多种JSON库
*
* @param <T> 需要转换的对象类型
*/
public abstract class JsonStringConverter<T> {
public abstract Class<T> getTClass();
/**
* 将对象转换为字符串
*
* @param obj 待转换的对象
* @return 转换后的字符串
*/
abstract String convert(T obj);
/**
* 将字符串转换为对象
*
* @param string 待转换的字符串
* @return 转换后的对象
*/
abstract T deConvert(String string);
/**
* 获取 Fastjson 的适配器
* @return 适配器实例
*/
public FastjsonJsonStringConverterAdapter<
JsonStringConverter<T>,
T
> getFastjsonJsonStringConverterAdapter() {
return FastjsonJsonStringConverterAdapter.of(this);
}
/**
* 获取 Gson 的适配器
* @return 适配器实例
*/
public GsonJsonStringConverterAdapter<
JsonStringConverter<T>,
T
> getGsonJsonStringConverterAdapter() {
return GsonJsonStringConverterAdapter.of(this);
}
/**
* 获取 Jackson 的适配器
* @return 适配器实例
*/
public JacksonJsonStringConverterAdapter<
JsonStringConverter<T>,
T
> getJacksonJsonStringConverterAdapter() {
return JacksonJsonStringConverterAdapter.of(this);
}
}

View File

@ -1,27 +0,0 @@
package com.mingliqiye.utils.json.converters;
import com.mingliqiye.utils.uuid.UUID;
public class UUIDJsonStringConverter extends JsonStringConverter<UUID> {
@Override
public Class<UUID> getTClass() {
return UUID.class;
}
@Override
public String convert(UUID obj) {
if (obj == null) {
return null;
}
return obj.toUUIDString();
}
@Override
public UUID deConvert(String string) {
if (string == null) {
return null;
}
return UUID.of(string);
}
}

View File

@ -1,561 +0,0 @@
/*
* 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:12:16
* 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 实例并支持与 DateLocalDateTime 等类型的互转
*<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());
}
}

View File

@ -1,65 +0,0 @@
/*
* 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 DateTimeOffset.java
* LastUpdate 2025-09-09 08:37:33
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.time;
import java.time.temporal.ChronoUnit;
import lombok.Getter;
/**
* 时间位移
*
* @author MingLiPro
*/
@Getter
public class DateTimeOffset {
private final ChronoUnit offsetType;
private final Long offset;
private DateTimeOffset(ChronoUnit offsetType, Long offset) {
this.offsetType = offsetType;
this.offset = offset;
}
/**
* 创建一个新的DateTimeOffset实例
*
* @param offsetType 偏移量的单位类型指定偏移量的计算单位
* @param offset 偏移量的数值可以为正数负数或零
* @return 返回一个新的DateTimeOffset对象包含指定的偏移量信息
*/
public static DateTimeOffset of(ChronoUnit offsetType, Long offset) {
return new DateTimeOffset(offsetType, offset);
}
/**
* 创建一个 DateTimeOffset 实例
*
* @param offset 偏移量数值
* @param offsetType 偏移量的时间单位类型
* @return 返回一个新的 DateTimeOffset 实例
*/
public static DateTimeOffset of(Long offset, ChronoUnit offsetType) {
return new DateTimeOffset(offsetType, offset);
}
}

View File

@ -1,65 +0,0 @@
/*
* 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 DateTimeUnit.java
* LastUpdate 2025-09-09 08:37:33
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.time;
/**
* 时间单位常量定义
*
* @author MingLiPro
*/
public interface DateTimeUnit {
// 时间单位常量
String YEAR = "year";
String MONTH = "month";
String WEEK = "week";
String DAY = "day";
String HOUR = "hour";
String MINUTE = "minute";
String SECOND = "second";
String MILLISECOND = "millisecond";
String MICROSECOND = "microsecond";
String NANOSECOND = "nanosecond";
// 时间单位缩写
String YEAR_ABBR = "y";
String MONTH_ABBR = "M";
String WEEK_ABBR = "w";
String DAY_ABBR = "d";
String HOUR_ABBR = "h";
String MINUTE_ABBR = "m";
String SECOND_ABBR = "s";
String MILLISECOND_ABBR = "ms";
String MICROSECOND_ABBR = "μs";
String NANOSECOND_ABBR = "ns";
// 时间单位转换系数毫秒为基准
long MILLIS_PER_SECOND = 1000L;
long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND;
long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE;
long MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR;
long MILLIS_PER_WEEK = 7 * MILLIS_PER_DAY;
// 月份和年的毫秒数仅为近似值
long MILLIS_PER_MONTH = 30 * MILLIS_PER_DAY; // 近似值
long MILLIS_PER_YEAR = 365 * MILLIS_PER_DAY; // 近似值
}

View File

@ -1,115 +0,0 @@
/*
* 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 Formatter.java
* LastUpdate 2025-09-09 08:37:33
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.time;
import lombok.Getter;
/**
* 时间格式化枚举类
* <p>
* 定义了常用的时间格式化模式用于日期时间的解析和格式化操作
* 每个枚举常量包含对应的格式化字符串和字符串长度
* </p>
*/
public enum Formatter {
/**
* 标准日期时间格式yyyy-MM-dd HH:mm:ss
*/
STANDARD_DATETIME("yyyy-MM-dd HH:mm:ss"),
/**
* 标准日期时间格式(7位毫秒)yyyy-MM-dd HH:mm:ss.SSSSSSS
*/
STANDARD_DATETIME_MILLISECOUND7("yyyy-MM-dd HH:mm:ss.SSSSSSS"),
/**
* 标准日期时间格式(6位毫秒)yyyy-MM-dd HH:mm:ss.SSSSSS
*/
STANDARD_DATETIME_MILLISECOUND6("yyyy-MM-dd HH:mm:ss.SSSSSS"),
/**
* 标准日期时间格式(5位毫秒)yyyy-MM-dd HH:mm:ss.SSSSS
*/
STANDARD_DATETIME_MILLISECOUND5("yyyy-MM-dd HH:mm:ss.SSSSS"),
/**
* 标准日期时间格式(4位毫秒)yyyy-MM-dd HH:mm:ss.SSSS
*/
STANDARD_DATETIME_MILLISECOUND4("yyyy-MM-dd HH:mm:ss.SSSS"),
/**
* 标准日期时间格式(3位毫秒)yyyy-MM-dd HH:mm:ss.SSS
*/
STANDARD_DATETIME_MILLISECOUND3("yyyy-MM-dd HH:mm:ss.SSS"),
/**
* 标准日期时间格式(2位毫秒)yyyy-MM-dd HH:mm:ss.SS
*/
STANDARD_DATETIME_MILLISECOUND2("yyyy-MM-dd HH:mm:ss.SS"),
/**
* 标准日期时间格式(1位毫秒)yyyy-MM-dd HH:mm:ss.S
*/
STANDARD_DATETIME_MILLISECOUND1("yyyy-MM-dd HH:mm:ss.S"),
/**
* 标准ISO格式yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
*/
STANDARD_ISO("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"),
/**
* 标准日期时间秒格式yyyy-MM-dd HH:mm:ss
*/
STANDARD_DATETIME_SECOUND("yyyy-MM-dd HH:mm:ss"),
/**
* 标准日期格式yyyy-MM-dd
*/
STANDARD_DATE("yyyy-MM-dd"),
/**
* ISO8601格式yyyy-MM-dd'T'HH:mm:ss.SSS'000'
*/
ISO8601("yyyy-MM-dd'T'HH:mm:ss.SSS'000'"),
/**
* 紧凑型日期时间格式yyyyMMddHHmmss
*/
COMPACT_DATETIME("yyyyMMddHHmmss");
@Getter
private final String value;
@Getter
private final int len;
/**
* 构造函数
*
* @param value 格式化模式字符串
*/
Formatter(String value) {
this.value = value;
this.len = value.length();
}
}

View File

@ -1,128 +0,0 @@
/*
* 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 DateTimeTypeHandler.java
* LastUpdate 2025-09-09 08:37:33
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.time.typehandlers;
import com.mingliqiye.utils.time.DateTime;
import com.mingliqiye.utils.time.Formatter;
import java.sql.*;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
/**
* DateTime类型处理器类
* 用于在MyBatis中处理DateTime类型与数据库VARCHAR类型之间的转换
*/
@MappedTypes({ DateTime.class })
@MappedJdbcTypes(JdbcType.VARCHAR)
public class DateTimeTypeHandler extends BaseTypeHandler<DateTime> {
/**
* 设置非空参数值
* 将DateTime对象转换为Timestamp并设置到PreparedStatement中
*
* @param ps PreparedStatement对象
* @param i 参数索引位置
* @param parameter DateTime参数值
* @param jdbcType JDBC类型
* @throws SQLException SQL异常
*/
@Override
public void setNonNullParameter(
PreparedStatement ps,
int i,
DateTime parameter,
JdbcType jdbcType
) throws SQLException {
ps.setTimestamp(i, Timestamp.valueOf(parameter.getLocalDateTime()));
}
/**
* 从ResultSet中获取可为空的结果值
* 根据列名获取字符串值并解析为DateTime对象
*
* @param rs ResultSet对象
* @param columnName 列名
* @return DateTime对象如果值为null则返回null
* @throws SQLException SQL异常
*/
@Override
public DateTime getNullableResult(ResultSet rs, String columnName)
throws SQLException {
return parse(rs.getString(columnName));
}
/**
* 从ResultSet中获取可为空的结果值
* 根据列索引获取字符串值并解析为DateTime对象
*
* @param rs ResultSet对象
* @param columnIndex 列索引
* @return DateTime对象如果值为null则返回null
* @throws SQLException SQL异常
*/
@Override
public DateTime getNullableResult(ResultSet rs, int columnIndex)
throws SQLException {
return parse(rs.getString(columnIndex));
}
/**
* 从CallableStatement中获取可为空的结果值
* 根据列索引获取字符串值并解析为DateTime对象
*
* @param cs CallableStatement对象
* @param columnIndex 列索引
* @return DateTime对象如果值为null则返回null
* @throws SQLException SQL异常
*/
@Override
public DateTime getNullableResult(CallableStatement cs, int columnIndex)
throws SQLException {
return parse(cs.getString(columnIndex));
}
/**
* 解析字符串为DateTime对象
*
* @param s 待解析的字符串
* @return DateTime对象如果字符串为null则返回null
*/
public DateTime parse(String s) {
if (s == null) {
return null;
}
return DateTime.parse(s, Formatter.STANDARD_DATETIME_MILLISECOUND7);
}
/**
* 格式化DateTime对象为字符串
*
* @param t DateTime对象
* @return 格式化后的字符串
*/
public String format(DateTime t) {
return t.format(Formatter.STANDARD_DATETIME_MILLISECOUND7);
}
}

View File

@ -16,15 +16,17 @@
* ProjectName mingli-utils * ProjectName mingli-utils
* ModuleName mingli-utils.main * ModuleName mingli-utils.main
* CurrentFile Main.kt * CurrentFile Main.kt
* LastUpdate 2025-09-15 08:56:35 * LastUpdate 2025-09-15 09:53:43
* UpdateUser MingLiPro * UpdateUser MingLiPro
*/ */
package com.mingliqiye.utils package com.mingliqiye.utils
import com.mingliqiye.utils.springboot.autoconfigure.AutoConfiguration import com.mingliqiye.utils.springboot.autoconfigure.AutoConfiguration
import com.mingliqiye.utils.time.DateTime
fun main() { fun main() {
AutoConfiguration.printBanner() AutoConfiguration.printBanner()
println(DateTime.now())
} }
fun test() { fun test() {

View File

@ -0,0 +1,181 @@
/*
* 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 Functions.kt
* LastUpdate 2025-09-15 09:56:54
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.functions
import java.util.concurrent.*
/**
* 防抖器类用于实现防抖功能防止在短时间内重复执行相同任务
*
* @author MingLiPro
*/
class Debouncer(private val delay: Long, unit: TimeUnit) {
private val scheduler: ScheduledExecutorService = Executors.newSingleThreadScheduledExecutor()
private val delayedMap: ConcurrentHashMap<Any, Future<*>> = ConcurrentHashMap()
private val delayMillis: Long = unit.toMillis(delay)
/**
* 执行防抖操作如果在指定延迟时间内再次调用相同key的任务则取消之前的任务并重新计时
*
* @param key 任务的唯一标识符用于区分不同任务
* @param task 要执行的任务
*/
fun debounce(key: Any, task: Runnable) {
// 提交新任务并获取之前可能存在的任务
val prev = delayedMap.put(
key,
scheduler.schedule(
{
try {
task.run()
} finally {
// 任务执行完成后从映射中移除
delayedMap.remove(key)
}
},
delayMillis,
TimeUnit.MILLISECONDS
)
)
// 如果之前存在任务,则取消它
if (prev != null) {
prev.cancel(true)
}
}
/**
* 关闭防抖器取消所有待执行的任务并关闭调度器
*/
fun shutdown() {
// 先取消所有延迟任务
for (future in delayedMap.values) {
future.cancel(true)
}
delayedMap.clear()
// 再关闭调度器
scheduler.shutdownNow()
}
}
@FunctionalInterface
fun interface P1Function<P> {
fun call(p: P)
}
@FunctionalInterface
fun interface P1RFunction<P, R> {
fun call(p: P): R
}
@FunctionalInterface
fun interface P2Function<P, P1> {
fun call(p: P, p1: P1)
}
@FunctionalInterface
fun interface P2RFunction<P, P1, R> {
fun call(p: P, p1: P1): R
}
@FunctionalInterface
fun interface P3Function<P, P1, P2> {
fun call(p: P, p1: P1, p2: P2)
}
@FunctionalInterface
fun interface P3RFunction<P, P1, P2, R> {
fun call(p: P, p1: P1, p2: P2): R
}
@FunctionalInterface
fun interface P4Function<P, P1, P2, P3> {
fun call(p: P, p1: P1, p2: P2, p3: P3)
}
@FunctionalInterface
fun interface P4RFunction<P, P1, P2, P3, R> {
fun call(p: P, p1: P1, p2: P2, p3: P3): R
}
@FunctionalInterface
fun interface P5Function<P, P1, P2, P3, P4> {
fun call(p: P, p1: P1, p2: P2, p3: P3, p4: P4)
}
@FunctionalInterface
fun interface P5RFunction<P, P1, P2, P3, P4, R> {
fun call(p: P, p1: P1, p2: P2, p3: P3, p4: P4): R
}
@FunctionalInterface
fun interface P6Function<P, P1, P2, P3, P4, P5> {
fun call(p: P, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5)
}
@FunctionalInterface
fun interface P6RFunction<P, P1, P2, P3, P4, P5, R> {
fun call(p: P, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5): R
}
@FunctionalInterface
fun interface P7Function<P, P1, P2, P3, P4, P5, P6> {
fun call(p: P, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6)
}
@FunctionalInterface
fun interface P7RFunction<P, P1, P2, P3, P4, P5, P6, R> {
fun call(p: P, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6): R
}
@FunctionalInterface
fun interface P8Function<P, P1, P2, P3, P4, P5, P6, P7> {
fun call(p: P, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7)
}
@FunctionalInterface
fun interface P8RFunction<P, P1, P2, P3, P4, P5, P6, P7, R> {
fun call(p: P, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7): R
}
@FunctionalInterface
fun interface P9Function<P, P1, P2, P3, P4, P5, P6, P7, P8> {
fun call(p: P, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8)
}
@FunctionalInterface
fun interface P9RFunction<P, P1, P2, P3, P4, P5, P6, P7, P8, R> {
fun call(p: P, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8): R
}
@FunctionalInterface
fun interface P10Function<P, P1, P2, P3, P4, P5, P6, P7, P8, P9> {
fun call(p: P, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9)
}
@FunctionalInterface
fun interface P10RFunction<P, P1, P2, P3, P4, P5, P6, P7, P8, P9, R> {
fun call(p: P, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9): R
}

View File

@ -0,0 +1,107 @@
/*
* 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 HashUtils.kt
* LastUpdate 2025-09-15 09:38:04
* UpdateUser MingLiPro
*/
@file:JvmName("HashUtils")
package com.mingliqiye.utils.hash
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.mindrot.jbcrypt.BCrypt
import java.io.File
import java.io.FileInputStream
import java.io.IOException
import java.security.MessageDigest
import java.security.NoSuchAlgorithmException
import java.security.Security
private val _addProvider = run {
Security.addProvider(BouncyCastleProvider())
}
/**
* 计算指定文件的哈希值
*
* @param file 要计算哈希值的文件对象
* @param algorithm 使用的哈希算法名称 SHA-256MD5
* @return 文件的十六进制格式哈希值字符串
* @throws IOException 当文件不存在或读取过程中发生 I/O 错误时抛出
* @throws NoSuchAlgorithmException 当指定的哈希算法不可用时抛出
*/
@Throws(IOException::class, NoSuchAlgorithmException::class)
fun calculateFileHash(file: File, algorithm: String): String {
// 检查文件是否存在
if (!file.exists()) {
throw IOException("File not found: " + file.absolutePath)
}
val digest = MessageDigest.getInstance(algorithm)
FileInputStream(file).use { fis ->
val buffer = ByteArray(8192)
var bytesRead: Int
// 分块读取文件内容并更新摘要
while (fis.read(buffer).also { bytesRead = it } != -1) {
digest.update(buffer, 0, bytesRead)
}
}
return bytesToHex(digest.digest())
}
/**
* 将字节数组转换为十六进制字符串表示
*
* @param bytes 输入的字节数组
* @return 对应的十六进制字符串
*/
private fun bytesToHex(bytes: ByteArray): String {
val hexString = StringBuilder(2 * bytes.size)
for (b in bytes) {
val hex = Integer.toHexString(0xff and b.toInt())
if (hex.length == 1) {
hexString.append('0')
}
hexString.append(hex)
}
return hexString.toString()
}
/**
* 使用 BCrypt 算法对字符串进行加密
*
* @param string 需要加密的明文字符串
* @return 加密后的 BCrypt 哈希字符串
*/
fun bcrypt(string: String): String {
return BCrypt.hashpw(string, BCrypt.gensalt())
}
/**
* 验证给定字符串与 BCrypt 哈希是否匹配
*
* @param string 明文字符串
* @param bcrypted 已经使用 BCrypt 加密的哈希字符串
* @return 如果匹配返回 true否则返回 false
*/
fun checkBcrypt(string: String, bcrypted: String): Boolean {
return BCrypt.checkpw(string, bcrypted)
}

View File

@ -0,0 +1,393 @@
/*
* 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 JsonApi.kt
* LastUpdate 2025-09-15 11:10:59
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.json
import com.mingliqiye.utils.json.converters.JsonConverter
import com.mingliqiye.utils.json.converters.JsonStringConverter
import java.io.*
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
/**
* JSON处理接口提供JSON字符串与Java对象之间的相互转换功能
*/
interface JsonApi {
/**
* 将JSON字符串解析为指定类型的对象
*
* @param json 待解析的JSON字符串
* @param clazz 目标对象的Class类型
* @param <T> 泛型参数表示目标对象的类型
* @return 解析后的对象实例
</T> */
fun <T> parse(json: String, clazz: Class<T>): T
/**
* 将JSON字符串解析为指定泛型类型对象
*
* @param json 待解析的JSON字符串
* @param type 目标对象的Type类型支持泛型
* @param <T> 泛型参数表示目标对象的类型
* @return 解析后的对象实例
</T> */
fun <T> parse(json: String, type: JsonTypeReference<T>): T
/**
* 将对象格式化为JSON字符串
*
* @param obj 待格式化的对象
* @return 格式化后的JSON字符串
*/
fun format(obj: Any): String
fun formatUnicode(obj: Any): String
@Throws(IOException::class)
fun <T> parseFrom(path: String, clazz: Class<T>): T {
return parseFrom(Paths.get(path), clazz)
}
@Throws(IOException::class)
fun <T> parseFrom(path: Path, clazz: Class<T>): T {
return parseFrom(path.toFile(), clazz)
}
@Throws(IOException::class)
fun <T> parseFrom(file: File, clazz: Class<T>): T {
Files.newInputStream(file.toPath()).use { inputStream ->
return parseFrom(inputStream, clazz)
}
}
@Throws(IOException::class)
fun <T> parseFrom(inputStream: InputStream, clazz: Class<T>): T {
val bytes = ByteArray(1024)
ByteArrayOutputStream().use { bos ->
var readlength: Int
while ((inputStream.read(bytes).also { readlength = it }) != -1) {
bos.write(bytes, 0, readlength)
}
return parse(bos.toByteArray(), clazz)
}
}
@Throws(IOException::class)
fun <T> parseFrom(path: String, type: JsonTypeReference<T>): T {
return parseFrom(Paths.get(path), type)
}
@Throws(IOException::class)
fun <T> parseFrom(path: Path, type: JsonTypeReference<T>): T {
return parseFrom(path.toFile(), type)
}
@Throws(IOException::class)
fun <T> parseFrom(file: File, type: JsonTypeReference<T>): T {
Files.newInputStream(file.toPath()).use { inputStream ->
return parseFrom<T>(inputStream, type)
}
}
@Throws(IOException::class)
fun <T> parseFrom(inputStream: InputStream, type: JsonTypeReference<T>): T {
val bytes = ByteArray(1024)
ByteArrayOutputStream().use { bos ->
var readlength: Int
while ((inputStream.read(bytes).also { readlength = it }) != -1) {
bos.write(bytes, 0, readlength)
}
return parse(bos.toByteArray(), type)
}
}
/**
* 将字节数组形式的JSON解析为指定类型的对象
*
* @param json 待解析的JSON字节数组
* @param clazz 目标对象的Class类型
* @param <T> 泛型参数表示目标对象的类型
* @return 解析后的对象实例
</T> */
fun <T> parse(json: ByteArray, clazz: Class<T>): T {
return parse(String(json), clazz)
}
/**
* 将字节数组形式的JSON解析为指定泛型类型对象
*
* @param json 待解析的JSON字节数组
* @param type 目标对象的Type类型支持泛型
* @param <T> 泛型参数表示目标对象的类型
* @return 解析后的对象实例
</T> */
fun <T> parse(json: ByteArray, type: JsonTypeReference<T>): T {
return parse(String(json), type)
}
/**
* 将JSON字符串解析为指定类型的对象解析失败时返回默认值
*
* @param json 待解析的JSON字符串
* @param clazz 目标对象的Class类型
* @param defaultValue 解析失败时返回的默认值
* @param <T> 泛型参数表示目标对象的类型
* @return 解析后的对象实例或默认值
</T> */
fun <T> parse(json: String, clazz: Class<T>, defaultValue: T): T {
try {
return parse(json, clazz)
} catch (e: Exception) {
return defaultValue
}
}
/**
* 将JSON字符串解析为指定泛型类型对象解析失败时返回默认值
*
* @param json 待解析的JSON字符串
* @param type 目标对象的Type类型支持泛型
* @param defaultValue 解析失败时返回的默认值
* @param <T> 泛型参数表示目标对象的类型
* @return 解析后的对象实例或默认值
**/
fun <T> parse(
json: String,
type: JsonTypeReference<T>,
defaultValue: T
): T {
try {
return parse<T>(json, type)
} catch (e: Exception) {
return defaultValue
}
}
/**
* 将对象格式化为美化格式的JSON字符串带缩进和换行
*
* @param object 待格式化的对象
* @return 格式化后的美化JSON字符串
*/
fun formatPretty(obj: Any): String
fun formatPrettyBytes(obj: Any): ByteArray {
return formatPretty(obj)!!.toByteArray()
}
fun formatPrettyUnicode(obj: Any): String
fun formatPrettyUnicodeBytes(obj: Any): ByteArray {
return formatPrettyUnicode(obj)!!.toByteArray()
}
@Throws(IOException::class)
fun formatPretty(obj: Any, file: String) {
formatPretty(obj, Paths.get(file))
}
@Throws(IOException::class)
fun formatPretty(obj: Any, file: Path) {
formatPretty(obj, file.toFile())
}
@Throws(IOException::class)
fun formatPretty(obj: Any, file: File) {
FileOutputStream(file).use { fos ->
formatPretty(obj, fos)
}
}
@Throws(IOException::class)
fun formatPretty(obj: Any, stream: OutputStream) {
stream.write(formatPrettyBytes(obj))
}
@Throws(IOException::class)
fun formatPrettyUnicode(obj: Any, file: String) {
formatPrettyUnicode(obj, Paths.get(file))
}
@Throws(IOException::class)
fun formatPrettyUnicode(obj: Any, file: Path) {
formatPrettyUnicode(obj, file.toFile())
}
@Throws(IOException::class)
fun formatPrettyUnicode(obj: Any, file: File) {
FileOutputStream(file).use { fos ->
formatPrettyUnicode(obj, fos)
}
}
@Throws(IOException::class)
fun formatPrettyUnicode(obj: Any, stream: OutputStream) {
stream.write(formatPrettyUnicodeBytes(obj))
}
fun formatBytes(obj: Any): ByteArray {
return format(obj)!!.toByteArray()
}
fun formatUnicodeBytes(obj: Any): ByteArray {
return formatUnicode(obj)!!.toByteArray()
}
@Throws(IOException::class)
fun format(obj: Any, file: String) {
format(obj, Paths.get(file))
}
@Throws(IOException::class)
fun format(obj: Any, file: Path) {
format(obj, file.toFile())
}
@Throws(IOException::class)
fun format(obj: Any, file: File) {
FileOutputStream(file).use { fos ->
format(obj, fos)
}
}
@Throws(IOException::class)
fun format(obj: Any, stream: OutputStream) {
stream.write(formatPrettyBytes(obj))
}
@Throws(IOException::class)
fun formatUnicode(obj: Any, file: String) {
formatUnicode(obj, Paths.get(file))
}
@Throws(IOException::class)
fun formatUnicode(obj: Any, file: Path) {
formatUnicode(obj, file.toFile())
}
@Throws(IOException::class)
fun formatUnicode(obj: Any, file: File) {
FileOutputStream(file).use { fos ->
formatUnicode(obj, fos)
}
}
@Throws(IOException::class)
fun formatUnicode(obj: Any, stream: OutputStream) {
stream.write(formatPrettyUnicodeBytes(obj))
}
/**
* 将JSON字符串解析为指定元素类型的List集合
*
* @param json 待解析的JSON字符串
* @param elementType List中元素的类型
* @param <T> 泛型参数表示List中元素的类型
* @return 解析后的List集合
</T> */
fun <T> parseList(json: String, elementType: Class<T>): MutableList<T> {
return parse<MutableList<T>>(json, JsonTypeUtils.listType<T>(elementType))
}
/**
* 将JSON字符串解析为指定键值类型的Map集合
*
* @param json 待解析的JSON字符串
* @param keyType Map中键的类型
* @param valueType Map中值的类型
* @param <K> 泛型参数表示Map中键的类型
* @param <V> 泛型参数表示Map中值的类型
* @return 解析后的Map集合
</V></K> */
fun <K, V> parseMap(
json: String,
keyType: Class<K>,
valueType: Class<V>
): MutableMap<K, V> {
val mapType: JsonTypeReference<MutableMap<K, V>> = object : JsonTypeReference<MutableMap<K, V>>() {}
return parse<MutableMap<K, V>>(json, mapType)
}
/**
* 验证字符串是否为有效的JSON格式
*
* @param json 待验证的字符串
* @return 如果是有效的JSON格式返回true否则返回false
*/
fun isValidJson(json: String): Boolean
/**
* 将对象转换为JSON字节数组
*
* @param object 待转换的对象
* @return 转换后的JSON字节数组
*/
fun toBytes(obj: Any): ByteArray {
return format(obj)!!.toByteArray()
}
/**
* 将对象转换为美化格式的JSON字节数组
*
* @param object 待转换的对象
* @return 转换后的美化格式JSON字节数组
*/
fun toBytesPretty(obj: Any): ByteArray {
return formatPretty(obj)!!.toByteArray()
}
/**
* 合并多个JSON字符串为一个JSON对象
*
* @param jsons 待合并的JSON字符串数组
* @return 合并后的JSON字符串
*/
fun merge(vararg jsons: String): String
/**
* 获取JSON字符串中指定路径节点的值
*
* @param json JSON字符串
* @param path 节点路径"user.name"
* @return 节点值的字符串表示
*/
fun getNodeValue(json: String, path: String): String
/**
* 更新JSON字符串中指定路径节点的值
*
* @param json 原始JSON字符串
* @param path 节点路径"user.name"
* @param newValue 新的节点值
* @return 更新后的JSON字符串
*/
fun updateNodeValue(json: String, path: String, newValue: Any): String
fun <T, D> convert(source: T, destinationClass: Class<D>): D
fun <T, D> convert(source: T, destinationType: JsonTypeReference<D>): D
fun addJsonConverter(c: JsonConverter<*, *>)
fun addJsonStringConverter(c: JsonStringConverter<*>)
}

View File

@ -15,14 +15,22 @@
* *
* ProjectName mingli-utils * ProjectName mingli-utils
* ModuleName mingli-utils.main * ModuleName mingli-utils.main
* CurrentFile P8RFunction.java * CurrentFile JsonConverter.kt
* LastUpdate 2025-09-09 08:37:34 * LastUpdate 2025-09-15 11:12:07
* UpdateUser MingLiPro * UpdateUser MingLiPro
*/ */
package com.mingliqiye.utils.functions; package com.mingliqiye.utils.json.converters
@FunctionalInterface interface JsonConverter<F, T> {
public interface P8RFunction<P, P1, P2, P3, P4, P5, P6, P7, R> { fun convert(obj: F?): T?
R call(P p, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7); fun deConvert(obj: T?): F?
val tClass: Class<F>
fun getStringConverter(): JsonStringConverter<F>? {
if (this is JsonStringConverter<*>) {
return this as JsonStringConverter<F>
}
return null
}
} }

View File

@ -0,0 +1,254 @@
/*
* 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 JsonStringConverter.kt
* LastUpdate 2025-09-15 11:03:53
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.json.converters
import com.alibaba.fastjson2.JSONReader
import com.alibaba.fastjson2.JSONWriter
import com.alibaba.fastjson2.reader.ObjectReader
import com.alibaba.fastjson2.writer.ObjectWriter
import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.databind.*
import com.fasterxml.jackson.databind.module.SimpleModule
import com.google.gson.TypeAdapter
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonWriter
import com.mingliqiye.utils.time.DateTime
import com.mingliqiye.utils.time.DateTime.Companion.parse
import com.mingliqiye.utils.time.Formatter
import com.mingliqiye.utils.uuid.UUID
import com.mingliqiye.utils.uuid.UUID.Companion.of
import java.io.IOException
import java.lang.reflect.Type
/**
* JSON转换器接口提供对象与字符串之间的相互转换功能并支持多种JSON库
*
* @param <T> 需要转换的对象类型
</T> */
abstract class JsonStringConverter<T> : JsonConverter<T, String> {
val fastjsonJsonStringConverterAdapter: FastjsonJsonStringConverterAdapter<JsonStringConverter<T>, T>
/**
* 获取 Fastjson 的适配器
* @return 适配器实例
*/
get() = FastjsonJsonStringConverterAdapter.of(this)
val gsonJsonStringConverterAdapter: GsonJsonStringConverterAdapter<JsonStringConverter<T>, T>
/**
* 获取 Gson 的适配器
* @return 适配器实例
*/
get() = GsonJsonStringConverterAdapter.of(this)
val jacksonJsonStringConverterAdapter: JacksonJsonStringConverterAdapter<JsonStringConverter<T>, T>
/**
* 获取 Jackson 的适配器
* @return 适配器实例
*/
get() = JacksonJsonStringConverterAdapter.of(this)
}
class JacksonJsonStringConverterAdapter<T : JsonStringConverter<TT>, TT
> private constructor(val jsonStringConverter: T) {
val jacksonJsonDeserializer: JsonDeserializer<TT?>
/**
* 获取Jackson反序列化器
*
* @return Jackson的JsonDeserializer实例
*/
get() = object : JsonDeserializer<TT?>() {
@Throws(IOException::class)
override fun deserialize(p: JsonParser, ctxt: DeserializationContext?): TT? {
if (p.isNaN) return null
return jsonStringConverter.deConvert(p.valueAsString)
}
}
val jacksonJsonSerializer: JsonSerializer<TT?>
/**
* 获取Jackson序列化器
*
* @return Jackson的JsonSerializer实例
*/
get() = object : JsonSerializer<TT?>() {
@Throws(IOException::class)
override fun serialize(
value: TT?,
gen: JsonGenerator,
serializers: SerializerProvider?
) {
if (value == null) {
gen.writeNull()
return
}
gen.writeString(jsonStringConverter.convert(value))
}
}
val jacksonModule: Module
/**
*
* 获取 Jackson 的格式化模块
*
* @return 格式化模块
*/
get() {
val tClass = jsonStringConverter.tClass
val m = SimpleModule(tClass.getSimpleName())
m.addSerializer<TT?>(tClass, this.jacksonJsonSerializer)
m.addDeserializer<TT?>(tClass, this.jacksonJsonDeserializer)
return m
}
companion object {
/**
*
* @param t JSON转换器实例
* @return JSON转换器的适配器
* @param <T> JSON转换器
* @param <TT> JSON转换器的泛型
**/
fun <T : JsonStringConverter<TT>, TT
> of(t: T): JacksonJsonStringConverterAdapter<T, TT> {
return JacksonJsonStringConverterAdapter(t)
}
}
}
class GsonJsonStringConverterAdapter<T : JsonStringConverter<TT>, TT
>(val jsonStringConverter: T) {
val gsonTypeAdapter: TypeAdapter<TT?>
/**
* 获取Gson类型适配器
*
* @return Gson的TypeAdapter实例
*/
get() = object : TypeAdapter<TT?>() {
@Throws(IOException::class)
override fun write(out: JsonWriter, value: TT?) {
if (value == null) {
out.nullValue()
return
}
out.value(jsonStringConverter.convert(value))
}
@Throws(IOException::class)
override fun read(`in`: JsonReader): TT? {
val value = `in`.nextString()
return jsonStringConverter.deConvert(value)
}
}
companion object {
fun <T : JsonStringConverter<TT>, TT
> of(t: T): GsonJsonStringConverterAdapter<T, TT> {
return GsonJsonStringConverterAdapter(t)
}
}
}
class FastjsonJsonStringConverterAdapter<T : JsonConverter<TT, String>, TT
>(val jsonStringConverter: T) {
@Suppress("UNCHECKED_CAST")
val fastJsonObjectWriter: ObjectWriter<T>
/**
* 获取FastJson对象写入器
*
* @return FastJson的ObjectWriter实例
*/
get() = ObjectWriter { writer: JSONWriter?, obj: Any?, _: Any?, _: Type?, _: Long
->
// 如果对象为null则写入null
if (obj == null) {
writer!!.writeNull()
return@ObjectWriter
}
writer!!.writeString(jsonStringConverter.convert(obj as TT))
}
val fastJsonObjectReader: ObjectReader<TT>
/**
* 获取FastJson对象读取器
*
* @return FastJson的ObjectReader实例
*/
get() = ObjectReader { reader: JSONReader?, _: Type?, _: Any?, _: Long
->
val value = reader!!.readString()
jsonStringConverter.deConvert(value)
}
companion object {
fun <T : JsonConverter<TT, String>, TT
> of(t: T): FastjsonJsonStringConverterAdapter<T, TT> {
return FastjsonJsonStringConverterAdapter(t)
}
}
}
class DateTimeJsonConverter : JsonStringConverter<DateTime>() {
override val tClass = DateTime::class.java
override fun convert(obj: DateTime?): String? {
if (obj == null) {
return null
}
return obj.format(Formatter.STANDARD_DATETIME)
}
override fun deConvert(obj: String?): DateTime? {
if (obj == null) {
return null
}
return parse(
obj,
Formatter.STANDARD_DATETIME_MILLISECOUND7,
true
)
}
}
class UUIDJsonStringConverter : JsonStringConverter<UUID>() {
override val tClass: Class<UUID> = UUID::class.java
override fun convert(obj: UUID?): String? {
if (obj == null) {
return null
}
return obj.getString()
}
override fun deConvert(obj: String?): UUID? {
if (obj == null) {
return null
}
return of(obj)
}
}

View File

@ -16,15 +16,13 @@
* ProjectName mingli-utils * ProjectName mingli-utils
* ModuleName mingli-utils.main * ModuleName mingli-utils.main
* CurrentFile RandomBytes.kt * CurrentFile RandomBytes.kt
* LastUpdate 2025-09-15 00:08:18 * LastUpdate 2025-09-15 09:54:33
* UpdateUser MingLiPro * UpdateUser MingLiPro
*/ */
@file:JvmName("RandomBytes") @file:JvmName("RandomBytes")
package com.mingliqiye.utils.random package com.mingliqiye.utils.random
import com.mingliqiye.utils.iterator.Range
/** /**
* 生成指定长度的随机字节数组 * 生成指定长度的随机字节数组
* @param length 数组长度 * @param length 数组长度
@ -32,7 +30,9 @@ import com.mingliqiye.utils.iterator.Range
*/ */
fun randomBytes(length: Int): ByteArray { fun randomBytes(length: Int): ByteArray {
val bytes = ByteArray(length) val bytes = ByteArray(length)
Range(0, length - 1).forEach { i: Int -> bytes[i] = randomByte(0x00.toByte(), 0xff.toByte()) } for (i in 0..<length) {
bytes[i] = randomByte(0x00.toByte(), 0xff.toByte())
}
return bytes return bytes
} }

View File

@ -16,7 +16,7 @@
* ProjectName mingli-utils * ProjectName mingli-utils
* ModuleName mingli-utils.main * ModuleName mingli-utils.main
* CurrentFile GsonAutoConfiguration.kt * CurrentFile GsonAutoConfiguration.kt
* LastUpdate 2025-09-14 22:06:47 * LastUpdate 2025-09-15 10:29:30
* UpdateUser MingLiPro * UpdateUser MingLiPro
*/ */
@ -58,16 +58,16 @@ open class GsonAutoConfiguration {
try { try {
return gsonBuilder return gsonBuilder
.registerTypeAdapter( .registerTypeAdapter(
uuidJsonStringConverter.getTClass(), uuidJsonStringConverter.tClass,
dateTimeJsonConverter uuidJsonStringConverter
.getGsonJsonStringConverterAdapter() .gsonJsonStringConverterAdapter
.getGsonTypeAdapter() .gsonTypeAdapter
) )
.registerTypeAdapter( .registerTypeAdapter(
dateTimeJsonConverter.getTClass(), dateTimeJsonConverter.tClass,
dateTimeJsonConverter dateTimeJsonConverter
.getGsonJsonStringConverterAdapter() .gsonJsonStringConverterAdapter
.getGsonTypeAdapter() .gsonTypeAdapter
) )
} finally { } finally {
log.info("MingliUtils GsonBuilder TypeAdapter add") log.info("MingliUtils GsonBuilder TypeAdapter add")

View File

@ -16,7 +16,7 @@
* ProjectName mingli-utils * ProjectName mingli-utils
* ModuleName mingli-utils.main * ModuleName mingli-utils.main
* CurrentFile JacksonAutoConfiguration.kt * CurrentFile JacksonAutoConfiguration.kt
* LastUpdate 2025-09-14 22:10:08 * LastUpdate 2025-09-15 10:29:02
* UpdateUser MingLiPro * UpdateUser MingLiPro
*/ */
@ -49,12 +49,12 @@ open class JacksonAutoConfiguration(objectMapper: ObjectMapper) {
.registerModule( .registerModule(
DateTimeJsonConverter() DateTimeJsonConverter()
.jacksonJsonStringConverterAdapter .jacksonJsonStringConverterAdapter
.getJacksonModule() .jacksonModule
) )
.registerModule( .registerModule(
UUIDJsonStringConverter() UUIDJsonStringConverter()
.jacksonJsonStringConverterAdapter .jacksonJsonStringConverterAdapter
.getJacksonModule() .jacksonModule
) )
} }
} }

View File

@ -16,7 +16,7 @@
* ProjectName mingli-utils * ProjectName mingli-utils
* ModuleName mingli-utils.main * ModuleName mingli-utils.main
* CurrentFile SystemUtil.kt * CurrentFile SystemUtil.kt
* LastUpdate 2025-09-15 08:50:23 * LastUpdate 2025-09-15 11:18:34
* UpdateUser MingLiPro * UpdateUser MingLiPro
*/ */
@file:JvmName("SystemUtils") @file:JvmName("SystemUtils")
@ -66,7 +66,7 @@ fun isUnix(): Boolean {
* *
* @return JDK版本号字符串 * @return JDK版本号字符串
*/ */
fun getJdkVersion(): String { fun getJdkVersion(): String? {
return System.getProperty("java.specification.version") return System.getProperty("java.specification.version")
} }

View File

@ -0,0 +1,678 @@
/*
* 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.kt
* LastUpdate 2025-09-15 09:57:50
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.time
import com.mingliqiye.utils.jna.FILETIME_EPOCH_OFFSET
import com.mingliqiye.utils.jna.NANOS_PER_100NS
import com.mingliqiye.utils.jna.WinKernel32Api
import com.mingliqiye.utils.jna.getWinKernel32Apis
import com.mingliqiye.utils.logger.mingLiLoggerFactory
import com.mingliqiye.utils.system.getJavaVersionAsInteger
import com.mingliqiye.utils.system.isWindows
import org.slf4j.Logger
import java.io.Serializable
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.format.DateTimeFormatter
import java.time.temporal.ChronoUnit
import java.util.*
import kotlin.time.ExperimentalTime
import kotlin.time.Instant
/**
* 时间位移
*
* @author MingLiPro
*/
class DateTimeOffset private constructor(
val offsetType: ChronoUnit,
val offset: Long
) {
companion object {
/**
* 创建一个新的DateTimeOffset实例
*
* @param offsetType 偏移量的单位类型指定偏移量的计算单位
* @param offset 偏移量的数值可以为正数负数或零
* @return 返回一个新的DateTimeOffset对象包含指定的偏移量信息
*/
@JvmStatic
fun of(offsetType: ChronoUnit, offset: Long): DateTimeOffset {
return DateTimeOffset(offsetType, offset)
}
/**
* 创建一个 DateTimeOffset 实例
*
* @param offset 偏移量数值
* @param offsetType 偏移量的时间单位类型
* @return 返回一个新的 DateTimeOffset 实例
*/
@JvmStatic
fun of(offset: Long, offsetType: ChronoUnit): DateTimeOffset {
return DateTimeOffset(offsetType, offset)
}
}
}
/**
* 时间格式化枚举类
*
*
* 定义了常用的时间格式化模式用于日期时间的解析和格式化操作
* 每个枚举常量包含对应的格式化字符串和字符串长度
*
*/
enum class Formatter(private val value: String) {
/**
* 标准日期时间格式yyyy-MM-dd HH:mm:ss
*/
STANDARD_DATETIME("yyyy-MM-dd HH:mm:ss"),
/**
* 标准日期时间格式(7位毫秒)yyyy-MM-dd HH:mm:ss.SSSSSSS
*/
STANDARD_DATETIME_MILLISECOUND7("yyyy-MM-dd HH:mm:ss.SSSSSSS"),
/**
* 标准日期时间格式(6位毫秒)yyyy-MM-dd HH:mm:ss.SSSSSS
*/
STANDARD_DATETIME_MILLISECOUND6("yyyy-MM-dd HH:mm:ss.SSSSSS"),
/**
* 标准日期时间格式(5位毫秒)yyyy-MM-dd HH:mm:ss.SSSSS
*/
STANDARD_DATETIME_MILLISECOUND5("yyyy-MM-dd HH:mm:ss.SSSSS"),
/**
* 标准日期时间格式(4位毫秒)yyyy-MM-dd HH:mm:ss.SSSS
*/
STANDARD_DATETIME_MILLISECOUND4("yyyy-MM-dd HH:mm:ss.SSSS"),
/**
* 标准日期时间格式(3位毫秒)yyyy-MM-dd HH:mm:ss.SSS
*/
STANDARD_DATETIME_MILLISECOUND3("yyyy-MM-dd HH:mm:ss.SSS"),
/**
* 标准日期时间格式(2位毫秒)yyyy-MM-dd HH:mm:ss.SS
*/
STANDARD_DATETIME_MILLISECOUND2("yyyy-MM-dd HH:mm:ss.SS"),
/**
* 标准日期时间格式(1位毫秒)yyyy-MM-dd HH:mm:ss.S
*/
STANDARD_DATETIME_MILLISECOUND1("yyyy-MM-dd HH:mm:ss.S"),
/**
* 标准ISO格式yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
*/
STANDARD_ISO("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"),
/**
* 标准日期时间秒格式yyyy-MM-dd HH:mm:ss
*/
STANDARD_DATETIME_SECOUND("yyyy-MM-dd HH:mm:ss"),
/**
* 标准日期格式yyyy-MM-dd
*/
STANDARD_DATE("yyyy-MM-dd"),
/**
* ISO8601格式yyyy-MM-dd'T'HH:mm:ss.SSS'000'
*/
ISO8601("yyyy-MM-dd'T'HH:mm:ss.SSS'000'"),
/**
* 紧凑型日期时间格式yyyyMMddHHmmss
*/
COMPACT_DATETIME("yyyyMMddHHmmss");
private val len: Int = value.length
fun getLen(): Int {
return this.len
}
fun getValue(): String {
return this.value
}
}
/**
* 时间类用于处理日期时间的转换格式化等操作
* 提供了多种静态方法来创建 DateTime 实例并支持与 DateLocalDateTime 等类型的互转
*<br>
* windows java 1.8 及以下 使用windows Api 获取高精度时间
*
* @author MingLiPro
* @see java.time
* @see LocalDateTime
* @see ChronoUnit
* @see Date
* @see DateTimeFormatter
* @see ZoneId
* @see Instant
*/
class DateTime private constructor(
private var localDateTime: LocalDateTime,
private val zoneId: ZoneId = ZoneId.systemDefault()
) : Serializable {
companion object {
private val WIN_KERNEL_32_API: WinKernel32Api? = if (
getJavaVersionAsInteger() == 8 &&
isWindows()
) {
val log: Logger = mingLiLoggerFactory.getLogger("mingli-utils DateTime")
val a = getWinKernel32Apis()
if (a.size > 1) {
log.warn("Multiple Size:{} WinKernel32Api implementations found.", a.size)
a.forEach { api ->
log.warn("Found WinKernel32Api: {}", api.javaClass.name)
}
}
if (a.isEmpty()) {
log.warn("No WinKernel32Api implementation found. Use Jdk1.8 LocalDateTime")
null
} else {
log.info("Found and Use WinKernel32Api: {}", a[a.size - 1].javaClass.name)
a[a.size - 1]
}
} else {
null
}
/**
* 获取当前时间的 DateTime 实例
* 如果运行在 Java 1.8 环境下则通过 WinKernel32 获取高精度时间
*
* @return 返回当前时间的 DateTime 实例
*/
@JvmStatic
fun now(): DateTime {
if (WIN_KERNEL_32_API != null) {
return DateTime(
WIN_KERNEL_32_API.getTime()
.atZone(ZoneId.systemDefault())
.toLocalDateTime()
)
}
return DateTime(LocalDateTime.now())
}
/**
* Date 对象转换为 DateTime 实例
*
* @param zoneId 时区信息
* @param date Date 对象
* @return 返回对应的 DateTime 实例
*/
@JvmStatic
fun of(date: Date, zoneId: ZoneId): DateTime {
return DateTime(date.toInstant().atZone(zoneId).toLocalDateTime(), zoneId)
}
/**
* Date 对象转换为 DateTime 实例使用系统默认时区
*
* @param date Date 对象
* @return 返回对应的 DateTime 实例
*/
@JvmStatic
fun of(date: Date): DateTime {
return DateTime(
date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime()
)
}
/**
* 根据 LocalDateTime 创建 DateTime 实例
*
* @param localDateTime LocalDateTime 对象
* @return 返回对应的 DateTime 实例
*/
@JvmStatic
fun of(localDateTime: LocalDateTime): DateTime {
return DateTime(localDateTime)
}
/**
* 解析时间字符串并生成 DateTime 实例
*
* @param timestr 时间字符串
* @param formatter 格式化模板
* @param fillZero 是否补零到模板长度
* @return 返回解析后的 DateTime 实例
*/
@JvmStatic
fun parse(
timestr: String,
formatter: String,
fillZero: Boolean
): DateTime {
return DateTime(
LocalDateTime.parse(
if (fillZero) getFillZeroByLen(timestr, formatter) else timestr,
DateTimeFormatter.ofPattern(formatter)
)
)
}
/**
* 使用 Formatter 枚举解析时间字符串并生成 DateTime 实例
*
* @param timestr 时间字符串
* @param formatter 格式化模板枚举
* @param fillZero 是否补零到模板长度
* @return 返回解析后的 DateTime 实例
*/
@JvmStatic
fun parse(
timestr: String,
formatter: Formatter,
fillZero: Boolean
): DateTime {
return parse(timestr, formatter.getValue(), fillZero)
}
/**
* 使用 Formatter 枚举解析时间字符串并生成 DateTime 实例默认不补零
*
* @param timestr 时间字符串
* @param formatter 格式化模板枚举
* @return 返回解析后的 DateTime 实例
*/
@JvmStatic
fun parse(timestr: String, formatter: Formatter): DateTime {
return parse(timestr, formatter.getValue())
}
/**
* 解析时间字符串并生成 DateTime 实例默认不补零
*
* @param timestr 时间字符串
* @param formatter 格式化模板
* @return 返回解析后的 DateTime 实例
*/
@JvmStatic
fun parse(timestr: String, formatter: String): DateTime {
return parse(timestr, formatter, false)
}
/**
* 补零处理时间字符串以匹配格式化模板长度
*
* @param dstr 原始时间字符串
* @param formats 格式化模板
* @return 补零后的时间字符串
*/
private fun getFillZeroByLen(dstr: String, formats: String): String {
if (dstr.length == formats.length) {
return dstr
}
if (formats.length > dstr.length) {
var modifiedDstr = dstr
if (dstr.length == 19) {
modifiedDstr += "."
}
val sb = StringBuilder(modifiedDstr)
for (i in 0 until formats.length - dstr.length) {
sb.append("0")
}
return sb.toString()
}
throw 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
*/
@JvmStatic
fun of(year: Int, month: Int, day: Int): DateTime {
return 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
*/
@JvmStatic
fun of(
year: Int,
month: Int,
day: Int,
hour: Int,
minute: Int
): DateTime {
return DateTime(LocalDateTime.of(year, month, day, hour, minute))
}
/**
* FILETIME 转换为 LocalDateTime
*
* @param fileTime FILETIME 时间戳100纳秒单位自1601年1月1日起
* @return 转换后的 LocalDateTime 实例
*/
@OptIn(ExperimentalTime::class)
@JvmStatic
fun fileTimeToLocalDateTime(fileTime: Long): LocalDateTime {
// 1. 将 FILETIME (100ns间隔 since 1601) 转换为 Unix 时间戳 (纳秒 since 1970)
val unixNanos = (fileTime + FILETIME_EPOCH_OFFSET) * NANOS_PER_100NS
// 2. 从纳秒时间戳创建 Instant
val instant = java.time.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 实例
*/
@JvmStatic
fun of(
year: Int,
month: Int,
day: Int,
hour: Int,
minute: Int,
second: Int
): DateTime {
return 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 实例
*/
@JvmStatic
fun of(
year: Int,
month: Int,
day: Int,
hour: Int,
minute: Int,
second: Int,
nano: Int
): DateTime {
return DateTime(
LocalDateTime.of(year, month, day, hour, minute, second, nano)
)
}
/**
* 根据毫秒时间戳创建 DateTime 实例
*
* @param epochMilli 毫秒时间戳
* @return 返回对应时间的 DateTime 实例
*/
@JvmStatic
fun of(epochMilli: Long): DateTime {
return DateTime(
java.time.Instant.ofEpochMilli(epochMilli)
.atZone(ZoneId.systemDefault())
.toLocalDateTime()
)
}
/**
* 根据毫秒时间戳和时区创建 DateTime 实例
*
* @param epochMilli 毫秒时间戳
* @param zoneId 时区信息
* @return 返回对应时间的 DateTime 实例
*/
@JvmStatic
fun of(epochMilli: Long, zoneId: ZoneId): DateTime {
return DateTime(
java.time.Instant.ofEpochMilli(epochMilli).atZone(zoneId).toLocalDateTime(),
zoneId
)
}
}
/**
* 将当前 DateTime 转换为 Date 对象
*
* @return 返回对应的 Date 对象
*/
fun toDate(): Date {
return Date.from(localDateTime.atZone(zoneId).toInstant())
}
/**
* 获取当前 DateTime 中的 LocalDateTime 实例
*
* @return 返回 LocalDateTime 对象
*/
fun toLocalDateTime(): LocalDateTime {
return localDateTime
}
/**
* 在当前时间基础上增加指定的时间偏移量
*
* @param dateTimeOffset 时间偏移对象
* @return 返回修改后的 DateTime 实例
*/
fun add(dateTimeOffset: DateTimeOffset): DateTime {
return DateTime(
this.localDateTime.plus(
dateTimeOffset.offset,
dateTimeOffset.offsetType
)
)
}
/**
* 在当前时间基础上减少指定的时间偏移量
*
* @param dateTimeOffset 时间偏移对象
* @return 返回修改后的 DateTime 实例
*/
fun sub(dateTimeOffset: DateTimeOffset): DateTime {
return DateTime(
this.localDateTime.plus(
-dateTimeOffset.offset,
dateTimeOffset.offsetType
)
)
}
/**
* 使用指定格式化模板将当前时间格式化为字符串
*
* @param formatter 格式化模板
* @return 返回格式化后的时间字符串
*/
fun format(formatter: String): String {
return format(formatter, false)
}
/**
* 使用 Formatter 枚举将当前时间格式化为字符串
*
* @param formatter 格式化模板枚举
* @return 返回格式化后的时间字符串
*/
fun format(formatter: Formatter): String {
return format(formatter.getValue())
}
/**
* 使用指定格式化模板将当前时间格式化为字符串并可选择是否去除末尾多余的零
*
* @param formatter 格式化模板
* @param repcZero 是否去除末尾多余的零
* @return 返回格式化后的时间字符串
*/
fun format(formatter: String, repcZero: Boolean): String {
var formatted = DateTimeFormatter.ofPattern(formatter).format(
toLocalDateTime()
)
if (repcZero) {
// 处理小数点后多余的0
formatted = formatted.replace(Regex("(\\.\\d*?)0+\\b"), "$1")
formatted = formatted.replace(Regex("\\.$"), "")
}
return formatted
}
/**
* 使用 Formatter 枚举将当前时间格式化为字符串并可选择是否去除末尾多余的零
*
* @param formatter 格式化模板枚举
* @param repcZero 是否去除末尾多余的零
* @return 返回格式化后的时间字符串
*/
fun format(formatter: Formatter, repcZero: Boolean): String {
return format(formatter.getValue(), repcZero)
}
/**
* 返回当前时间的标准字符串表示形式
*
* @return 返回标准格式的时间字符串
*/
override fun toString(): String {
return String.format(
"DateTime(%s)",
format(Formatter.STANDARD_DATETIME_MILLISECOUND7, true)
)
}
/**
* 比较当前DateTime对象与指定对象是否相等
*
* @param other 要比较的对象
* @return 如果对象相等则返回true否则返回false
*/
override fun equals(other: Any?): Boolean {
// 检查对象类型是否为DateTime
if (other is DateTime) {
// 比较两个DateTime对象转换为LocalDateTime后的值
return toLocalDateTime() == other.toLocalDateTime()
}
return false
}
/**
* 获取对象的哈希码
*
* @return 哈希码
*/
override fun hashCode(): Int {
return localDateTime.hashCode()
}
/**
* 将当前 DateTime 转换为 Instant 对象
*
* @return 返回 Instant 对象
*/
fun toInstant(): java.time.Instant {
return localDateTime.atZone(zoneId).toInstant()
}
/**
* 判断当前时间是否在指定时间之后
*
* @param dateTime 指定时间
* @return 如果当前时间在指定时间之后则返回 true否则返回 false
*/
fun isAfter(dateTime: DateTime?): Boolean {
if (dateTime == null) {
return false
}
return toInstant().isAfter(dateTime.toInstant())
}
/**
* 判断当前时间是否在指定时间之前
*
* @param dateTime 指定时间
* @return 如果当前时间在指定时间之前则返回 true否则返回 false
*/
fun isBefore(dateTime: DateTime?): Boolean {
if (dateTime == null) {
return false
}
return toInstant().isBefore(dateTime.toInstant())
}
/**
* 获取时区ID
*
* @return ZoneId对象
*/
fun getZoneId(): ZoneId {
return zoneId
}
}