Initial project

This commit is contained in:
Timi
2025-07-12 12:45:46 +08:00
parent a87693cf37
commit abf09cee04
20 changed files with 2769 additions and 94 deletions

View File

@ -0,0 +1,250 @@
package com.imyeyu.utils;
import com.imyeyu.java.bean.CallbackArgReturn;
import com.imyeyu.java.bean.timi.TimiCode;
import com.imyeyu.java.bean.timi.TimiException;
import com.imyeyu.java.ref.Ref;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author 夜雨
* @since 2024-12-18 10:10
*/
public class AsciiTable<T> {
/** 制表符水平边框 */
private static final String BORDER_H = "";
/** 制表符垂直边框 */
private static final String BORDER_V = "";
/** 制表符左上边框 */
private static final String BORDER_TL = "";
/** 制表符上边框 */
private static final String BORDER_T = "";
/** 制表符右上边框 */
private static final String BORDER_TR = "";
/** 制表符左边框 */
private static final String BORDER_L = "";
/** 制表符十字边框 */
private static final String BORDER_P = "";
/** 制表符右边框 */
private static final String BORDER_R = "";
/** 制表符左下边框 */
private static final String BORDER_BL = "";
/** 制表符下边框 */
private static final String BORDER_B = "";
/** 制表符右下边框 */
private static final String BORDER_BR = "";
private final List<ColMapping<T>> colMappingList = new ArrayList<>();
private final List<Row> rowList = new ArrayList<>();
public String render(List<T> dataList) {
try {
{
// 表头
Row header = new Row();
for (int i = 0; i < colMappingList.size(); i++) {
Row.Col col = new Row.Col();
col.text = colMappingList.get(i).name;
header.colList.add(col);
}
rowList.add(header);
}
{
// 数据
for (int i = 0; i < dataList.size(); i++) {
Row row = new Row();
for (int j = 0; j < colMappingList.size(); j++) {
Row.Col col = new Row.Col();
Object objValue;
ColMapping<T> tColMapping = colMappingList.get(j);
if (tColMapping.itemCallback == null) {
objValue = Ref.getFieldValue(dataList.get(i), tColMapping.field, Object.class);
} else {
objValue = tColMapping.itemCallback.handler(dataList.get(i));
}
if (tColMapping.valueCallback != null) {
objValue = tColMapping.valueCallback.handler(objValue.toString());
}
col.text = objValue.toString();
row.colList.add(col);
}
rowList.add(row);
}
}
// 对齐渲染
Map<Integer, Integer> colMaxSize = new HashMap<>();
for (int i = 0; i < rowList.size(); i++) {
List<Row.Col> colList = rowList.get(i).colList;
for (int j = 0; j < colList.size(); j++) {
String colText = colList.get(j).text;
if (!colMaxSize.containsKey(j)) {
colMaxSize.put(j, colText.length());
}
colMaxSize.put(j, Math.max(colMaxSize.get(j), colText.length()));
}
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < rowList.size(); i++) {
List<Row.Col> colList = rowList.get(i).colList;
if (i == 0) {
// 顶边
sb.append(BORDER_TL);
for (int j = 0; j < colList.size(); j++) {
sb.append(BORDER_H.repeat(colMaxSize.get(j)));
if (j == colList.size() - 1) {
sb.append(BORDER_TR);
} else {
sb.append(BORDER_T);
}
}
sb.append('\n');
}
// 数据
for (int j = 0; j < colList.size(); j++) {
String text = colList.get(j).text;
if (j == 0) {
sb.append(BORDER_V);
}
sb.append(Text.paddedSpaceEnd(text, colMaxSize.get(j))).append(BORDER_V);
}
sb.append('\n');
if (i != rowList.size() - 1) {
// 数据分割
for (int j = 0; j < colList.size(); j++) {
if (j == 0) {
sb.append(BORDER_L);
}
sb.append(BORDER_H.repeat(colMaxSize.get(j)));
if (j == colList.size() - 1) {
sb.append(BORDER_R);
} else {
sb.append(BORDER_P);
}
}
sb.append('\n');
} else {
// 底边
sb.append(BORDER_BL);
for (int j = 0; j < colList.size(); j++) {
sb.append(BORDER_H.repeat(colMaxSize.get(j)));
if (j == colList.size() - 1) {
sb.append(BORDER_BR);
} else {
sb.append(BORDER_B);
}
}
}
}
return sb.toString();
} catch (IllegalAccessException e) {
throw new TimiException(TimiCode.ERROR, e.getMessage(), e);
}
}
public void addHeader(String field) {
addHeader(field.substring(0, 1).toUpperCase() + field.substring(1), field);
}
public void addHeader(String name, String field) {
TimiException.required(field, "not found field");
ColMapping<T> colMapping = new ColMapping<>();
colMapping.name = name;
colMapping.field = field;
colMappingList.add(colMapping);
}
public void addHeader(ColMapping<T> colMapping) {
colMappingList.add(colMapping);
}
/**
*
*
* @author 夜雨
* @since 2024-12-18 10:16
*/
public static class ColMapping<T> extends Row.Col {
private String name;
private String field;
private CallbackArgReturn<T, String> itemCallback;
private CallbackArgReturn<String, String> valueCallback;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getField() {
return field;
}
public void setField(String field) {
this.field = field;
}
public CallbackArgReturn<T, String> getItemCallback() {
return itemCallback;
}
public void setItemCallback(CallbackArgReturn<T, String> itemCallback) {
this.itemCallback = itemCallback;
}
public CallbackArgReturn<String, String> getValueCallback() {
return valueCallback;
}
public void setValueCallback(CallbackArgReturn<String, String> valueCallback) {
this.valueCallback = valueCallback;
}
}
/**
*
*
* @author 夜雨
* @since 2024-12-18 10:10
*/
private static class Row {
List<Col> colList = new ArrayList<>();
/**
*
*
* @author 夜雨
* @since 2024-12-18 10:10
*/
private static class Col {
String text;
}
}
}

View File

