JDK8 官方日期、时间 API 笔记,可以看看官方是怎么介绍 date-time 相关工具类的、它的设计思想等

  1. package cn.mrcode.date.jdk8;
  2. import java.time.*;
  3. import java.time.format.DateTimeFormatter;
  4. import java.time.temporal.TemporalAdjusters;
  5. import java.time.temporal.WeekFields;
  6. import java.util.ArrayList;
  7. import java.util.Date;
  8. import java.util.List;
  9. /**
  10. * @author mrcode
  11. * @date 2022
  12. */
  13. public class Jdk8DateUtil {
  14. /**
  15. * 时区信息
  16. */
  17. public static ZoneId zoneId = ZoneId.systemDefault();
  18. /**
  19. * 符合中国普遍国情的周数计算; 周 1 为一周的开始,只要有一天是下一年的开始,就认为是下一年的第一周
  20. * <pre>
  21. * 比如:2022-01-01 和 2021-12-30 都认为是 2022年第一周
  22. * </pre>
  23. */
  24. public final static WeekFields WEEK_FIELDS_CN = WeekFields.of(DayOfWeek.MONDAY, 1);
  25. /**
  26. * 周一为一周的开始,但是必须大于等于 4 天在下一年,才认为是下一年的第一周
  27. */
  28. public final static WeekFields WEEK_FIELDS_ISO = WeekFields.ISO;
  29. /**
  30. * 年月日 -> 年月日 00:00:00
  31. *
  32. * @param date
  33. * @return
  34. */
  35. public static Date localDateToDate(LocalDate date) {
  36. return Date.from(date.atStartOfDay(zoneId).toInstant());
  37. }
  38. public static LocalDate dateToLocalDate(Date date) {
  39. return date.toInstant().atZone(zoneId).toLocalDate();
  40. }
  41. /**
  42. * 按指定格式解析为 LocalDate
  43. *
  44. * @param dateStr 日期字符串
  45. * @param pattern 如 yyyy-MM-dd,可以自己实现定义好 DateTimeFormatter 加快速度,并且还可以使用 format, fmt.format(day)
  46. * @return
  47. */
  48. public static LocalDate localDateParse(String dateStr, String pattern) {
  49. final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(pattern);
  50. return LocalDate.parse(dateStr, dateTimeFormatter);
  51. }
  52. /**
  53. * 获取某月第一天
  54. *
  55. * @param date
  56. * @return
  57. */
  58. public static LocalDate firstDayOfMonth(LocalDate date) {
  59. return date.with(TemporalAdjusters.firstDayOfMonth());
  60. }
  61. /**
  62. * 获取某月的最后一天
  63. *
  64. * @param date
  65. * @return
  66. */
  67. public static LocalDate lastDayOfMonth(LocalDate date) {
  68. return date.with(TemporalAdjusters.lastDayOfMonth());
  69. }
  70. /**
  71. * 获取下个月的第一天
  72. *
  73. * @return
  74. */
  75. public static LocalDate firstDayOfNextMonth(LocalDate date) {
  76. return date.with(TemporalAdjusters.firstDayOfNextMonth());
  77. }
  78. /**
  79. * 获取某月的第一天
  80. *
  81. * @param date
  82. * @return
  83. */
  84. public static LocalDate firstDayOfMonth(YearMonth date) {
  85. return date.atDay(1);
  86. }
  87. /**
  88. * 获取某月最后一天
  89. *
  90. * @param date
  91. * @return
  92. */
  93. public static LocalDate lastDayOfMonth(YearMonth date) {
  94. return date.atDay(date.lengthOfMonth());
  95. }
  96. public static Date localDateTimeToDate(LocalDateTime time) {
  97. return Date.from(time.atZone(zoneId).toInstant());
  98. }
  99. /**
  100. * 获取该小时的开始时间,2021-08-30 01:05:00,05 -> 2021-08-30 01:00:00,000
  101. *
  102. * @param time
  103. * @return
  104. */
  105. public static LocalDateTime beginOfHour(LocalDateTime time) {
  106. return LocalDateTime.of(time.toLocalDate(), LocalTime.of(time.getHour(), 0, 0));
  107. }
  108. public static LocalDateTime dateToLocalDateTime(Date date) {
  109. return date.toInstant().atZone(zoneId).toLocalDateTime();
  110. }
  111. /**
  112. * 获取两个时间范围内的每一天的时间,包含 startDay 和 endDay
  113. *
  114. * @param startDay
  115. * @param endDay
  116. * @return
  117. */
  118. public static List<LocalDate> getDays(LocalDate startDay, LocalDate endDay) {
  119. List<LocalDate> result = new ArrayList<>();
  120. LocalDate currentDay = startDay;
  121. while (currentDay.isBefore(endDay) || currentDay.isEqual(endDay)) {
  122. result.add(currentDay);
  123. currentDay = currentDay.plusDays(1);
  124. }
  125. return result;
  126. }
  127. /**
  128. * 该时间是第几周?所属年份是哪一年?比如 2021-12-31 号所属年则是 2022 年,第一周
  129. *
  130. * @param date
  131. * @return
  132. */
  133. public static Integer weekBasedYear(LocalDate date) {
  134. return date.get(WEEK_FIELDS_CN.weekBasedYear());
  135. }
  136. /**
  137. * 第几周:该时间是第几周?比如 2021-12-31 号所属周是第 1 周,但是所属年确是第 2022 年
  138. *
  139. * @param date
  140. * @return
  141. */
  142. public static Integer weekOfWeekBasedYear(LocalDate date) {
  143. return date.get(WEEK_FIELDS_CN.weekOfWeekBasedYear());
  144. }
  145. /**
  146. * 该时间属于哪一年的第几周
  147. * <pre>
  148. * 2021-12-27 ~ 2022-01-02 都属于 202201,2022 年第 1 周
  149. * </pre>
  150. *
  151. * @param date
  152. * @return
  153. */
  154. public static int dateToWeekInt(LocalDate date) {
  155. Integer weekOfWeekBasedYear = Jdk8DateUtil.weekOfWeekBasedYear(date);
  156. return Integer.valueOf(Jdk8DateUtil.weekBasedYear(date) + "" +
  157. (weekOfWeekBasedYear < 10 ? 0 + "" + weekOfWeekBasedYear : weekOfWeekBasedYear));
  158. }
  159. }

Date 和 JDK8 日期互转可以参考上面的语法,其他的应该也是类似的 API;

系统默认时区的设置方式

关于上面为什么使用 ZoneId.systemDefault() 默认时区而不是使用 ZoneOffset._ofHours_(8); 自定义时区的方式:因为我觉得在全局设置时区会比在某一个里面写死时区会更好。原因有如下:

  1. 如果不设置系统的默认时区,在获取系统默认时区的时候就会获取到系统上的时区信息,假设系统上是 UTC 时区,你程序要求是东八区,那么就会导致问题出现
  2. 假设要统一修改时区,这个时候如果散落在各个类里面的时区配置就是灾难
  3. 时区涉及到了获取当前系统时间的表现形式,和第一条类似,如果设置了默认时区,new Date()的时候会将系统时间转化为默认时区的时间

在系统启动时,如果使用 SpringBoot 程序,在 main 方法里面 SpringApplication.run() 方法前调用默认时区的设置,如下代码

  1. import java.util.TimeZone;
  2. // 使用时区名称
  3. TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"));
  4. // 或则使用偏移量
  5. TimeZone.setDefault(TimeZone.getTimeZone(ZoneOffset.ofHours(8)));

:::warning 需要注意的是:默认时区只能被设置一次,连续调用改变成其他的时区,是不会生效的 :::