在过去的Java版本中,日期和时间的处理主要依赖于java.util.Date和java.util.Calendar类,然而随着业务系统的复杂以及技术层面的提升,这些传统的日期时间类暴露出了若干显著的不足之处。随着Java8的发布,其引入了一套全新的日期时间API,彻底改变了我们处理日期和时间的方式。
相比较Java8中新引入的java.time包下的时间处理类,传统的日期时间处理类在易用性,线程安全,不支持市时区等缺点。
Date currentDate = new Date(); // 输出原始的日期时间,通常不是人类可读格式 Fri Mar 08 03:13:47 CST 2024System.out.println(currentDate);// 要改变日期的某个部分,必须先将其转换为 Calendar,然后设置 Calendar calendar = Calendar.getInstance(); calendar.setTime(currentDate); // 修改日期的天数 calendar.add(Calendar.DAY_OF_MONTH, 1);
Date类内部维护了一个 long 类型的瞬时值,当调用如setTime()方法来更新这个瞬时值时,不同的线程同时调用就会互相覆盖彼此的值,造成数据不一致。
Calendar类不仅包含了日期和时间信息,还有一系列内部状态变量,如年、月、日、小时、分钟、秒等。Calendar类的方法通常会修改这些内部状态,例如 add()、set() 等。在多线程环境下,若多个线程尝试同时修改同一个 Calendar 实例,也会导致不可预期的结果。
SimpleDateFormat类在执行格式化和解析日期时间操作时,内部会维护一个 Calendar对象以及其他一些状态变量。在 format() 或 parse() 方法执行过程中,这些状态会被更新以完成格式转换。并且SimpleDateFormat中的方法并非原子操作,因此在多线程并发调用时,可能在一个线程还未完成整个操作时就被另一个线程打断,导致错误的日期时间处理结果。
Java8中引入的LocalDate,LocalTime,LocalDateTime这几个位于java.time下的类克服了上述传统类别的局限性,提供了更强大、直观和精准的日期时间处理能力,成为现代Java开发中处理日期时间首选的工具类。相比较传统的日期时间类,具备以下显著优势:
java.time下主要有如下一些关键类:
这些类共同构成了一个强大、灵活且易于使用的日期时间处理体系,大大改善了Java在处理日期时间问题时的效率和准确性。接下来我们在使用上分别介绍这些类,以及使用他们的方式,感受他们的强大。
LocalTime localTime = LocalTime.now(); System.out.println("localTime:"+localTime); LocalDate localDate = LocalDate.now(); System.out.println("localDate:"+localDate); LocalDateTime localDateTime = LocalDateTime.now(); System.out.println("localDateTime:"+localDateTime);
输出为:
localTime:15:28:45.241181localDate:2024-03-11localDateTime:2024-03-11T15:28:45.260655
针对LocalTime,LocalDateTime获取当前时刻默认会带有毫秒,如果不需要毫秒的话,可以通过设置纳秒为0 保留秒 1秒 = 十亿纳秒 。例如:
LocalTime localTime = LocalTime.now().withNano(0);System.out.println("localTime:"+localTime); LocalDateTime localDateTime = LocalDateTime.now().withNano(0); System.out.println("localDateTime:"+localDateTime);
输出为:
localTime:15:32:31localDateTime:2024-03-11T15:32:31
而对于LocalDateTime获取当前日期,默认toString会带有T分隔日期和时间,在项目中,可以通过全局序列化,进行统一的时间格式输出为 yyyy-MM-dd HH:mm:ss。但是一般不建议这么干,毕竟改变全局序列化配置,建议不使用toString,可以使用DateTimeFormatter进行自定义转换。
// of方法直接传递对应的年、月、日 LocalDate localDate = LocalDate.of(2024, 3, 11); System.out.println("localDate:"+localDate); localDate = LocalDate.of(2024, Month.MARCH, 11); System.out.println("localDate:"+localDate); localDate = LocalDate.ofYearDay(2024, 71); System.out.println("localDate:"+localDate); // 北京时间对应的时区 ZoneId chinaTimeZone = ZoneId.of("Asia/Shanghai"); // 创建一个 Instant,这里使用当前时间的 InstantInstant instant = Instant.now(); localDate = LocalDate.ofInstant(instant, chinaTimeZone); System.out.println("localDate:"+localDate); // 使用ofEpochDay()方法,则EpochDay为从公元1970年1月1日(Unix纪元)开始的第多少天 localDate = LocalDate.ofEpochDay(LocalDate.now().toEpochDay()); System.out.println("localDate:"+localDate); LocalTime localTime = LocalTime.of(1, 30); System.out.println("localTime:"+localTime); localTime = LocalTime.of(1, 30, 30); System.out.println("localTime:"+localTime); localTime = LocalTime.ofInstant(instant, chinaTimeZone); System.out.println("localTime:"+localTime); // 根据一天中的总秒数构建时间localTime = LocalTime.ofSecondOfDay(localTime.toSecondOfDay()); System.out.println("localTime:"+localTime); LocalDateTime localDateTime = LocalDateTime.of(2024, 3, 11, 1, 30, 30); System.out.println("localDateTime:"+localDateTime); localDateTime = LocalDateTime.of(2024, Month.MARCH, 11, 1, 30, 30); System.out.println("localDateTime:"+localDateTime); // 使用LocalDate和LocalTime组合构造 localDateTime = LocalDateTime.of(localDate, localTime); System.out.println("localDateTime:"+localDateTime); localDateTime = LocalDateTime.ofInstant(instant, chinaTimeZone); System.out.println("localDateTime:"+localDateTime);
输出为:
localDate:2024-03-11localDate:2024-03-11localDate:2024-03-11localDate:2024-03-11localDate:2024-03-11localTime:01:30localTime:01:30:30localTime:16:41:37.893310localTime:16:41:37localDateTime:2024-03-11T01:30:30localDateTime:2024-03-11T01:30:30localDateTime:2024-03-11T16:41:37localDateTime:2024-03-11T16:41:37.893310
from()方法将TemporalAccessor类型(如ZonedDateTime)转换为相对应的日期或者时间。TemporalAccessor接口是一个用于读取或写入日期、时间或者日期时间的通用接口。
// 创建一个ZonedDateTime实例 ZonedDateTime zonedDateTime = ZonedDateTime.now(ZoneId.of("Asia/Shanghai")); LocalTime localTime = LocalTime.from(zonedDateTime); System.out.println("localTime:"+localTime); LocalDate localDate = LocalDate.from(zonedDateTime); System.out.println("localDate:"+localDate); LocalDateTime localDateTime = LocalDateTime.from(zonedDateTime); System.out.println("localDateTime:"+localDateTime);
输出为:
localTime:17:18:27.942911localDate:2024-03-11localDateTime:2024-03-11T17:18:27.942911
将字符串按照指定格式(可选)解析为对应的日期时间类。
LocalTime localTime = LocalTime.parse("17:25:30"); System.out.println("localTime:"+localTime); localTime = LocalTime.parse("17:25:30", DateTimeFormatter.ofPattern("HH:mm:ss")); System.out.println("localTime:"+localTime); LocalDate localDate = LocalDate.parse("2024-03-11"); System.out.println("localDate:"+localDate); localDate = LocalDate.parse("2024/03/11", DateTimeFormatter.ofPattern("yyyy/MM/dd")); System.out.println("localDate:"+localDate); LocalDateTime localDateTime = LocalDateTime.parse("2024-03-11T17:25:30"); System.out.println("localDateTime:"+localDateTime); localDateTime = LocalDateTime.parse("2024/03/11 17:25:30", DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss")); System.out.println("localDateTime:"+localDateTime);
输出为:
localTime:17:25:30localTime:17:25:30localDate:2024-03-11localDate:2024-03-11localDateTime:2024-03-11T17:25:30localDateTime:2024-03-11T17:25:30
// LocalTime + LocalDate = LocalDateTimeLocalDateTime localDateTime = LocalTime.now().atDate(LocalDate.now());System.out.println("localDateTime:"+localDateTime);localDateTime = LocalDate.now().atTime(LocalTime.now());System.out.println("localDateTime:"+localDateTime);localDateTime = LocalDateTime.of(LocalDate.now(), LocalTime.now());System.out.println("localDateTime:"+localDateTime);// LocalDateTime 转 LocalDateLocalDate localDate = LocalDateTime.now().toLocalDate();System.out.println("localDate:"+localDate);// LocalDateTime 转 LocalTimeLocalTime localTime = LocalDateTime.now().toLocalTime();System.out.println("localTime:"+localTime);// 获取今日开始时间 2024-03-11T00:00localDateTime = LocalDate.now().atStartOfDay();System.out.println("localDateTime:"+localDateTime);// 获取今日开始时间 2024-03-11T00:00LocalDateTime startDateTime = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);System.out.println("startDateTime:"+ startDateTime);// 获取今日结束时间 2024-03-11T23:59:59.999999999LocalDateTime endDateTime = LocalDateTime.of(LocalDate.now(), LocalTime.MAX);System.out.println("endDateTime:"+ endDateTime);
输出为:
localDateTime:2024-03-11T18:04:22.348539localDateTime:2024-03-11T18:04:22.370562localDateTime:2024-03-11T18:04:22.370768localDate:2024-03-11localTime:18:04:22.371062localDateTime:2024-03-11T00:00startDateTime:2024-03-11T00:00endDateTime:2024-03-11T23:59:59.999999999
主要使用format 和 parse 进行转换,使用方法基本相同。使用 DateTimeFormatter.ofPattern() 定义时间格式,再进行转换。DateTimeFormatter线程安全。
// LocalTime 转 String 自定义输出格式,例如:**时**分**秒 该转化的 00 不会被省略String localTimeStr = LocalTime.now().format(DateTimeFormatter.ofPattern("HH时mm分ss秒"));System.out.println("localTimeStr:"+localTimeStr);String localDateStr = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));System.out.println("localDateStr:"+localDateStr);// LocalDateTime 转 StringString localDateTimeStr = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));System.out.println("localDateTimeStr:"+localDateTimeStr);// String 转 LocalDateTimeLocalDateTime localDateTime = LocalDateTime.parse("2023-04-14 15:59:40", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));System.out.println("localDateTime:"+localDateTime);
输出结果:
localTimeStr:19时02分58秒localDateStr:2024-03-11localDateTimeStr:2024-03-11 19:02:58localDateTime:2023-04-14T15:59:40
// Date 转 LocalDateTimeDate currentDate = new Date();// 转换为Instant Instant instant = currentDate.toInstant();// 通过zoneId设置时区(这里使用系统时区),转换为带带时区的 ZoneDateTimeZonedDateTime zonedDateTime = instant.atZone(ZoneId.systemDefault());// 然后通过ZonedDateTime转换为LocalDateTimeLocalDateTime localDateTime = zonedDateTime.toLocalDateTime();System.out.println("localDateTime:"+localDateTime);// LocalDateTime 转 Date,同理也是通过ZonedDateTime转换为DateDate localDateTimeToDate = Date.from(LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant());System.out.println(localDateTimeToDate);// Date转LocalDate 同理 LocalDateTime转换LocalDate localDate = currentDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();System.out.println("localDate:"+localDate);// LocalDate 转 Date 需要先将 LocalDate 转 LocalDateTimeDate localDateToDate = Date.from(LocalDate.now().atStartOfDay(ZoneId.systemDefault()).toInstant());
这里介绍一下ZoneId。java.time.ZoneId是Java 8中java.time包中用于表示时区的类。时区是地球上的地理位置,用于确定在该位置观察太阳升落以及规定当地居民生活和商业活动时间的标准时间。ZoneId使用IANA时区数据库提供的时区标识符,这个标识符是唯一的,这些标识符通常是地区/城市对,例如“Asia/Shanghai”代表中国上海所在的时区,America/New_York代表美国纽约城市。其实例获取有两种方式:
时间戳转换。
long timeMillis = System.currentTimeMillis();// 时间戳(Long) 转 LocalDateTimeLocalDateTime localDateTime = Instant.ofEpochMilli(timeMillis).atZone(ZoneOffset.ofHours(8)).toLocalDateTime();System.out.println("localDateTime:"+localDateTime);localDateTime = Instant.ofEpochMilli(timeMillis).atZone(ZoneId.systemDefault()).toLocalDateTime();System.out.println("localDateTime:"+localDateTime);// LocalDateTime 转 时间戳(Long) 秒级long localDateTimeToSecond = LocalDateTime.now().toEpochSecond(ZoneOffset.ofHours(8));System.out.println("localDateTimeToSecond:"+ localDateTimeToSecond);// LocalDateTime 转 时间戳(Long) 毫秒级long localDateTimeToMilliSecond = LocalDateTime.now().toInstant(ZoneOffset.ofHours(8)).toEpochMilli();System.out.println("localDateTimeToMilliSecond:"+ localDateTimeToMilliSecond);// 时间戳(Long) 转 LocalDateLocalDate localDate = Instant.ofEpochMilli(timeMillis).atZone(ZoneOffset.ofHours(8)).toLocalDate();System.out.println("localDate:"+ localDate);// LocalDate 转 时间戳(Long) 秒级long localDateToSecond = LocalDate.now().atStartOfDay().toEpochSecond(ZoneOffset.ofHours(8));System.out.println("localDateToSecond:"+ localDateToSecond);// LocalDate 转 时间戳(Long) 毫秒级long localDateToMilliSecond = LocalDate.now().atStartOfDay().toInstant(ZoneOffset.ofHours(8)).toEpochMilli();System.out.println("localDateToMilliSecond:"+ localDateToMilliSecond);
输出结果为:
localDateTime:2024-03-11T19:37:02.335localDateTime:2024-03-11T19:37:02.335localDateTimeToSecond:1710157022localDateTimeToMilliSecond:1710157022365localDate:2024-03-11localDateToSecond:1710086400localDateToMilliSecond:1710086400000
java.time.ZoneOffset是Java8中java.time包内用来表示时区偏移量的类,它表示的是格林尼治标准时间或协调世界时间(UTC)基础上的固定偏移量。每一个时区都可以通过一个或多个偏移量来表示,比如“+02:00”表示比UTC时间快两个小时的时区偏移。其实例创建有如下方式:
java.time 包中日期时间类(如 LocalDateTime、LocalDate 和 LocalTime)可以通过plusXxx() 和 minusXxx() 方法,用于对日期时间对象进行加减操作,以增加或减少指定的时间或日期单位。
1、LocalDateTime 加减:
// LocalDateTime 加减LocalDateTime localDateTime = LocalDateTime.now();// 以下为增加时、分、秒LocalDateTime plusLocalDateTime = localDateTime.plusHours(1).plusMinutes(1).plusSeconds(1);System.out.println("plusLocalDateTime:"+plusLocalDateTime);plusLocalDateTime = localDateTime.plus(1, ChronoUnit.HOURS).plus(1, ChronoUnit.MINUTES).plus(1, ChronoUnit.SECONDS);System.out.println("plusLocalDateTime:"+plusLocalDateTime);plusLocalDateTime = localDateTime.plus(Duration.ofHours(1)).plus(Duration.of(1, ChronoUnit.MINUTES)).plus(Duration.of(1, ChronoUnit.SECONDS));System.out.println("plusLocalDateTime:"+plusLocalDateTime);// 以下为增加年、月、日plusLocalDateTime = localDateTime.plusYears(1).plusMonths(1).plusWeeks(1).plusDays(1);System.out.println("plusLocalDateTime:"+plusLocalDateTime);plusLocalDateTime = localDateTime.plus(1, ChronoUnit.YEARS).plus(1, ChronoUnit.MONTHS).plus(1, ChronoUnit.WEEKS).plus(1, ChronoUnit.DAYS);System.out.println("plusLocalDateTime:"+plusLocalDateTime);plusLocalDateTime = localDateTime.plus(Duration.of(1, ChronoUnit.YEARS)).plus(Duration.of(1, ChronoUnit.MONTHS)).plus(Duration.of(1, ChronoUnit.WEEKS)).plus(Duration.ofDays(1));System.out.println("plusLocalDateTime:"+plusLocalDateTime);plusLocalDateTime = localDateTime.plus(Period.ofYears(1)).plus(Period.ofMonths(1)).plus(Period.ofWeeks(1)).plus(Period.ofDays(1));System.out.println("plusLocalDateTime:"+plusLocalDateTime);// 以下为减少时、分、秒LocalDateTime minusLocalDateTime = localDateTime.minusHours(1).minusMinutes(1).minusSeconds(1);System.out.println("minusLocalDateTime:"+minusLocalDateTime);minusLocalDateTime = localDateTime.minus(1, ChronoUnit.HOURS).minus(1, ChronoUnit.MINUTES).minus(1, ChronoUnit.SECONDS);System.out.println("minusLocalDateTime:"+minusLocalDateTime);minusLocalDateTime = localDateTime.minus(Duration.ofHours(1)).minus(Duration.of(1, ChronoUnit.MINUTES)).minus(Duration.of(1, ChronoUnit.SECONDS));System.out.println("minusLocalDateTime:"+minusLocalDateTime);// 以下为减少年、月、日minusLocalDateTime = localDateTime.minusYears(1).minusMonths(1).minusWeeks(1).minusDays(1);System.out.println("minusLocalDateTime:"+minusLocalDateTime);minusLocalDateTime = localDateTime.minus(1, ChronoUnit.YEARS).minus(1, ChronoUnit.MONTHS).minus(1, ChronoUnit.WEEKS).minus(1, ChronoUnit.DAYS);System.out.println("minusLocalDateTime:"+minusLocalDateTime);minusLocalDateTime = localDateTime.minus(Duration.of(1, ChronoUnit.YEARS)).minus(Duration.of(1, ChronoUnit.MONTHS)).minus(Duration.of(1, ChronoUnit.WEEKS)).minus(Duration.ofDays(1));System.out.println("minusLocalDateTime:"+minusLocalDateTime);minusLocalDateTime = localDateTime.minus(Period.ofYears(1)).minus(Period.ofMonths(1)).minus(Period.ofWeeks(1)).minus(Period.ofDays(1));System.out.println("plusLocalDateTime:"+minusLocalDateTime);
2、LocalDate 加减:
// LocalDate 加减LocalDate localDate = LocalDate.now();LocalDate plusLocalDate = localDate.plusYears(1).plusMonths(1).plusWeeks(1).plusDays(1);System.out.println("plusLocalDate:"+plusLocalDate);plusLocalDate = localDate.plus(1, ChronoUnit.YEARS).plus(1, ChronoUnit.MONTHS).plus(1, ChronoUnit.WEEKS).plus(1, ChronoUnit.DAYS);System.out.println("plusLocalDate:"+plusLocalDate);plusLocalDate = localDate.plus(Duration.of(1, ChronoUnit.YEARS)).plus(Duration.of(1, ChronoUnit.MONTHS)).plus(Duration.of(1, ChronoUnit.WEEKS)).plus(Duration.ofDays(1));System.out.println("plusLocalDate:"+plusLocalDate);plusLocalDate = localDate.plus(Period.ofYears(1)).plus(Period.ofMonths(1)).plus(Period.ofWeeks(1)).plus(Period.ofDays(1));System.out.println("plusLocalDate:"+plusLocalDate);LocalDate minusLocalDate = localDate.minusYears(1).minusMonths(1).minusWeeks(1).minusDays(1);System.out.println("minusLocalDate:"+minusLocalDate);minusLocalDate = localDate.minus(1, ChronoUnit.YEARS).minus(1, ChronoUnit.MONTHS).minus(1, ChronoUnit.WEEKS).minus(1, ChronoUnit.DAYS);System.out.println("minusLocalDate:"+minusLocalDate);minusLocalDate = localDate.minus(Duration.of(1, ChronoUnit.YEARS)).minus(Duration.of(1, ChronoUnit.MONTHS)).minus(Duration.of(1, ChronoUnit.WEEKS)).minus(Duration.ofDays(1));System.out.println("minusLocalDate:"+minusLocalDate);minusLocalDate = localDate.minus(Period.ofYears(1)).minus(Period.ofMonths(1)).minus(Period.ofWeeks(1)).minus(Period.ofDays(1));System.out.println("minusLocalDate:"+minusLocalDate);
3、LocalTime 加减:
// LocalTime 加减LocalTime localTime = LocalTime.now();LocalTime plusLocalTime = localTime.plusHours(1).plusMinutes(1).plusSeconds(1);System.out.println("plusLocalTime:"+plusLocalTime);plusLocalTime = localTime.plus(1, ChronoUnit.HOURS).plus(1, ChronoUnit.MINUTES).plus(1, ChronoUnit.SECONDS);System.out.println("plusLocalTime:"+plusLocalTime);plusLocalTime = localTime.plus(Duration.ofHours(1)).plus(Duration.of(1, ChronoUnit.MINUTES)).plus(Duration.of(1, ChronoUnit.SECONDS));System.out.println("plusLocalTime:"+plusLocalTime);LocalTime minusLocalTime = localTime.minusHours(1).minusMinutes(1).minusSeconds(1);System.out.println("minusLocalTime:"+minusLocalTime);minusLocalTime = localTime.minus(1, ChronoUnit.HOURS).minus(1, ChronoUnit.MINUTES).minus(1, ChronoUnit.SECONDS);System.out.println("minusLocalDateTime:"+minusLocalTime);minusLocalTime = localTime.minus(Duration.ofHours(1)).minus(Duration.of(1, ChronoUnit.MINUTES)).minus(Duration.of(1, ChronoUnit.SECONDS));System.out.println("minusLocalDateTime:"+minusLocalTime);
LocalDate、LocalTime、LocalDateTime、ZonedDateTime可以通过相对应的withXxx()方法修改指定的值。
1、LocalDate:
LocalDate localDate = LocalDate.of(2024, 3, 12);LocalDate newDate = localDate.withYear(2025).withMonth(4).with(ChronoField.DAY_OF_MONTH, 13);System.out.println("newDate:"+newDate);
2、LocalTime:
LocalTime localTime = LocalTime.of(17, 25, 30);LocalTime newTime = localTime.withHour(18).withMinute(26).with(ChronoField.SECOND_OF_MINUTE, 31);System.out.println("newTime:"+newTime);
3、LocalDateTime:
LocalDateTime localDateTime = LocalDateTime.of(2024, 3, 12, 17, 25, 30);LocalDateTime newDateTime = localDateTime.withYear(2025).withMonth(4).with(ChronoField.DAY_OF_MONTH, 13).withHour(18).withMinute(26).with(ChronoField.SECOND_OF_MINUTE, 31);System.out.println("newDateTime:"+ newDateTime);
4、ZonedDateTime:
ZonedDateTime zonedDateTime = ZonedDateTime.of(2024, 3, 12, 17, 25, 30, 0, ZoneId.of("Europe/London"));ZonedDateTime newZonedDateTime = zonedDateTime.withZoneSameInstant(ZoneId.of("America/New_York"));System.out.println("newZonedDateTime:"+ newZonedDateTime);
除此之外,调整日期时间还可以通过TemporalAdjusters,TemporalAdjuster 是一个函数式接口,用于根据给定的规则调整日期时间对象。Java8的 java.time.temporal 包中预定义了一系列常用的 TemporalAdjuster 实现,例如获取下一个工作日、月初、月末等。
LocalDate date = LocalDate.of(2024, 3, 11);// 下一个工作日LocalDate nextWorkingDay = date.with(TemporalAdjusters.next(DayOfWeek.MONDAY)); // 如果11号不是周一,则返回下一个周一的日期// 下一个月的第一天LocalDate firstDayNextMonth = date.with(TemporalAdjusters.firstDayOfMonth()); // 返回4月1日// 当月的最后一个工作日LocalDate lastWorkingDay = date.with(TemporalAdjusters.lastInMonth(DayOfWeek.FRIDAY)); // 返回3月最后一个周五的日期// 自定义 TemporalAdjusterTemporalAdjuster adjuster = temporal -> { return temporal.plusDays(10).with(TemporalAdjusters.lastDayOfMonth());};LocalDate tenthDayNextMonthEnd = date.with(adjuster); // 返回4月最后一个日期,前提是先加10天
在Java8及其以后版本的日期时间API中,isBefore() 和 isAfter() 方法是 java.time 包中的 LocalDate、LocalTime、LocalDateTime、ZonedDateTime 等日期时间类所共有的方法,用于比较两个日期时间对象的先后顺序。
isBefore():
LocalDate date1 = LocalDate.of(2024, 3, 11);LocalDate date2 = LocalDate.of(2024, 3, 12);boolean isEarlier = date1.isBefore(date2); // 返回 true,因为 date1 在 date2 之前
isAfter():
LocalDateTime time1 = LocalDateTime.of(2024, 3, 11, 10, 0);LocalDateTime time2 = LocalDateTime.of(2024, 3, 11, 9, 0);boolean isLater = time1.isAfter(time2); // 返回 true,因为 time1 在 time2 之后
compareTo()在Java 8的 java.time 包中,大部分日期时间类如 LocalDate、LocalTime、LocalDateTime、ZonedDateTime 都实现了 Comparable 接口,从而可以直接使用 compareTo() 方法进行比较。compareTo() 方法用于比较两个日期时间对象的先后顺序,返回值含义如下:
LocalDate date1 = LocalDate.of(2024, 3, 11);LocalDate date2 = LocalDate.of(2024, 3, 12);int comparisonResult = date1.compareTo(date2);if (comparisonResult < 0) { System.out.println("date1 is before date2");} else if (comparisonResult > 0) { System.out.println("date1 is after date2");} else { System.out.println("date1 is equal to date2");}LocalDateTime dateTime1 = LocalDateTime.of(2024, 3, 11, 10, 30);LocalDateTime dateTime2 = LocalDateTime.of(2024, 3, 11, 11, 00);int timeComparisonResult = dateTime1.compareTo(dateTime2);
在Java8的 java.time 包中,各个日期时间类如 LocalDate、LocalTime、LocalDateTime 提供了一系列 get 方法,用于获取特定字段的值。
获取日期中的特定字段:
LocalDate date = LocalDate.of(2024, 3, 11);int dayOfMonth = date.getDayOfMonth(); // 获取当月的第几天,此处返回11int monthValue = date.getMonthValue(); // 获取月份值,此处返回3Month month = date.getMonth(); // 获取Month枚举,此处返回Marchint year = date.getYear(); // 获取年份,此处返回2024
对于时间部分,类似地可以获取小时、分钟、秒和纳秒:
LocalTime time = LocalTime.of(19, 30, 45);int hour = time.getHour(); // 获取小时数,此处返回10int minute = time.getMinute(); // 获取分钟数,此处返回30int second = time.getSecond(); // 获取秒数,此处返回45int nano = time.getNano(); // 获取纳秒数
SpringBoot默认集成了Jackson作为JSON处理库,Jackson已经能自动处理 LocalDate、LocalTime 和 LocalDateTime 类型。
如果需要使用自定义日期时间格式,我们有两种方式:
手动更改全局配置: 如果需要自定义日期格式,可以通过 ObjectMapper 的配置类来注册自定义的日期格式化器:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-json</artifactId></dependency>
@Configurationpublic class JacksonConfig { @Bean public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() { return builder -> { builder.simpleDateFormat("yyyy-MM-dd"); // 使用Java 8时间API的日期格式器 builder.dateFormat(new StdDateFormat().withColonInTimeZone(true)); // 注册LocalDateTime的序列化和反序列化模块 builder.modules(new JavaTimeModule()); }; }}
手动绑定格式化配置SpringBoot支持自动绑定HTTP请求参数到控制器方法参数中,包括 LocalDate、LocalTime 和 LocalDateTime 类型。客户端需发送符合日期格式的字符串,Spring Boot会自动转换成相应类型。
@PostMapping("/events") public ResponseEntity<Event> createEvent(@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date, @RequestParam @DateTimeFormat(pattern = "HH:mm:ss") LocalTime startTime, @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime timestamp) { // ... }
或者请求或者响应VO中:
public static class ResponseVO{ @DateTimeFormat(pattern = "yyyy-MM-dd") private LocalDate date; @DateTimeFormat(pattern = "HH:mm:ss") private LocalTime startTime; @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime timestamp; }
在MyBatis中查询MySQL数据库时,使用Java 8的 java.time.LocalDate、java.time.LocalTime 和 java.time.LocalDateTime类型。
LocalDate 对应MySQL的DATE类型。LocalTime 对应MySQL 的 TIME 类型。LocalDateTime 对应MySQL的 DATETIME或TIMESTAMP类型。
CREATE TABLE `test_date`( `id` BIGINT ( 20 ) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增主键', test_local_date DATE , test_local_time TIME, test_local_date_time DATETIME, PRIMARY KEY ( `id` ))ENGINE = INNODB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8mb4 COMMENT = '日期时间测试';
@Datapublic class TestDate implements Serializable { /** * 自增主键 */ private Long id; private LocalDate testLocalDate; private LocalTime testLocalTime; private LocalDateTime testLocalDateTime; private static final long serialVersionUID = 1L;}
3.MyBatis配置:
@Testpublic void testInsertDate(){ TestDate testDate = new TestDate(); testDate.setTestLocalDate(LocalDate.of(2024, 3, 12)); testDate.setTestLocalTime(LocalTime.of(20,10,30)); testDate.setTestLocalDateTime(LocalDateTime.of(2024, 3, 12,20,10,30,0)); testDateMapper.insert(testDate);}@Testpublic void testQueryDate(){ TestDate testDate = testDateMapper.selectByPrimaryKey(1L); System.out.println("testLocalDate:"+testDate.getTestLocalDate()); System.out.println("testLocalTime:"+testDate.getTestLocalTime()); System.out.println("testLocalDateTime:"+testDate.getTestLocalDateTime());}
image.png
本文探讨了Java 8引入的全新日期时间API相较于传统的Date和Calendar类的优势及实际应用。鉴于Java 8新日期时间API在设计上的先进性和易用性,强烈建议积极采纳并替换掉陈旧的Date和Calendar类,转而采用如LocalDate、LocalDateTime、ZonedDateTime等现代日期时间类。
Java 8新日期时间API提供了更为清晰、直观的操作接口,支持不可变对象设计模式,增强了类型安全性,并具备丰富的日期时间运算、解析与格式化功能,显著提高了代码质量与可读性。此外,新API对日期时间单位的精确度控制、时区管理以及与其他日期时间规范的兼容性等方面均表现出卓越的表现力和灵活性,使得我们在处理各类复杂日期时间逻辑时能够更加得心应手,提升开发效率。
本文链接:http://www.28at.com/showinfo-26-76496-0.html还在用Calendar操作Date?Java8都被放弃了,你还不知道Java8中全新的日期时间API
声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com
上一篇: 想进阿里?先搞懂Spring Bean的循环依赖!
下一篇: 如何实现一个支持海量大并发的服务?