@ -0,0 +1,204 @@
package com.imyeyu.utils;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.security.SecureRandom;
import java.util.Random;
/**
* 数学计算扩展
*
* @author 夜雨
* @version 2023-03-20 10:28
*/
public class Calc {
private static final SecureRandom RANDOM = new SecureRandom();
/**
* 是否为数字
*
* @param data 字符串
* @return 为 true 是表示是数字
*/
public static boolean isNumber(String data) {
try {
Double.parseDouble(data);
return true;
} catch (Exception e) {
return false;
}
}
/**
* 向上取整返回整型
*
* @param v 数值
* @return 取整结果
*/
public static int ceil(double v) {
return (int) Math.ceil(v);
}
/**
* 向下取整返回整型
*
* @param v 数值
* @return 取整结果
*/
public static int floor(double v) {
return (int) Math.floor(v);
}
/**
* 四舍五入返回整型
*
* @param v 数值
* @return 结果
*/
public static int round(double v) {
return (int) Math.round(v);
}
public static double round(double v, int scale) {
return round(v, scale, RoundingMode.DOWN);
}
public static double round(double v, int scale, RoundingMode mode) {
BigDecimal bd = new BigDecimal(Double.toString(v));
bd = bd.setScale(scale, mode);
return bd.doubleValue();
}
/**
* 四舍五入返回长整型
*
* @param v 数值
* @return 结果
*/
public static long roundLong(double v) {
return Math.round(v);
}
/**
* 范围内取随机值 [min, max]
*
* @param min 最小值
* @param max 最大值
* @return 随机值
*/
public static int random(int min, int max) {
if (max < min) {
throw new IllegalArgumentException("min must less than max");
}
return RANDOM.nextInt(max + 1 - min) + min;
}
/**
* 范围内取随机值 [min, max]
*
* @param min 最小值
* @param max 最大值
* @return 随机值
*/
public static long random(long min, long max) {
if (max < min) {
throw new IllegalArgumentException("min must less than max");
}
return RANDOM.nextLong(max + 1 - min) + min;
}
/**
* 范围内取随机值 (min, max)
*
* @param min 最小值
* @param max 最大值
* @return 随机值
*/
public static float random(float min, float max) {
if (max < min) {
throw new IllegalArgumentException("min must less than max");
}
return RANDOM.nextFloat(max - min) + min;
}
/**
* 范围内取随机值 (min, max)
*
* @param min 最小值
* @param max 最大值
* @return 随机值
*/
public static double random(double min, double max) {
if (max < min) {
throw new IllegalArgumentException("min must less than max");
}
return RANDOM.nextDouble(max - min) + min;
}
public static boolean randomBoolean() {
return random(0, 1) == 1;
}
/**
* 计算两数差值
*
* @param v0 第一个数
* @param v1 第二个数
* @return 差值
* @param <T> 类型
*/
@SuppressWarnings("unchecked")
public static <T extends Number> T between(T v0, T v1) {
if (v0 instanceof BigDecimal bd0 && v1 instanceof BigDecimal bd1) {
return (T) (Double) bd0.subtract(bd1).abs().doubleValue();
}
return (T) (Double) Math.abs(v0.doubleValue() - v1.doubleValue());
}
/**
* 计算一个数字是否在区间内
*
* @param min 区间最小值
* @param max 区间最大值
* @param number 判定值
* @return true 时number 在 min 和 max 之间
*/
public static boolean in(double min, double max, double number) {
if (max < min) {
throw new IllegalArgumentException("min must less than max");
}
return min <= number && number <= max;
}
/**
* 是否为正整数
*
* @param num 数字
* @return true 为正整数
*/
public static boolean isN1(Long num) {
if (num == null) {
return false;
}
return 0 < num;
}
/**
* 安全限制数值范围,当入参值在最小值和最大值之间时返回原值,否则返回最小或最大值
*
* @param min 最小值
* @param value 入参值
* @param max 最大值
* @return 限制结果
*/
public static Number range(Number min, Number value, Number max) {
if (value.doubleValue() < min.doubleValue()) {
return min;
}
if (max.doubleValue() < value.doubleValue()) {
return max;
}
return value;
}
}

View File

@ -0,0 +1,355 @@
package com.imyeyu.utils;
import com.imyeyu.java.TimiJava;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
/**
* @author 夜雨
* @version 2023-08-07 11:50
*/
public class Collect {
/**
* 数组元素移除
*
* @param array 源数组
* @param items 移除项
* @param <T> 数组数据类型
* @return 移除结果数组
*/
@SuppressWarnings("unchecked")
public static <T> T[] arrayRemove(T[] array, T... items) {
if (array == null) {
return null;
}
if (TimiJava.isEmpty(array) || TimiJava.isEmpty(items)) {
return array;
}
Map<T, Integer> occurrences = new HashMap<>(array.length);
for (T v : items) {
occurrences.merge(v, 1, Integer::sum);
}
BitSet toRemove = new BitSet();
for (int i = 0; i < array.length; i++) {
T key = array[i];
Integer count = occurrences.get(key);
if (count != null) {
occurrences.put(key, count - 1);
if (occurrences.get(key) == 0) {
occurrences.remove(key);
}
toRemove.set(i);
}
}
int srcLength = array.length;
int removals = toRemove.cardinality();
T[] result = (T[]) Array.newInstance(array.getClass().getComponentType(), srcLength - removals);
int srcIndex = 0;
int destIndex = 0;
int count;
int set;
while ((set = toRemove.nextSetBit(srcIndex)) != -1) {
count = set - srcIndex;
if (count > 0) {
System.arraycopy(array, srcIndex, result, destIndex, count);
destIndex += count;
}
srcIndex = toRemove.nextClearBit(set);
}
count = srcLength - srcIndex;
if (count > 0) {
System.arraycopy(array, srcIndex, result, destIndex, count);
}
return result;
}
/**
* 取出哈希表的键作为列表
*
* @param map 哈希表
* @param <K> 键泛型
* @param <V> 值泛型
* @return 以哈希表键为类型的列表
*/
public static <K, V> List<K> mapKeys(Map<K, V> map) {
if ((map != null) && (!map.isEmpty())) {
return new ArrayList<>(map.keySet());
}
return null;
}
/**
* 随机哈希表
*
* @param map 哈希表
* @param limit 数量限制
* @param <K> 键泛型
* @param <V> 值泛型
* @return 随机结果
*/
public static <K, V> Map<K, V> randomMap(Map<K, V> map, int limit) {
Map<K, V> result = new LinkedHashMap<>();
List<K> list = mapKeys(map);
list.sort((lhs, rhs) -> {
int r1 = (int) (Math.random() * 10 + lhs.hashCode());
int r2 = (int) (Math.random() * 10 + rhs.hashCode());
return r1 - r2;
});
for (int i = 0, l = list.size(); i < l; i++) {
if (result.size() < limit) {
result.put(list.get(i), map.get(list.get(i)));
}
}
return result;
}
/**
* 根据键排序哈希表
*
* @param map 哈希表
* @param comparator 比较器
* @param <K> 键泛型
* @param <V> 值泛型
* @return 排序结果
*/
public static <K, V> Map<K, V> sortMap(Map<K, V> map, Comparator<K> comparator) {
if (map == null) {
return null;
}
if (map.isEmpty()) {
return map;
}
Map<K, V> r = new TreeMap<>(comparator);
r.putAll(map);
return r;
}
/**
* 根据数字值排序 Map正序
*
* @param map 哈希表
* @param <K> 键类型
* @return 排序结果列表
*/
public static <K> LinkedHashMap<K, Number> sortMapByNumberValueASC(Map<K, Number> map) {
return sortMapByNumberValue(map, true);
}
/**
* 根据数字值排序 Map倒序
*
* @param map 哈希表
* @param <K> 键类型
* @return 排序结果列表
*/
public static <K> LinkedHashMap<K, Number> sortMapByNumberValueDESC(Map<K, Number> map) {
return sortMapByNumberValue(map, false);
}
/**
* 根据数字值排序 Map
*
* @param map 哈希表
* @param isASC true 为正序
* @param <K> 键类型
* @return 排序结果列表
*/
public static <K> LinkedHashMap<K, Number> sortMapByNumberValue(Map<K, Number> map, boolean isASC) {
return sortMapByValue(map, (o1, o2) -> {
if (isASC) {
return Double.compare(o1.getValue().doubleValue(), o2.getValue().doubleValue());
} else {
return Double.compare(o2.getValue().doubleValue(), o1.getValue().doubleValue());
}
});
}
/**
* 根据值排序 Map
*
* @param map 哈希表
* @param comparator 比较器
* @param <K> 键类型
* @param <V> 值类型
* @return 排序结果列表
*/
public static <K, V> LinkedHashMap<K, V> sortMapByValue(Map<K, V> map, Comparator<Map.Entry<K, V>> comparator) {
List<Map.Entry<K, V>> list = new ArrayList<>(map.entrySet());
list.sort(comparator);
LinkedHashMap<K, V> result = new LinkedHashMap<>();
for (Map.Entry<K, V> entry : list) {
result.put(entry.getKey(), entry.getValue());
}
return result;
}
/**
* 根据字符串键排序哈希表
*
* @param map 哈希表
* @param <V> 值泛型
* @return 排序结果
*/
public static <V> Map<String, V> sortMapByStringKeyASC(Map<String, V> map) {
return sortMap(map, String::compareTo);
}
/**
* 根据数字键排序哈希表
*
* @param map 哈希表
* @param <V> 值泛型
* @return 排序结果
*/
public static <V> Map<Number, V> sortMapByNumberKeyASC(Map<Number, V> map) {
return sortMap(map, Comparator.comparingDouble(Number::doubleValue));
}
/**
* 根据数字键排序哈希表
*
* @param map 哈希表
* @param <V> 值泛型
* @return 排序结果
*/
public static <V> Map<Number, V> sortMapByNumberKeyDESC(Map<Number, V> map) {
return sortMap(map, Comparator.comparingDouble(Number::doubleValue).reversed());
}
/**
* 安全地根据键移除哈希表的对象
*
* @param map 哈希表
* @param key 键
* @param <K> 键类型
* @param <V> 值类型
* @return 被移除值
*/
public static <K, V> V removeByKey(Map<K, V> map, K key) {
Iterator<K> iterator = map.keySet().iterator();
K k;
while (iterator.hasNext()) {
k = iterator.next();
if (key.equals(k)) {
V v = map.get(k);
iterator.remove();
return v;
}
}
return null;
}
/**
* 安全地根据一些键移除哈希表的对象
*
* @param map 哈希表
* @param keys 键
* @param <K> 键类型
* @param <V> 值类型
* @return 被移除值列表
*/
@SafeVarargs
public static <K, V> List<V> removeByKeys(Map<K, V> map, K... keys) {
Set<K> keysList = Set.of(keys);
List<V> removes = new ArrayList<>();
Iterator<K> iterator = map.keySet().iterator();
K k;
while (iterator.hasNext()) {
k = iterator.next();
if (keysList.contains(k)) {
removes.add(map.get(k));
iterator.remove();
}
}
return removes;
}
/**
* 安全地根据值从哈希表移除数据
*
* @param map 哈希表
* @param value 值
* @param <K> 键类型
* @param <V> 值类型
* @return 被移除的键
*/
public static <K, V> K removeByValue(Map<K, V> map, V value) {
Iterator<K> iterator = map.keySet().iterator();
K k;
while (iterator.hasNext()) {
k = iterator.next();
if (map.get(k).equals(value)) {
iterator.remove();
return k;
}
}
return null;
}
/**
* 安全地根据一些值从哈希表移除数据
*
* @param map 哈希表
* @param values 值
* @param <K> 键类型
* @param <V> 值类型
* @return 被移除的键列表
*/
@SafeVarargs
public static <K, V> List<K> removeByValues(Map<K, V> map, V... values) {
Set<V> valuesList = Set.of(values);
List<K> removes = new ArrayList<>();
Iterator<K> iterator = map.keySet().iterator();
K k;
while (iterator.hasNext()) {
k = iterator.next();
if (valuesList.contains(map.get(k))) {
iterator.remove();
removes.add(k);
}
}
return removes;
}
/**
* 深克隆列表(通过序列化)
*
* @param list 列表
* @param <T> 数据类型
* @return 克隆列表
* @throws Exception 克隆异常
*/
@SuppressWarnings("unchecked")
public static <T> List<T> deepCopyList(List<T> list) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(list);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream in = new ObjectInputStream(bais);
List<T> result = (List<T>) in.readObject();
in.close();
bais.close();
oos.close();
baos.close();
return result;
}
}

