JDK8 官方日期、时间 API 笔记,可以看看官方是怎么介绍 date-time 相关工具类的、它的设计思想等
package cn.mrcode.date.jdk8;import java.time.*;import java.time.format.DateTimeFormatter;import java.time.temporal.TemporalAdjusters;import java.time.temporal.WeekFields;import java.util.ArrayList;import java.util.Date;import java.util.List;/*** @author mrcode* @date 2022*/public class Jdk8DateUtil {/*** 时区信息*/public static ZoneId zoneId = ZoneId.systemDefault();/*** 符合中国普遍国情的周数计算; 周 1 为一周的开始,只要有一天是下一年的开始,就认为是下一年的第一周* <pre>* 比如:2022-01-01 和 2021-12-30 都认为是 2022年第一周* </pre>*/public final static WeekFields WEEK_FIELDS_CN = WeekFields.of(DayOfWeek.MONDAY, 1);/*** 周一为一周的开始,但是必须大于等于 4 天在下一年,才认为是下一年的第一周*/public final static WeekFields WEEK_FIELDS_ISO = WeekFields.ISO;/*** 年月日 -> 年月日 00:00:00** @param date* @return*/public static Date localDateToDate(LocalDate date) {return Date.from(date.atStartOfDay(zoneId).toInstant());}public static LocalDate dateToLocalDate(Date date) {return date.toInstant().atZone(zoneId).toLocalDate();}/*** 按指定格式解析为 LocalDate** @param dateStr 日期字符串* @param pattern 如 yyyy-MM-dd,可以自己实现定义好 DateTimeFormatter 加快速度,并且还可以使用 format, fmt.format(day)* @return*/public static LocalDate localDateParse(String dateStr, String pattern) {final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(pattern);return LocalDate.parse(dateStr, dateTimeFormatter);}/*** 获取某月第一天** @param date* @return*/public static LocalDate firstDayOfMonth(LocalDate date) {return date.with(TemporalAdjusters.firstDayOfMonth());}/*** 获取某月的最后一天** @param date* @return*/public static LocalDate lastDayOfMonth(LocalDate date) {return date.with(TemporalAdjusters.lastDayOfMonth());}/*** 获取下个月的第一天** @return*/public static LocalDate firstDayOfNextMonth(LocalDate date) {return date.with(TemporalAdjusters.firstDayOfNextMonth());}/*** 获取某月的第一天** @param date* @return*/public static LocalDate firstDayOfMonth(YearMonth date) {return date.atDay(1);}/*** 获取某月最后一天** @param date* @return*/public static LocalDate lastDayOfMonth(YearMonth date) {return date.atDay(date.lengthOfMonth());}public static Date localDateTimeToDate(LocalDateTime time) {return Date.from(time.atZone(zoneId).toInstant());}/*** 获取该小时的开始时间,2021-08-30 01:05:00,05 -> 2021-08-30 01:00:00,000** @param time* @return*/public static LocalDateTime beginOfHour(LocalDateTime time) {return LocalDateTime.of(time.toLocalDate(), LocalTime.of(time.getHour(), 0, 0));}public static LocalDateTime dateToLocalDateTime(Date date) {return date.toInstant().atZone(zoneId).toLocalDateTime();}/*** 获取两个时间范围内的每一天的时间,包含 startDay 和 endDay** @param startDay* @param endDay* @return*/public static List<LocalDate> getDays(LocalDate startDay, LocalDate endDay) {List<LocalDate> result = new ArrayList<>();LocalDate currentDay = startDay;while (currentDay.isBefore(endDay) || currentDay.isEqual(endDay)) {result.add(currentDay);currentDay = currentDay.plusDays(1);}return result;}/*** 该时间是第几周?所属年份是哪一年?比如 2021-12-31 号所属年则是 2022 年,第一周** @param date* @return*/public static Integer weekBasedYear(LocalDate date) {return date.get(WEEK_FIELDS_CN.weekBasedYear());}/*** 第几周:该时间是第几周?比如 2021-12-31 号所属周是第 1 周,但是所属年确是第 2022 年** @param date* @return*/public static Integer weekOfWeekBasedYear(LocalDate date) {return date.get(WEEK_FIELDS_CN.weekOfWeekBasedYear());}/*** 该时间属于哪一年的第几周* <pre>* 2021-12-27 ~ 2022-01-02 都属于 202201,2022 年第 1 周* </pre>** @param date* @return*/public static int dateToWeekInt(LocalDate date) {Integer weekOfWeekBasedYear = Jdk8DateUtil.weekOfWeekBasedYear(date);return Integer.valueOf(Jdk8DateUtil.weekBasedYear(date) + "" +(weekOfWeekBasedYear < 10 ? 0 + "" + weekOfWeekBasedYear : weekOfWeekBasedYear));}}
Date 和 JDK8 日期互转可以参考上面的语法,其他的应该也是类似的 API;
系统默认时区的设置方式
关于上面为什么使用 ZoneId.systemDefault() 默认时区而不是使用 ZoneOffset._ofHours_(8); 自定义时区的方式:因为我觉得在全局设置时区会比在某一个里面写死时区会更好。原因有如下:
- 如果不设置系统的默认时区,在获取系统默认时区的时候就会获取到系统上的时区信息,假设系统上是 UTC 时区,你程序要求是东八区,那么就会导致问题出现
- 假设要统一修改时区,这个时候如果散落在各个类里面的时区配置就是灾难
- 时区涉及到了获取当前系统时间的表现形式,和第一条类似,如果设置了默认时区,
new Date()的时候会将系统时间转化为默认时区的时间
在系统启动时,如果使用 SpringBoot 程序,在 main 方法里面 SpringApplication.run() 方法前调用默认时区的设置,如下代码
import java.util.TimeZone;// 使用时区名称TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"));// 或则使用偏移量TimeZone.setDefault(TimeZone.getTimeZone(ZoneOffset.ofHours(8)));
:::warning 需要注意的是:默认时区只能被设置一次,连续调用改变成其他的时区,是不会生效的 :::
