十二时辰与现代时间的互转(精确版)

简介: 十二时辰与现代时间的互转(精确版)


内抑怒气,外制愠色,处世待人,心平气和。——圣德太子

最新版优化了一版,支持了

宋以后把十二时辰中每个时辰平分为初、正两部分,这样,子初、子正、丑初、丑正…依次下去,恰为二十四时辰,同现在一天二十四小时时间一致

hutool-core/src/main/java/org/dromara/hutool/core/date/chinese/ShiChen.java · dromara/hutool - Gitee.com

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>
 * 异常情况:
 * <ul>
 * <li>如果输入的时辰描述无效或不被识别,{@code toModernTime} 方法将抛出 {@code IllegalArgumentException}。</li>
 * <li>同样,如果{@code toShiChen}方法接收到无效的小时数,将返回“未知”。</li>
 * </ul>
 * 示例:
 * <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>
 *
 * @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 {
    // 初始化时辰对应的小时范围
    final String[] times = {"子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥"};
    int hour = 23;
    for (final 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 (final 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;
    }
  }
  /**
   * 将时辰描述转换为现代时间段。示例:
   * <ul>
   * <li>{@code toModernTime("子时")} 返回的时间段从23点开始到1点结束。</li>
   * <li>{@code toModernTime("子初")} 返回的时间段从23点开始到0点结束。</li>
   * <li>{@code toModernTime("子正")} 返回的时间段从0点开始到1点结束。</li>
   * </ul>
   *
   * @param shiChen 时辰描述,可以是“时”、“初”或“正”结尾。
   * @return {@link DateBetween} 对象,表示起始和结束时间。
   * @throws IllegalArgumentException 如果输入的时辰描述无效。
   */
  public static DateBetween toModernTime(final String shiChen) {
    if (StrUtil.isEmpty(shiChen)) {
      throw new IllegalArgumentException("Invalid shiChen");
    }
    final Integer startHour;
    final Integer endHour;
    final LocalDateTime start;
    final LocalDateTime end;
    if (shiChen.endsWith("初") || shiChen.endsWith("正")) {
      startHour = timeMap.get(shiChen);
      if (startHour == null) {
        throw new IllegalArgumentException("Invalid ShiChen time");
      }
      endHour = (startHour + 1) % 24;
    } else {
      final String baseTime = shiChen.replace("时", "");
      final Integer[] hours = fullTimeMap.get(baseTime);
      if (hours == null) {
        throw new IllegalArgumentException("Invalid ShiChen 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);
    final Date startDate = Date.from(start.atZone(ZoneId.systemDefault()).toInstant());
    final Date endDate = Date.from(end.atZone(ZoneId.systemDefault()).toInstant());
    return DateBetween.of(startDate, endDate);
  }
  /**
   * 根据给定的小时数转换为对应的时辰描述。示例:
   * <ul>
   *   <li>{@code toShiChen(0, false)} 返回“子正”。</li>
   *   <li>{@code toShiChen(0, true)} 返回“子时”。</li>
   * </ul>
   *
   * @param hour  小时数,应在0到23之间。
   * @param isAbs 是否返回绝对时辰描述(即包含“时”后缀),而不是“初”或“正”。
   * @return 时辰描述,如果小时数无效,则返回“未知”。
   */
  public static String toShiChen(final int hour, final boolean isAbs) {
    String result = hourToShiChenAbsMap.getOrDefault(hour, "未知");
    if (!isAbs && !result.equals("未知")) {
      result = hourToShiChenMap.get(hour);
    }
    return result;
  }
}

hutool-core/src/test/java/org/dromara/hutool/core/date/chinese/ShiChenTest.java · dromara/hutool - Gitee.com

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));
  }
}


相关文章
|
15天前
|
存储 安全 Java
"Java编码魔法:揭秘图片与文件的Base64神秘转换术,让数据在指尖跳跃!"
【8月更文挑战第16天】Base64编码在Java开发中常用于将二进制数据如图片转换为ASCII字符串以便传输。编码使用64个字符及等号填充,每3字节数据编码为4个字符。Java利用`java.util.Base64`类实现此功能:读取图片或文件为字节数组后进行编码。解码时将Base64字符串还原为字节数组并写入文件。需注意编码效率降低、不提供安全性及特殊字符兼容性等问题。掌握这些技巧有助于解决Web开发中的数据传输需求。
35 4
|
4月前
|
存储 安全 Unix
C#.Net筑基-类型系统②常见类型--日期和时间的故事
在System命名空间中,有几种表示日期时间的不可变结构体(Struct):DateTime、DateTimeOffset、TimeSpan、DateOnly和TimeOnly。DateTime包含当前本地或UTC时间,以及最小和最大值;DateTimeOffset增加了时区偏移信息,适合跨时区操作。UTC是世界标准时间,而格林尼治标准时间(GMT)不稳定,已被更精确的UTC取代。DateTimeOffset和DateTime提供了转换为UTC和本地时间的方法,以及各种解析和格式化函数。
|
4月前
长安十二时辰与现代时间的互转
长安十二时辰与现代时间的互转
35 0
|
4月前
|
人工智能 IDE Java
CodeFuse代码优化实战:Java日期格式化时如何正确表示年份?
Java日期格式化时这里的坑你知道吗?一起来看正确用法!使用 CodeFuse 代码优化功能,可以帮你完美避坑,快来试试吧~
55 0
|
4月前
|
JavaScript 小程序 前端开发
JS将时间戳转换为刚刚、N分钟前、今天几点几分、昨天几点几分等表示法
JS将时间戳转换为刚刚、N分钟前、今天几点几分、昨天几点几分等表示法
|
4月前
|
算法 Java API
算法编程(二十四):日期之间隔几天
算法编程(二十四):日期之间隔几天
61 0
|
4月前
|
算法
算法编程(二十九):统计一致字符串的数目
算法编程(二十九):统计一致字符串的数目
67 0
|
安全 Java Linux
正确认识及掌握时间的用法
时间是一个相对地区而言的概念,因此有一个基准地区,就是本初子午线穿过的地区。了解世界时间相关的概念可以更好地协调全球人们的活动,便于跨越不同地区的时差。比如按照UTC时区划分算,洛杉矶和北京 之间的时间差异是16个小时, 但是一旦洛杉矶启用了夏令时两者之间的时间差异只有15个小时,神奇吗?
243 0
正确认识及掌握时间的用法
|
存储 C语言 索引
第十一周:结构类型
你会坚持下来的对吗?希望C语言不会成为你跨进编程世界的拦路虎,而是你的启蒙语言,梦的开始
76 0
|
前端开发
前端工作总结262-时间戳转换问题
前端工作总结262-时间戳转换问题
78 0