View File

@ -0,0 +1,105 @@
package com.imyeyu.utils;
import com.imyeyu.java.bean.timi.TimiCode;
import com.imyeyu.java.bean.timi.TimiException;
import java.io.StringWriter;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
/**
* 编码操作
*
* @author 夜雨
* @version 2021-02-13 10:59
*/
public class Decoder {
/**
* 解码 Unicode 字符串
*
* @param data Unicode 字符串
* @return 解码结果
*/
public static String unicode(String data) {
StringWriter out = new StringWriter(data.length());
StringBuilder unicode = new StringBuilder(4);
boolean hadSlash = false;
boolean inUnicode = false;
for (int i = 0, l = data.length(); i < l; i++) {
char c = data.charAt(i);
if (inUnicode) {
unicode.append(c);
if (unicode.length() == 4) {
try {
out.write((char) Integer.parseInt(unicode.toString(), 16));
unicode.setLength(0);
inUnicode = false;
} catch (NumberFormatException nfe) {
throw new TimiException(TimiCode.ERROR, "Unable to parse unicode value: " + unicode);
}
}
continue;
}
if (hadSlash) {
hadSlash = false;
switch (c) {
case '\\' -> out.write('\\');
case '\'' -> out.write('\'');
case '\"' -> out.write('"');
case 'r' -> out.write('\r');
case 'f' -> out.write('\f');
case 't' -> out.write('\t');
case 'n' -> out.write('\n');
case 'b' -> out.write('\b');
case 'u' -> inUnicode = true;
default -> out.write(c);
}
continue;
} else if (c == '\\') {
hadSlash = true;
continue;
}
out.write(c);
}
if (hadSlash) {
out.write('\\');
}
return out.toString();
}
/**
* 解码 Base64 字符串
*
* @param data Base64 字符串
* @return 解码结果
*/
public static byte[] base64(String data) {
return Base64.getDecoder().decode(data);
}
/**
* 解码 Base64 字符串
*
* @param data Base64 字符串
* @return 解码结果
*/
public static String base64String(String data) {
return new String(base64(data), StandardCharsets.UTF_8);
}
/**
* 解码 URL 链接
*
* @param url 已编码的 URL 链接
* @return 解码结果
*/
public static String url(String url) {
if (url == null) {
return "";
}
return URLDecoder.decode(url, StandardCharsets.UTF_8);
}
}

View File

