!1184 [v6] [feature] 添加十二时辰与现代时间的互转

Merge pull request !1184 from 阿超/v6-dev
This commit is contained in:
Looly 2024-02-27 00:50:42 +00:00 committed by Gitee
commit 648ffb20a6
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
3 changed files with 200 additions and 0 deletions

View File

@ -168,6 +168,14 @@ public class DateBetween implements Serializable {
return result;
}
public Date getBegin() {
return begin;
}
public Date getEnd() {
return end;
}
/**
* 格式化输出时间差
*

View File

@ -0,0 +1,139 @@
package org.dromara.hutool.core.date.chinese;
import org.dromara.hutool.core.date.DateBetween;
import org.dromara.hutool.core.text.StrUtil;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* 时辰转换器支持宋以后的二十四时辰制度
* <p>本转换器提供以下功能
* <ul>
* <li>处理包含后缀的长安时辰描述并自动返回相应的现代时间段
* 分别对应每个时辰的前半段和后半段而不带后缀的描述则涵盖该时辰的完整时间段</li>
* <li>根据小时数转换为相应的长安时辰描述通过{@code isAbs}参数控制是否包含</li>
* </ul>
* </p>
* <p>
* 异常情况
* <ul>
* <li>如果输入的长安时辰描述无效或不被识别{@code toModernTime} 方法将抛出 {@code IllegalArgumentException}</li>
* <li>同样如果{@code toShiChen}方法接收到无效的小时数将返回未知</li>
* </ul>
* </p>
* <p>
* 示例
* <ul>
* <li>{@code toModernTime("子时")} 返回的时间段从23点开始到1点结束</li>
* <li>{@code toModernTime("子初")} 返回的时间段从23点开始到0点结束</li>
* <li>{@code toModernTime("子正")} 返回的时间段从0点开始到1点结束</li>
* <li>{@code toShiChen(0, false)} 返回子正</li>
* <li>{@code toShiChen(0, true)} 返回子时</li>
* </ul>
* </p>
*
* @author achao@hutool.cn
*/
public class ShiChen {
private static final Map<String, Integer> timeMap = new HashMap<>();
private static final Map<String, Integer[]> fullTimeMap = new HashMap<>();
private static final Map<Integer, String> hourToShiChenMap = new HashMap<>();
private static final Map<Integer, String> hourToShiChenAbsMap = new HashMap<>();
static {
// 初始化时辰对应的小时范围
String[] times = {"", "", "", "", "", "", "", "", "", "", "", ""};
int hour = 23;
for (String time : times) {
timeMap.put(time + "", hour % 24);
timeMap.put(time + "", (hour + 1) % 24);
fullTimeMap.put(time, new Integer[]{hour % 24, (hour + 2) % 24});
hour += 2;
}
// 初始化小时到时辰的映射
hour = 23;
for (String time : times) {
hourToShiChenMap.put(hour % 24, time + "");
hourToShiChenMap.put((hour + 1) % 24, time + "");
hourToShiChenAbsMap.put(hour % 24, time + "");
hourToShiChenAbsMap.put((hour + 1) % 24, time + "");
hour += 2;
}
}
/**
* 将长安时辰描述转换为现代时间段
* <p>
* 示例
* <ul>
* <li>{@code toModernTime("子时")} 返回的时间段从23点开始到1点结束</li>
* <li>{@code toModernTime("子初")} 返回的时间段从23点开始到0点结束</li>
* <li>{@code toModernTime("子正")} 返回的时间段从0点开始到1点结束</li>
* </ul>
* </p>
*
* @param shiChen 长安时辰描述可以是结尾
* @return {@link DateBetween} 对象表示起始和结束时间
* @throws IllegalArgumentException 如果输入的长安时辰描述无效
*/
public static DateBetween toModernTime(String shiChen) {
if (StrUtil.isEmpty(shiChen)) {
throw new IllegalArgumentException("Invalid shiChen");
}
Integer startHour, endHour;
LocalDateTime start, end;
if (shiChen.endsWith("") || shiChen.endsWith("")) {
startHour = timeMap.get(shiChen);
if (startHour == null) {
throw new IllegalArgumentException("Invalid ChangAn time");
}
endHour = (startHour + 1) % 24;
} else {
String baseTime = shiChen.replace("", "");
Integer[] hours = fullTimeMap.get(baseTime);
if (hours == null) {
throw new IllegalArgumentException("Invalid ChangAn time");
}
startHour = hours[0];
endHour = hours[1];
}
start = LocalDateTime.now().withHour(startHour).withMinute(0).withSecond(0).withNano(0);
end = (startHour > endHour) ? start.plusDays(1).withHour(endHour) : start.withHour(endHour);
Date startDate = Date.from(start.atZone(ZoneId.systemDefault()).toInstant());
Date endDate = Date.from(end.atZone(ZoneId.systemDefault()).toInstant());
return DateBetween.of(startDate, endDate);
}
/**
* 根据给定的小时数转换为对应的长安时辰描述
* <p>
* 示例
* <ul>
* <li>{@code toShiChen(0, false)} 返回子正</li>
* <li>{@code toShiChen(0, true)} 返回子时</li>
* </ul>
* </p>
*
* @param hour 小时数应在0到23之间
* @param isAbs 是否返回绝对时辰描述即包含后缀而不是
* @return 长安时辰描述如果小时数无效则返回未知
*/
public static String toShiChen(int hour, boolean isAbs) {
String result = hourToShiChenAbsMap.getOrDefault(hour, "未知");
if (!isAbs && !result.equals("未知")) {
result = hourToShiChenMap.get(hour);
}
return result;
}
}

View File

@ -0,0 +1,53 @@
package org.dromara.hutool.core.date.chinese;
import org.dromara.hutool.core.date.DateUnit;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
/**
* ShiChenTest
*
* @author achao@apache.org
*/
public class ShiChenTest {
@Test
void testToModernTime() {
// 测试后缀的转换表示整个时辰
assertEquals(2, ShiChen.toModernTime("子时").between(DateUnit.HOUR));
// 测试后缀的转换表示时辰的前半段和后半段
assertEquals(1, ShiChen.toModernTime("子初").between(DateUnit.HOUR));
assertEquals(1, ShiChen.toModernTime("子正").between(DateUnit.HOUR));
// 测试所有时辰
String[] times = {"", "", "", "", "", "", "", "", "", "", "", ""};
for (String time : times) {
assertEquals(2, ShiChen.toModernTime(time + "").between(DateUnit.HOUR));
assertEquals(1, ShiChen.toModernTime(time + "").between(DateUnit.HOUR));
assertEquals(1, ShiChen.toModernTime(time + "").between(DateUnit.HOUR));
}
assertThrows(IllegalArgumentException.class, () -> ShiChen.toModernTime("无效时"));
assertThrows(IllegalArgumentException.class, () -> ShiChen.toModernTime("无效正"));
assertThrows(IllegalArgumentException.class, () -> ShiChen.toModernTime(""));
assertThrows(IllegalArgumentException.class, () -> ShiChen.toModernTime(null));
}
@Test
void testToShiChen() {
// 测试小时转换为长安时辰不包含
assertEquals("子时", ShiChen.toShiChen(23, true));
assertEquals("子时", ShiChen.toShiChen(0, true));
// 测试小时转换为长安时辰包含
assertEquals("子正", ShiChen.toShiChen(0, false));
assertEquals("丑初", ShiChen.toShiChen(1, false));
// 测试边界条件
assertEquals("未知", ShiChen.toShiChen(24, true));
assertEquals("未知", ShiChen.toShiChen(-1, false));
}
}