@ -0,0 +1,83 @@
package com.imyeyu.utils;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* @author 夜雨
* @version 2023-08-07 14:27
*/
public class Digest {
public static String md5(String data) throws NoSuchAlgorithmException {
return md5(data, StandardCharsets.UTF_8);
}
public static String md5(String data, Charset charset) throws NoSuchAlgorithmException {
return md5(data.getBytes(charset));
}
public static String md5(byte[] data) throws NoSuchAlgorithmException {
if (data == null || data.length == 0) {
return null;
}
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.update(data);
byte[] bytes = md5.digest();
char[] chars = new char[bytes.length * 2];
for (int i = 0, j = 0; i < bytes.length; i++) {
chars[j++] = Text.HEX_DIGITS_LOWER[bytes[i] >>> 4 & 0xF];
chars[j++] = Text.HEX_DIGITS_LOWER[bytes[i] & 0xF];
}
return new String(chars);
}
public static String sha1(String data) throws NoSuchAlgorithmException {
return sha1(data, StandardCharsets.UTF_8);
}
public static String sha1(String data, Charset charset) throws NoSuchAlgorithmException {
return sha1(data.getBytes(charset));
}
public static String sha1(byte[] bytes) throws NoSuchAlgorithmException {
MessageDigest sha = MessageDigest.getInstance("SHA");
sha.update(bytes);
return Text.byteToHex(sha.digest());
}
public static String sha256(String data) throws NoSuchAlgorithmException {
return sha256(data, StandardCharsets.UTF_8);
}
public static String sha256(String data, Charset charset) throws NoSuchAlgorithmException {
return sha256(data.getBytes(charset));
}
public static String sha256(byte[] bytes) throws NoSuchAlgorithmException {
MessageDigest sha = MessageDigest.getInstance("SHA-256");
sha.update(bytes);
return Text.byteToHex(sha.digest());
}
public static String sha512(String data) throws NoSuchAlgorithmException {
return sha512(data, StandardCharsets.UTF_8);
}
public static String sha512(String data, Charset charset) throws NoSuchAlgorithmException {
return sha512(data.getBytes(charset));
}
public static String sha512(byte[] bytes) throws NoSuchAlgorithmException {
MessageDigest sha = MessageDigest.getInstance("SHA-512");
BigInteger number = new BigInteger(1, sha.digest(bytes));
StringBuilder result = new StringBuilder(number.toString(16));
while (result.length() < 64) {
result.insert(0, "0");
}
return result.toString();
}
}

View File

@ -0,0 +1,162 @@
package com.imyeyu.utils;
import com.imyeyu.java.TimiJava;
import com.imyeyu.java.bean.timi.TimiCode;
import com.imyeyu.java.bean.timi.TimiException;
import java.io.StringWriter;
import java.net.IDN;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Locale;
import java.util.Map;
/**
* 编码操作
*
* @author 夜雨
* @version 2021-02-13 10:59
*/
public class Encoder {
/**
* Unicode 编码全角字符
*
* @param data 字符串
* @return 编码结果
*/
public static String unicode(String data) {
StringWriter out = new StringWriter(data.length() * 2);
for (int i = 0, l = data.length(); i < l; i++) {
char c = data.charAt(i);
String cHex = Integer.toHexString(c).toUpperCase(Locale.ENGLISH);
if (0xFFF < c) {
out.write("\\u" + cHex);
} else if (0xFF < c) {
out.write("\\u0" + cHex);
} else if (0x7F < c) {
out.write("\\u00" + cHex);
} else if (c < 32) {
switch (c) {
case '\b' -> {
out.write('\\');
out.write('b');
}
case '\n' -> {
out.write('\\');
out.write('n');
}
case '\t' -> {
out.write('\\');
out.write('t');
}
case '\f' -> {
out.write('\\');
out.write('f');
}
case '\r' -> {
out.write('\\');
out.write('r');
}
default -> {
if (0xF < c) {
out.write("\\u00" + cHex);
} else {
out.write("\\u000" + cHex);
}
}
}
} else {
switch (c) {
case '\'' -> {
out.write('\\');
out.write('\'');
}
case '"' -> {
out.write('\\');
out.write('"');
}
case '\\' -> {
out.write('\\');
out.write('\\');
}
case '/' -> {
out.write('\\');
out.write('/');
}
default -> out.write(c);
}
}
}
return out.toString();
}
/**
* Base64 编码字符串
*
* @param data 字符串
* @return 编码结果
*/
public static String base64(String data) {
return base64(data.getBytes(StandardCharsets.UTF_8));
}
/**
* Base64 编码字节数据
*
* @param bytes 字节数据
* @return 编码结果
*/
public static String base64(byte[] bytes) {
return Base64.getEncoder().encodeToString(bytes);
}
/**
* 编码 URL 参数值。不要传整个链接,此方法只用于<u>参数值</u>的编码
*
* @param data URL 参数
* @return 编码结果
*/
public static String urlArg(String data) {
return URLEncoder.encode(data, StandardCharsets.UTF_8);
}
/**
* 编码 URL 参数
*
* @param args URL 参数表
* @return 编码结果
*/
public static String urlArgs(Map<String, Object> args) {
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, Object> param : args.entrySet()) {
sb.append('&').append(param.getKey()).append('=').append(urlArg(param.getValue().toString()));
}
return sb.substring(1);
}
/**
* 编码 URL 链接,半角 "+" 号会使用 "%2B"
*
* @param urlStr URL 链接
* @return 编码结果
*/
public static String url(String urlStr) {
if (TimiJava.isEmpty(urlStr)) {
return "";
}
try {
URL url = new URL(urlStr);
URI uri = new URI(url.getProtocol(), url.getUserInfo(), IDN.toASCII(url.getHost()), url.getPort(), url.getPath(), url.getQuery(), url.getRef());
return uri.toASCIIString().replace("+", "%2B");
} catch (MalformedURLException | URISyntaxException e) {
throw new TimiException(TimiCode.ERROR, e.getMessage());
}
}
}

View File

@ -0,0 +1,261 @@
package com.imyeyu.utils;
import com.sun.management.OperatingSystemMXBean;
import java.awt.Desktop;
import java.awt.Toolkit;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.management.ManagementFactory;
import java.util.Comparator;
/**
* @author 夜雨
* @version 2023-08-07 11:46
*/
public class OS {
/**
*
*
* @author 夜雨
* @since 2021-02-13 10:15
*/
public interface FileSystem {
/** 文件系统路径分隔符 FileSystem.separator */
String SEP = File.separator;
/** 文件名排序,优先文件和文件夹,次级名称 */
Comparator<File> COMPARATOR_FILE_NAME = (f1, f2) -> {
if (f1.isDirectory() && f2.isFile()) {
return -1;
} else {
if (f1.isFile() && f2.isDirectory()) {
return 1;
} else {
return f1.getName().compareToIgnoreCase(f2.getName());
}
}
};
}
/**
* 系统平台
*
* @author 夜雨
* @version 2024-06-22 14:41
*/
public enum Platform {
WINDOWS,
LINUX,
MAC
}
/** 运行时系统 */
public static final String NAME = System.getProperty("os.name");
/** true 为 Windows 系统 */
public static final boolean IS_WINDOWS = NAME.toLowerCase().contains("win");
/** true 为 Mac OSX 系统 */
public static final boolean IS_OSX = NAME.toLowerCase().contains("mac os x");
/** true 为 UNIX 系统 */
public static final boolean IS_UNIX = NAME.contains("nix") || NAME.contains("nux") || NAME.contains("mac");
/** 当前系统平台 */
public static final Platform PLATFORM = IS_WINDOWS ? Platform.WINDOWS : (IS_OSX ? Platform.MAC : Platform.LINUX);
/**
* 不处理异常执行命令
*
* @param command 命令
*/
public static void run(String command) {
try {
Runtime.getRuntime().exec(new String[] {command});
} catch (Exception ignored) {
}
}
/**
* 终止程序时执行命令(主线程结束后)
*
* @param command 命令
*/
public static void runAfterShutdown(String command) {
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try {
new ProcessBuilder(command).start();
} catch (IOException ignored) {
}
}));
}
/** @return 系统内存大小(单位:字节) */
public static Long getSystemMemorySize() {
return ((OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean()).getTotalMemorySize();
}
/** Windows 系统禁用的字符 */
public static final Character[] INVALID_WINDOWS_SPECIFIC_CHARS = {'"', '*', ':', '<', '>', '?', '\\', '|', '/', 0x7F};
/** Unix 系统禁用的字符 */
public static final Character[] INVALID_UNIX_SPECIFIC_CHARS = {'\000'};
/**
* 文件名规则验证
*
* @param fileName 文件名
* @return true 为有效的
*/
public static boolean isValidFileName(String fileName) {
if (fileName == null || fileName.isEmpty() || 255 < fileName.length()) {
return false;
}
Character[] chars;
if (IS_WINDOWS) {
chars = INVALID_WINDOWS_SPECIFIC_CHARS;
} else if (IS_UNIX) {
chars = INVALID_UNIX_SPECIFIC_CHARS;
} else {
return true;
}
for (int i = 0; i < chars.length; i++) {
if (fileName.contains(chars[i].toString())) {
return false;
}
}
return true;
}
/**
* 调用系统资源管理器打开位置
*
* @param dir 文件
*/
public static void showInExplorer(File dir) {
if (dir == null || !dir.exists()) {
throw new IllegalArgumentException("dir is not found");
}
if (IS_WINDOWS) {
if (dir.isFile()) {
dir = dir.getParentFile();
}
run("explorer " + dir.getAbsolutePath() + FileSystem.SEP);
} else {
Desktop.getDesktop().browseFileDirectory(dir);
}
}
/**
* 调用系统资源管理器打开文件位置并选中
*
* @param files 文件列表
*/
public static void showAndSelectInExplorer(File... files) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < files.length; i++) {
sb.append('"').append(files[i].getAbsolutePath()).append('"').append(',');
}
if (IS_WINDOWS) {
run("explorer /select," + sb.substring(0, sb.length() - 1));
} else {
Desktop.getDesktop().browseFileDirectory(files[0]);
}
}
/**
* 检查某程序的某进程是否在运行Windows 方法)
*
* @param appName 程序名
* @param processName 进程名
* @param excludeProcessName 排除名称
* @return true 为正在运行
* @throws Exception 异常
*/
public static boolean findProcess4Similarity(String appName, String processName, String... excludeProcessName) throws Exception {
return findProcess(appName, processName, true, .8F, excludeProcessName);
}
/**
* 检查某程序的某进程是否在运行Windows 方法)
*
* @param appName 程序名
* @param processName 进程名
* @param similarity true 为启用相似度搜索
* @param similarityRate 相似度达到多少判定为 true
* @param excludeProcessName 排除名称
* @return true 为正在运行
* @throws Exception 异常
*/
public static boolean findProcess(String appName, String processName, boolean similarity, float similarityRate, String... excludeProcessName) throws Exception {
Process proc = Runtime.getRuntime().exec("tasklist -v -fi " + '"' + "imagename eq " + appName + '"');
BufferedReader br = new BufferedReader(new InputStreamReader(proc.getInputStream()));
int titleStart = -1; // 线程名起始字符
String line;
whi1e:
while ((line = br.readLine()) != null) {
if (titleStart == -1) {
if (line.startsWith("===")) {
titleStart = line.lastIndexOf(" ");
}
continue;
}
if (line.startsWith(appName)) {
// 排除名
if (excludeProcessName != null) {
for (int i = 0; i < excludeProcessName.length; i++) {
if (line.contains(excludeProcessName[i])) {
continue whi1e;
}
}
}
// 相似度匹配
if (similarity && titleStart < line.length()) {
String title = line.substring(titleStart).trim();
if (!title.equals("")) {
if (similarityRate < Text.similarityRatio(processName, title)) {
return true;
}
}
} else {
return line.contains(processName);
}
}
}
br.close();
return false;
}
/**
* 设置字符串到剪切板(复制)
*
* @param s 字符串
*/
public static void setIntoClipboard(String s) {
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(s), null);
}
/**
* 获取剪切版的字符串(粘贴)
*
* @return 剪切板字符串,如果剪切板没有字符串将返回空的字符串
*/
public static String getIntoClipboard() {
try {
return Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null).getTransferData(DataFlavor.stringFlavor).toString();
} catch (Exception e) {
return "";
}
}
}

View File

@ -0,0 +1,128 @@
package com.imyeyu.utils;
import com.imyeyu.java.TimiJava;
import com.imyeyu.java.bean.CallbackArgReturn;
import com.imyeyu.java.ref.Ref;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
/**
* 字符串插值器
*
* @author 夜雨
* @version 2022-12-01 00:41
*/
public class StringInterpolator {
/** ${} 插值正则 */
public static final String DOLLAR_OBJ = "\\$\\{(.+?)\\}";
/** {} 插值正则 */
public static final String SIMPLE_OBJ = "\\{(.+?)\\}";
/** 正则匹配器 */
private final Pattern pattern;
/** 为 true 时允许安全插值空,变量可以插值 null 而不抛出异常 */
private boolean nullable = false;
/** 过滤器 */
private Map<String, CallbackArgReturn<String, String>> filterMap;
/**
* 默认构造
*
* @param regex 插槽正则
*/
public StringInterpolator(String regex) {
this.pattern = Pattern.compile(regex);
}
/**
* 注入变量
*
* @param string 字符串
* @param argsMap 变量表
* @return 插值结果
*/
public String inject(String string, Map<String, Object> argsMap) {
return pattern.matcher(string).replaceAll(result -> {
String group = result.group(1);
String key = group.trim();
String[] filters = null;
if (group.contains("|")) {
// 过滤器
String[] groups = group.split("\\|");
key = groups[0].trim();
filters = new String[groups.length - 1];
System.arraycopy(groups, 1, filters, 0, filters.length);
}
Object value = argsMap.get(key);
if (key.contains(".")) {
// 反射获取
try {
String[] deepKey = key.split("\\.");
value = argsMap.get(deepKey[0]);
for (int i = 1; i < deepKey.length; i++) {
value = Ref.getFieldValue(value, deepKey[i], Object.class);
}
} catch (IllegalAccessException e) {
throw new RuntimeException("ref field file: " + e.getMessage(), e);
}
}
if (TimiJava.isNotEmpty(filterMap) && TimiJava.isNotEmpty(filters)) {
// 过滤
for (int i = 0; i < filters.length; i++) {
CallbackArgReturn<String, String> filter = filterMap.get(filters[i].trim());
if (filter == null) {
throw new NullPointerException("not found %s filter for %s".formatted(filters[i], key));
}
value = filter.handler((String) value);
}
}
if (value == null) {
if (!nullable) {
throw new NullPointerException("null pointer exception for arg: " + key);
}
value = "";
}
return String.valueOf(value);
});
}
public void putFilter(String name, CallbackArgReturn<String, String> callback) {
if (filterMap == null) {
filterMap = new HashMap<>();
}
filterMap.put(name, callback);
}
public void putAllFilter(Map<String, CallbackArgReturn<String, String>> filterMap) {
if (this.filterMap == null) {
this.filterMap = new HashMap<>();
}
this.filterMap.putAll(filterMap);
}
public void removeFilter(String name) {
if (TimiJava.isNotEmpty(filterMap)) {
filterMap.remove(name);
}
}
public void clearFilter() {
if (TimiJava.isNotEmpty(filterMap)) {
filterMap.clear();
}
}
public void setNullable(boolean nullable) {
this.nullable = nullable;
}
public boolean isNullable() {
return nullable;
}
}

View File

@ -0,0 +1,463 @@
package com.imyeyu.utils;
import com.imyeyu.java.TimiJava;
import java.io.UnsupportedEncodingException;
import java.security.SecureRandom;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;
import java.util.regex.Pattern;
/**
* @author 夜雨
* @version 2023-08-07 11:58
*/
public class Text {
/** 十六进制小写 */
public static char[] HEX_DIGITS_LOWER = "0123456789abcdef".toCharArray();
/** 十六进制大写 */
public static char[] HEX_DIGITS_UPPER = "0123456789ABCDEF".toCharArray();
/**
* 字节数据转 16 进制字符串
*
* @param bytes 字节数据
* @return 16 进制字符串
*/
public static String byteToHex(byte[] bytes) {
final int l = bytes.length;
final char[] c = new char[l << 1];
for (int i = 0, j = 0; i < l; i++) {
c[j++] = Text.HEX_DIGITS_LOWER[(0xF0 & bytes[i]) >>> 4];
c[j++] = Text.HEX_DIGITS_LOWER[0x0F & bytes[i]];
}
return new String(c);
}
/**
* 16 进制字符串转字节数据
*
* @param hex 16 进制字符串
* @return 字节数据
* @throws UnsupportedEncodingException 不支持的编码
*/
public static byte[] hexToByte(String hex) throws UnsupportedEncodingException {
final char[] c = hex.toCharArray();
final byte[] b = new byte[c.length >> 1];
final int len = c.length;
if ((len & 0x01) != 0) {
throw new UnsupportedEncodingException("Odd number of characters.");
}
final int outLen = len >> 1;
if (c.length < outLen) {
throw new UnsupportedEncodingException("Output array is not large enough to accommodate decoded data.");
}
for (int i = 0, j = 0; j < len; i++) {
int f = toDigit(c[j], j) << 4;
j++;
f = f | toDigit(c[j], j);
j++;
b[i] = (byte) (f & 0xFF);
}
return b;
}
private static int toDigit(final char ch, final int index) throws UnsupportedEncodingException {
final int digit = Character.digit(ch, 16);
if (digit == -1) {
throw new UnsupportedEncodingException("Illegal hexadecimal character " + ch + " at index " + index);
}
return digit;
}
/**
* 是否为半角字符
*
* @param c 字符
* @return 为 true 是表示是半角字符
*/
public static boolean isHalfChar(char c) {
return (int) c < 129;
}
/**
* 字符串加双引号
*
* @param text 字符串内容
* @return 结果
*/
public static String quote(String text) {
return '"' + text + '"';
}
/**
* 前补零(最终长度 2 字符)
*
* @param number 数值
* @return 补零字符串
*/
public static String zero(Number number) {
return zero(2, number);
}
public static String paddedSpaceStart(String str, int totalWidth) {
return String.format("%" + totalWidth + "s", str);
}
public static String paddedSpaceEnd(String str, int totalWidth) {
return String.format("%-" + totalWidth + "s", str);
}
/**
* 前补零
*
* @param l 最终长度
* @param number 数值
* @return 补零字符串
*/
public static String zero(int l, Number number) {
return String.format("%0" + l + "d", number);
}
/**
* 正则表达式测试
*
* @param reg 正则
* @param value 文本
* @return true 为匹配
*/
public static boolean testReg(String reg, String value) {
return Pattern.compile(reg).matcher(value).matches();
}
/**
* 驼峰转下划线
*
* @param camelCaseStr 驼峰字符串
* @return 下划线字符串
*/
public static String camelCase2underscore(String camelCaseStr) {
return camelCaseStr.replaceAll("([a-z])([A-Z])", "$1_$2").toLowerCase();
}
/**
* 下划线转驼峰
*
* @param underscoreName 下划线字符串
* @return 驼峰字符串
*/
public static String underscore2camelCase(String underscoreName) {
if (TimiJava.isEmpty(underscoreName)) {
return underscoreName;
}
StringBuilder result = new StringBuilder();
boolean flag = false;
for (int i = 0; i < underscoreName.length(); i++) {
char c = underscoreName.charAt(i);
if ('_' == c) {
flag = true;
} else {
if (flag) {
result.append(Character.toUpperCase(c));
flag = false;
} else {
result.append(c);
}
}
}
return result.toString();
}
/**
* 与多个字符串进行与比较
*
* @param string 比较字符串
* @param other 其他字符串
* @return true 时全部其他字符串和比较字符串一致
*/
public static boolean eqAnd(String string, String... other) {
for (int i = 0; i < other.length; i++) {
if (!string.equals(other[i])) {
return false;
}
}
return true;
}
/**
* 与多个字符串进行或比较
*
* @param string 比较字符串
* @param other 其他字符串
* @return true 时其他字符串存在和比较字符串一致
*/
public static boolean eqOr(String string, String... other) {
for (int i = 0; i < other.length; i++) {
if (!string.equals(other[i])) {
return false;
}
}
return true;
}
/**
* 与多个字符串进行忽略大小写的与比较
*
* @param string 比较字符串
* @param other 其他字符串
* @return true 时全部其他字符串和比较字符串一致
*/
public static boolean eqIgnoreCaseAnd(String string, String... other) {
for (int i = 0; i < other.length; i++) {
if (!string.equalsIgnoreCase(other[i])) {
return false;
}
}
return true;
}
/**
* 与多个字符串进行忽略大小写的或比较
*
* @param string 比较字符串
* @param other 其他字符串
* @return true 时其他字符串存在和比较字符串一致
*/
public static boolean eqIgnoreCaseOr(String string, String... other) {
for (int i = 0; i < other.length; i++) {
if (string.equalsIgnoreCase(other[i])) {
return true;
}
}
return false;
}
/**
* 与多个字符串进行忽略大小写包含关系
*
* @param string 原字符串
* @param other 其他字符串
* @return true 为 string 中至少含有一个 other 的忽略大小写的字符段
*/
public static boolean containsIgnoreCase(String string, String... other) {
String stringUpper = string.toUpperCase();
String stringLower = string.toLowerCase();
for (int i = 0; i < other.length; i++) {
if (stringLower.contains(other[i].toLowerCase()) || stringUpper.contains(other[i].toUpperCase())) {
return true;
}
}
return false;
}
public static String randomString(int length) {
return randomString("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", length);
}
public static String randomString(String pool, int length) {
SecureRandom r = new SecureRandom();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < length; i++) {
sb.append(pool.charAt(r.nextInt(pool.length() - 1)));
}
return sb.toString();
}
/**
* 较短的临时 UUID如果使用在庞大的数据里很可能会发生重复
*
* @return 完整 UUID 前 8 位
*/
public static String tempUUID() {
return UUID.randomUUID().toString().substring(0, 8);
}
/**
* 计算字符串相似度(编辑距离算法)
*
* @param source 需比较的字符串
* @param target 被比较的字符串
* @param isIgnore 为 true 时忽略大小写
* @return 相似度 [0, 1]
*/
private static float levenshteinDistance(String source, String target, boolean isIgnore) {
int[][] d;
int n = source.length(), m = target.length(), i, j, temp;
char charS, charT;
if (n == 0) {
return m;
}
if (m == 0) {
return n;
}
d = new int[n + 1][m + 1];
for (i = 0; i <= n; i++) {
d[i][0] = i;
}
for (j = 0; j <= m; j++) {
d[0][j] = j;
}
for (i = 1; i <= n; i++) {
charS = source.charAt(i - 1);
for (j = 1; j <= m; j++) {
charT = target.charAt(j - 1);
if (isIgnore) {
if (charS == charT || charS == charT + 32 || charS + 32 == charT) {
temp = 0;
} else {
temp = 1;
}
} else {
if (charS == charT) {
temp = 0;
} else {
temp = 1;
}
}
d[i][j] = min(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + temp);
}
}
return d[n][m];
}
/**
* 三数求最小
*
* @param one 值一
* @param two 值二
* @param three 值三
* @return 最小值
*/
public static int min(int one, int two, int three) {
return (one = Math.min(one, two)) < three ? one : three;
}
/**
* 批量相似度比较字符串(忽略大小写),返回相似度比较列表倒叙结果,较为相似的排最前
*
* @param sources 需比较的字符串列表
* @param target 被比较的字符串
* @return 比较结果列表
*/
public static LinkedHashMap<String, Number> similarityRatioList(Collection<String> sources, String target) {
return similarityRatioList(sources, target, true);
}
/**
* 批量相似度比较字符串,返回相似度比较列表倒叙结果,较为相似的排最前
*
* @param sources 需比较的字符串列表
* @param target 被比较的字符串
* @param isIgnoreCase true 为忽略大小写
* @return 比较结果列表
*/
public static LinkedHashMap<String, Number> similarityRatioList(Collection<String> sources, String target, boolean isIgnoreCase) {
Map<String, Number> items = new HashMap<>();
for (String source : sources) {
items.put(source, 0);
}
items.replaceAll((k, v) -> similarityRatio(k, target, isIgnoreCase));
return Collect.sortMapByNumberValueDESC(items);
}
/**
* 求字符串相似度,忽略大小写
*
* @param source 需比较的字符串
* @param target 被比较的字符串
* @return 相似度 [0, 1]
*/
public static float similarityRatio(String source, String target) {
return similarityRatio(source, target, true);
}
/**
* 求字符串相似度
*
* @param source 需比较的字符串
* @param target 被比较的字符串
* @param isIgnoreCase true 为忽略大小写
* @return 相似度 [0, 1]
*/
public static float similarityRatio(String source, String target, boolean isIgnoreCase) {
float ret;
final int max = Math.max(source.length(), target.length());
if (max == 0) {
ret = 1;
} else {
ret = 1 - levenshteinDistance(source, target, isIgnoreCase) / max;
}
return ret;
}
/**
* 检验字符串是否为 json 数据,不校验是否有错误
*
* @param s 字符串
* @return true 为是 JSON 数据
*/
public static boolean isJson(String s) {
return isJsonObject(s) || isJsonArray(s);
}
/**
* 检验字符串是否为 json 对象,不校验是否有错误
*
* @param s 字符串
* @return true 为是 JSON 对象
*/
public static boolean isJsonObject(String s) {
return s.startsWith("{") && s.endsWith("}");
}
/**
* 检验字符串是否为 json 数组,不校验是否有错误
*
* @param s 字符串
* @return true 为是 JSON 数组
*/
public static boolean isJsonArray(String s) {
return s.startsWith("[") && s.endsWith("]");
}
/**
* 字符串替换,不需要正则的情况下
*
* @param string 字符串
* @param from 被替换字符
* @param to 替换字符串
* @return 替换结果
*/
public static String replaceAll(String string, char from, String to) {
return replaceAll(new StringBuilder(string), from, to);
}
/**
* 字符串替换,不需要正则的情况下
*
* @param sb 字符构造器
* @param from 被替换字符
* @param to 替换字符串
* @return 替换结果
*/
public static String replaceAll(StringBuilder sb, char from, String to) {
for (int i = 0; i < sb.length(); i++) {
if (sb.charAt(i) == from) {
sb.replace(i, ++i, to);
}
}
return sb.toString();
}
}

View File

@ -0,0 +1,417 @@
package com.imyeyu.utils;
import com.imyeyu.java.TimiJava;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 时间转换相关
*
* @author 夜雨
* @version 2021-06-10 20:07
*/
public class Time {
// ---------- 格式化 ----------
/** 格式化 yy */
public static final SimpleDateFormat year = new SimpleDateFormat("yy");
/** 格式化 yyyy */
public static final SimpleDateFormat yearFull = new SimpleDateFormat("yyyy");
/** 格式化 M */
public static final SimpleDateFormat month = new SimpleDateFormat("M");
/** 格式化 MM */
public static final SimpleDateFormat monthFull = new SimpleDateFormat("MM");
/** 格式化 d */
public static final SimpleDateFormat day = new SimpleDateFormat("d");
/** 格式化 dd */
public static final SimpleDateFormat dayFull = new SimpleDateFormat("dd");
/** 格式化 HH */
public static final SimpleDateFormat hour = new SimpleDateFormat("HH");
/** 格式化 mm */
public static final SimpleDateFormat minute = new SimpleDateFormat("mm");
/** 格式化 ss */
public static final SimpleDateFormat second = new SimpleDateFormat("ss");
/** 格式化 hh:mm */
public static final SimpleDateFormat hhmm = new SimpleDateFormat("HH:mm");
/** 格式化 mm:ss */
public static final SimpleDateFormat mmss = new SimpleDateFormat("mm:ss");
/** 格式化 yyyyMMdd */
public static final SimpleDateFormat ymd = new SimpleDateFormat("yyyyMMdd");
/** 格式化 HH:mm:ss.SSS */
public static final SimpleDateFormat log = new SimpleDateFormat("[HH:mm:ss.SSS]");
/** 格式化 HH:mm:ss.SSS */
public static final SimpleDateFormat longLog = new SimpleDateFormat("[yyyy-MM-dd HH:mm:ss.SSS]");
/** 格式化 yyyy-MM-dd */
public static final SimpleDateFormat date = new SimpleDateFormat("yyyy-MM-dd");
/** 格式化 HH:mm:ss */
public static final SimpleDateFormat time = new SimpleDateFormat("HH:mm:ss");
/** 格式化 yyyy-MM-dd HH:mm:ss */
public static final SimpleDateFormat dateTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
/** 格式化 yyyy-MM-dd'T'HH:mm:ss */
public static final SimpleDateFormat dateTimeT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
// ---------- 长整型时间戳 ----------
/** 1 秒时间戳 */
public static final long S = 1000;
/** 1 分钟时间戳 */
public static final long M = S * 60;
/** 1 小时时间戳 */
public static final long H = M * 60;
/** 1 天时间戳 */
public static final long D = H * 24;
// ---------- 整型时间戳 ----------
/** 1 秒时间戳(整型) */
public static final int SI = 1000;
/** 1 分钟时间戳(整型) */
public static final int MI = SI * 60;
/** 1 小时时间戳(整型) */
public static final int HI = MI * 60;
/** 1 天时间戳(整型) */
public static final int DI = HI * 24;
/**
* 计算两个时间戳精确的日期时间差
*
* @param begin 开始时间戳
* @param end 结束时间戳
* @return 时差
*/
public static Between between(long begin, long end) {
if (end < begin) {
throw new IllegalArgumentException("end time must greater than begin time" + end + " < " + begin);
}
LocalDateTime ldtBegin = toLocalDateTime(begin);
LocalDateTime ldtEnd = toLocalDateTime(end);
Between between = new Between();
between.year = (int) ChronoUnit.YEARS.between(ldtBegin, ldtEnd);
between.month = (int) ChronoUnit.MONTHS.between(ldtBegin, ldtEnd) % 12;
between.day = (int) ChronoUnit.DAYS.between(ldtBegin.plusMonths(between.year * 12L + between.month), ldtEnd);
between.hour = (int) (ChronoUnit.HOURS.between(ldtBegin, ldtEnd) - ChronoUnit.DAYS.between(ldtBegin, ldtEnd) * 24);
between.minute = (int) ChronoUnit.MINUTES.between(ldtBegin, ldtEnd) % 60;
between.second = (int) ChronoUnit.SECONDS.between(ldtBegin, ldtEnd) % 60;
between.millis = (int) ((end - begin) % 1000);
return between;
}
/**
* 获取此刻毫秒
*
* @return 毫秒
*/
public static long now() {
return System.currentTimeMillis();
}
/**
* 获取当前时间 yyyy-MM-dd HH:mm:ss
*
* @return 当前时间
*/
public static String nowString() {
return toDateTime(new Date());
}
/**
* 获取昨天零时时间戳
*
* @return 昨天零时时间戳
*/
public static long yesterday() {
return today() - D;
}
/**
* 获取今天零时时间戳
*
* @return 今天零时时间戳
*/
public static long today() {
long now = now();
final long H8 = H * 8;
return ((now + H8 - (now + H8) % (H * 24)) - H8);
}
/**
* 获取明天零时时间戳
*
* @return 明天零时时间戳
*/
public static long tomorrow() {
return today() + D;
}
/**
* 转义为日期 yyyy-MM-dd
*
* @param unixTime 时间戳
* @return 日期字符串
*/
public static String toDate(long unixTime) {
return date.format(unixTime);
}
/**
* 转义为时间 HH:mm:ss
*
* @param unixTime 时间戳
* @return 时间字符串
*/
public static String toTime(long unixTime) {
return time.format(unixTime);
}
/**
* 转义为日期时间 yyyy-MM-dd HH:mm:ss
*
* @param unixTime 时间戳
* @return 日期时间字符串
*/
public static String toDateTime(long unixTime) {
return dateTime.format(unixTime);
}
/**
* 转义为日期时间 yyyy-MM-dd HH:mm:ss
*
* @param date 时间对象
* @return 日期时间字符串
*/
public static String toDateTime(Date date) {
return dateTime.format(date);
}
/**
* 本地时间对象转时间戳(本地时区)
*
* @param date 本地日期对象
* @return 时间戳
*/
public static Long fromLocalDate(LocalDate date) {
if (date == null) {
return null;
}
return date.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
}
/**
* 本地时间对象转时间戳(本地时区)
*
* @param dateTime 本地日期对象
* @return 时间戳
*/
public static Long fromLocalDateTime(LocalDateTime dateTime) {
if (dateTime == null) {
return null;
}
return dateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
}
/**
* 时间戳转本地日期(本地时区)
*
* @param unixTime 时间戳
* @return 本地日期对象
*/
public static LocalDateTime toLocalDateTime(Long unixTime) {
if (unixTime == null) {
return null;
}
return Instant.ofEpochMilli(unixTime).atZone(ZoneId.systemDefault()).toLocalDateTime();
}
/**
* 媒体时间操作
*
* @author 夜雨
* @version 2023-01-24 01:10
*/
public static class Media {
private static final Pattern PATTERN = Pattern.compile("(\\d*):?([0-5]\\d):([0-5]\\d)\\.?(\\d{1,3})?");
/**
* 解析字符串为毫秒,匹配 999:59:59.999 格式,兼容 999:59:59
*
* @param time 时间字符串
* @return 毫秒
*/
public static long fromString(String time) {
if (TimiJava.isEmpty(time)) {
return 0;
}
Matcher matcher = PATTERN.matcher(time);
if (matcher.find()) {
long h = 0;
if (TimiJava.isNotEmpty(matcher.group(1))) {
h = Long.parseLong(matcher.group(1));
}
long m = Long.parseLong(matcher.group(2));
long s = Long.parseLong(matcher.group(3));
long ms = 0;
if (TimiJava.isNotEmpty(matcher.group(4))) {
String gms = matcher.group(4);
ms = Long.parseLong(gms);
if (gms.length() == 2) {
ms *= 10;
}
}
return h * Time.H + m * Time.M + s * Time.S + ms;
}
return 0;
}
/**
* 秒转媒体时间00:00:00
*
* @param second 秒
* @return 时间字符串
*/
public static String toString(double second) {
return toString((int) second);
}
/**
* 秒转媒体时间00:00:00
*
* @param second 秒
* @return 时间字符串
*/
public static String toString(int second) {
int h = second / 60 / 60;
if (0 < h) {
return String.format("%d:%02d:%02d", h, second / 60 - h * 60, second % 60);
} else {
return String.format("%02d:%02d", second / 60, second % 60);
}
}
/**
* 毫秒转媒体时间音视频播放时间00:00:00.000
*
* @param millis 毫秒
* @return 时间字符串
*/
public static String toString(long millis) {
long s = millis / 1000;
long h = s / 60 / 60;
return String.format("%d:%02d:%02d.%03d", h, s / 60 - h * 60, s % 60, millis % 1000);
}
}
/**
* 时差
*
* @author 夜雨
* @version 2022-10-12 14:46
*/
public static class Between {
int year;
int month;
int day;
int hour;
int minute;
int second;
int millis;
/**
* 获取年数
*
* @return 年数
*/
public int getYear() {
return year;
}
/**
* 获取月数
*
* @return 月数
*/
public int getMonth() {
return month;
}
/**
* 获取天数
*
* @return 天数
*/
public int getDay() {
return day;
}
/**
* 获取小时
*
* @return 小时
*/
public int getHour() {
return hour;
}
/**
* 获取分钟
*
* @return 分钟
*/
public int getMinute() {
return minute;
}
/**
* 获取秒
*
* @return 秒
*/
public int getSecond() {
return second;
}
/**
* 获取毫秒
*
* @return 毫秒
*/
public int getMillis() {
return millis;
}
}
}