还在用Calendar操作Date?Java8都弃用了,还不知道它的这款强大的工具吗?

这篇具有很好参考价值的文章主要介绍了还在用Calendar操作Date?Java8都弃用了,还不知道它的这款强大的工具吗?。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

引言

在过去的Java版本中,日期和时间的处理主要依赖于java.util.Datejava.util.Calendar类,然而随着业务系统的复杂以及技术层面的提升,这些传统的日期时间类暴露出了若干显著的不足之处。随着Java8的发布,其引入了一套全新的日期时间API,彻底改变了我们处理日期和时间的方式。

传统的日期时间类

相比较Java8中新引入的java.time包下的时间处理类,传统的日期时间处理类在易用性,线程安全,不支持市时区等缺点。

  1. 设计复杂性Date类的设计较为简单,但它实际上混合了日期和时间信息,并且没有提供直观的方法来单独操作日期或时间部分。Calendar类虽然提供了更多灵活性,但由于其内部状态和方法的复杂性,使得开发者在使用过程中容易出现错误和混淆,尤其是在进行日期时间计算和格式化时。比如:
Date currentDate = new Date();  
// 输出原始的日期时间,通常不是人类可读格式   Fri Mar 08 03:13:47 CST 2024
System.out.println(currentDate);

// 要改变日期的某个部分,必须先将其转换为 Calendar,然后设置  
Calendar calendar = Calendar.getInstance();  
calendar.setTime(currentDate);  
// 修改日期的天数  
calendar.add(Calendar.DAY_OF_MONTH, 1);
  1. 线程安全性Date和 Calendar类,以及格式化日期的SimpleDateFormat类都不是线程安全的,这意味着在多线程环境下的并发访问可能会导致数据不一致。

    • Date类内部维护了一个 long 类型的瞬时值,当调用如setTime()方法来更新这个瞬时值时,不同的线程同时调用就会互相覆盖彼此的值,造成数据不一致。

    • Calendar类不仅包含了日期和时间信息,还有一系列内部状态变量,如年、月、日、小时、分钟、秒等。Calendar类的方法通常会修改这些内部状态,例如 add()set() 等。在多线程环境下,若多个线程尝试同时修改同一个 Calendar 实例,也会导致不可预期的结果。

    • SimpleDateFormat类在执行格式化和解析日期时间操作时,内部会维护一个 Calendar对象以及其他一些状态变量。在 format() 或 parse() 方法执行过程中,这些状态会被更新以完成格式转换。并且SimpleDateFormat中的方法并非原子操作,因此在多线程并发调用时,可能在一个线程还未完成整个操作时就被另一个线程打断,导致错误的日期时间处理结果。

  2. 时区处理能力Date类虽能表示时间戳,但它不直接关联时区信息,难以进行时区相关的转换。而 Calendar 虽然支持时区,但操作过程相当复杂。

  3. 精度差异Date类精度只到毫秒级。

Java8中日期时间类

Java8中引入的LocalDateLocalTimeLocalDateTime这几个位于java.time下的类克服了上述传统类别的局限性,提供了更强大、直观和精准的日期时间处理能力,成为现代Java开发中处理日期时间首选的工具类。相比较传统的日期时间类,具备以下显著优势:

  1. 功能丰富
    java.time包下的类如 LocalDateLocalTimeLocalDateTimeZonedDateTime 等都有明确的职责划分,分别处理日期、时间、日期时间以及带时区的日期时间,结构清晰,易于理解和使用。并且它们提供了一系列直观、面向对象的API,如 plusXxx()minusXxx()withXxx()等方法,使日期时间操作变得简单明了。

  2. 时区支持
    除此之外,还支持时区,通过ZonedDateTime和 ZoneId等类提供了对时区的更好支持,可以方便地进行时区转换和处理。

  3. 线程安全
    这些类都是不可变对象,线程安全,可以在多线程环境下安全使用,不会出现因并发操作导致的数据不一致问题。

  4. 更高的精度
    支持纳秒级精度,相比 Date 类的毫秒精度更胜一筹。

java.time下主要有如下一些关键类:

  1. LocalDate
    LocalDate类表示一个不包含时间信息的日期,只包含年、月、日三个部分,且不关联任何特定时区。

  2. LocalTime
    LocalTime类表示一个不包含日期信息的具体时间,包含时、分、秒和纳秒四个部分。

  3. LocalDateTime
    LocalDateTime类结合了日期和时间,表示一个具体的日期和时间点,但是不包含时区信息。

  4. ZonedDateTime
    ZonedDateTime类表示一个带有时区的日期时间,它可以明确表示某一特定时区内的某一确切时间点。

  5. Instant
    Instant类表示时间线上某一瞬时点,通常以Unix纪元(1970-01-01T00:00:00Z)以来的秒数和纳秒数表示,它是全球通用的时间点表示。

  6. Period
    Period类用于表示两个日期之间的期间,包括年、月、日的数量。

  7. Duration
    Duration类表示两个时间点之间的时间差,包含秒和纳秒的持续时间,主要用于表示时间间隔而非日历单位。

  8. DateTimeFormatter
    DateTimeFormatter类用于日期时间的格式化和解析,提供了标准化和自定义的日期时间格式化方式。

  9. TemporalAdjusters
    TemporalAdjusters类提供了一系列实用方法,用于调整日期时间,例如获取下一个工作日、月初、月末等。

这些类共同构成了一个强大、灵活且易于使用的日期时间处理体系,大大改善了Java在处理日期时间问题时的效率和准确性。接下来我们在使用上分别介绍这些类,以及使用他们的方式,感受他们的强大。

Java8中日期时间类使用

创建

NOW方法获取当前 时刻、日期、时间
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.241181
localDate:2024-03-11
localDateTime:2024-03-11T15:28:45.260655

针对LocalTimeLocalDateTime获取当前时刻默认会带有毫秒,如果不需要毫秒的话,可以通过设置纳秒为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:31
localDateTime:2024-03-11T15:32:31

而对于LocalDateTime获取当前日期,默认toString会带有T分隔日期和时间,在项目中,可以通过全局序列化,进行统一的时间格式输出为 yyyy-MM-dd HH:mm:ss。但是一般不建议这么干,毕竟改变全局序列化配置,建议不使用toString,可以使用DateTimeFormatter进行自定义转换。

of()方法指定年、月、日、时刻创建
// 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-11
localDate:2024-03-11
localDate:2024-03-11
localDate:2024-03-11
localDate:2024-03-11
localTime:01:30
localTime:01:30:30
localTime:16:41:37.893310
localTime:16:41:37
localDateTime:2024-03-11T01:30:30
localDateTime:2024-03-11T01:30:30
localDateTime:2024-03-11T16:41:37
localDateTime:2024-03-11T16:41:37.893310
from()方法转换

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.942911
localDate:2024-03-11
localDateTime:2024-03-11T17:18:27.942911
parse()方法转换

将字符串按照指定格式(可选)解析为对应的日期时间类。

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:30
localTime:17:25:30
localDate:2024-03-11
localDate:2024-03-11
localDateTime:2024-03-11T17:25:30
localDateTime:2024-03-11T17:25:30

日期时间类的相互转换

LocalTime、LocalDate、LocalDateTime 相互转化
// LocalTime + LocalDate = LocalDateTime
LocalDateTime 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 转 LocalDate
LocalDate localDate = LocalDateTime.now().toLocalDate();
System.out.println("localDate:"+localDate);
// LocalDateTime 转 LocalTime
LocalTime localTime = LocalDateTime.now().toLocalTime();
System.out.println("localTime:"+localTime);

// 获取今日开始时间 2024-03-11T00:00
localDateTime = LocalDate.now().atStartOfDay();
System.out.println("localDateTime:"+localDateTime);
// 获取今日开始时间 2024-03-11T00:00
LocalDateTime startDateTime = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
System.out.println("startDateTime:"+ startDateTime);
// 获取今日结束时间 2024-03-11T23:59:59.999999999
LocalDateTime endDateTime = LocalDateTime.of(LocalDate.now(), LocalTime.MAX);
System.out.println("endDateTime:"+ endDateTime); 

输出为:

localDateTime:2024-03-11T18:04:22.348539
localDateTime:2024-03-11T18:04:22.370562
localDateTime:2024-03-11T18:04:22.370768
localDate:2024-03-11
localTime:18:04:22.371062
localDateTime:2024-03-11T00:00
startDateTime:2024-03-11T00:00
endDateTime:2024-03-11T23:59:59.999999999
String 与 LocalTime、LocalDate、LocalDateTime 相互转化

主要使用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 转 String
String localDateTimeStr = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println("localDateTimeStr:"+localDateTimeStr);

// String 转 LocalDateTime
LocalDateTime 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-11
localDateTimeStr:2024-03-11 19:02:58
localDateTime:2023-04-14T15:59:40
Date 与 LocalDate、LocalDateTime 相互转化
// Date 转 LocalDateTime
Date currentDate = new Date();
// 转换为Instant 
Instant instant = currentDate.toInstant();
// 通过zoneId设置时区(这里使用系统时区),转换为带带时区的 ZoneDateTime
ZonedDateTime zonedDateTime = instant.atZone(ZoneId.systemDefault());
// 然后通过ZonedDateTime转换为LocalDateTime
LocalDateTime localDateTime = zonedDateTime.toLocalDateTime();
System.out.println("localDateTime:"+localDateTime);
// LocalDateTime 转 Date,同理也是通过ZonedDateTime转换为Date
Date 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 转 LocalDateTime
Date localDateToDate = Date.from(LocalDate.now().atStartOfDay(ZoneId.systemDefault()).toInstant());

这里介绍一下ZoneId。java.time.ZoneId是Java 8中java.time包中用于表示时区的类。时区是地球上的地理位置,用于确定在该位置观察太阳升落以及规定当地居民生活和商业活动时间的标准时间。ZoneId使用IANA时区数据库提供的时区标识符,这个标识符是唯一的,这些标识符通常是地区/城市对,例如“Asia/Shanghai”代表中国上海所在的时区,America/New_York代表美国纽约城市。
其实例获取有两种方式:

  • ZoneId.systemDefault():获取系统默认的时区ID。
  • ZoneId.of(String zoneId):根据提供的时区ID字符串获取ZoneId实例。至于zoneId的值,可以查看源码。可以通过ZoneId.getAvailableZoneIds()查看获取。
Long 与 LocalDate、LocalDateTime 相互转化

时间戳转换。

long timeMillis = System.currentTimeMillis();

// 时间戳(Long) 转 LocalDateTime
LocalDateTime 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) 转 LocalDate
LocalDate 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.335
localDateTime:2024-03-11T19:37:02.335
localDateTimeToSecond:1710157022
localDateTimeToMilliSecond:1710157022365
localDate:2024-03-11
localDateToSecond:1710086400
localDateToMilliSecond:1710086400000

java.time.ZoneOffset是Java8中java.time包内用来表示时区偏移量的类,它表示的是格林尼治标准时间或协调世界时间(UTC)基础上的固定偏移量。每一个时区都可以通过一个或多个偏移量来表示,比如“+02:00”表示比UTC时间快两个小时的时区偏移。
其实例创建有如下方式:

  • ZoneOffset.ofHours(int hours):根据小时数创建偏移量,例如 ZoneOffset.ofHours(2) 表示比UTC早2小时的时区。
  • ZoneOffset.ofHoursMinutes(int hours, int minutes):根据小时数和分钟数创建偏移量。
  • ZoneOffset.ofHoursMinutesSeconds(int hours, int minutes, int seconds):根据小时、分钟和秒数创建偏移量。
  • ZoneOffset.ofTotalSeconds(int totalSeconds):根据相对于UTC的总秒数创建偏移量。
  • ZoneOffset.of(String offsetId):根据偏移量ID(如 "+02:00")创建实例。

日期时间类的操作

日期时间的增减

java.time 包中日期时间类(如 LocalDateTimeLocalDate 和 LocalTime)可以通过plusXxx() 和 minusXxx() 方法,用于对日期时间对象进行加减操作,以增加或减少指定的时间或日期单位。

1、LocalDateTime 加减

  • plusHours(int hours)plusMinutes(int minutes)plusSeconds(int seconds):分别用于向 LocalDateTime 对象添加指定的小时数、分钟数和秒数。
  • plus(1, ChronoUnit.XXX):这里的 ChronoUnit 参数可以是 HOURSMINUTESSECONDS 等,也可以是 YEARSMONTHSDAYSWEEKS 等,用于向日期时间对象添加指定单位的数量。
  • plus(Duration duration):使用 Duration 对象来增加时间,Duration 可以包含秒和纳秒的精度。
  • plus(Period period):使用 Period 对象来增加日期,Period 可以表示年、月、日的数量。
  • plusXxx() 方法相对应,minusXxx() 方法用于从日期时间对象中减少指定的单位。例如 minusHours(int hours)minusMinutes(int minutes)minusSeconds(int seconds) 等方法用于减少小时、分钟、秒数。
// 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 加减

  • 同样的,plusYears(int years)plusMonths(int months)plusDays(int days) 分别用于增加年、月、日。
  • plus(1, ChronoUnit.XXX)plus(Duration/Period duration/period) 方法在此同样适用,用于增加指定的日期单位。
  • plusXxx() 方法相对应,minusXxx() 方法用于从日期时间对象中减少指定的单位。例如 minusYears(int years)minusMonths(int months)minusWeeks(int weeks)minusDays(int days) 等方法用于减少年、月、周、天数。
// 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 加减

  • plusHours(int hours)plusMinutes(int minutes)plusSeconds(int seconds):分别用于向 LocalTime 对象添加指定的小时数、分钟数和秒数。
  • 同样支持 plus(1, ChronoUnit.XXX) 和 plus(Duration duration) 方法,用于增加时间单位。
  • 与 plusXxx() 方法相对应,minusXxx() 方法用于从日期时间对象中减少指定的单位。minus(1, ChronoUnit.XXX)minus(Duration duration)minus(Period period) 方法也分别用于减少指定的日期或时间单位。例如 minusHours(int hours)minusMinutes(int minutes)minusSeconds(int seconds) 等方法用于减少小时、分钟、秒数。
// 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);
日期时间修改指定值

LocalDateLocalTimeLocalDateTimeZonedDateTime可以通过相对应的withXxx()方法修改指定的值。

1、LocalDate

  • LocalDate.withYear(int year):修改年份字段。
  • LocalDate.withMonth(int month):修改月份字段(注意月份是从1开始计数的)。
  • LocalDate.withDayOfMonth(int dayOfMonth):修改日期字段。
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.withHour(int hour):修改小时字段。
  • LocalTime.withMinute(int minute):修改分钟字段。
  • LocalTime.withSecond(int second):修改秒字段。
  • LocalTime.withNano(int nanoOfSecond):修改纳秒字段。
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.withYear(int year)
  • LocalDateTime.withMonth(int month)
  • LocalDateTime.withDayOfMonth(int dayOfMonth)
  • LocalDateTime.withHour(int hour)
  • LocalDateTime.withMinute(int minute)
  • LocalDateTime.withSecond(int second)
  • LocalDateTime.withNano(int nanoOfSecond)
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

  • 除了上述的日期和时间字段外,还有时区相关的 withZoneSameInstant(ZoneId zone) 方法,可以改变时区的同时保持同一瞬间不变。
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);

除此之外,调整日期时间还可以通过TemporalAdjustersTemporalAdjuster 是一个函数式接口,用于根据给定的规则调整日期时间对象。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月最后一个周五的日期

// 自定义 TemporalAdjuster
TemporalAdjuster adjuster = temporal -> {
    return temporal.plusDays(10).with(TemporalAdjusters.lastDayOfMonth());
};
LocalDate tenthDayNextMonthEnd = date.with(adjuster); // 返回4月最后一个日期,前提是先加10天
日期时间的比较

在Java8及其以后版本的日期时间API中,isBefore() 和 isAfter() 方法是 java.time 包中的 LocalDateLocalTimeLocalDateTimeZonedDateTime 等日期时间类所共有的方法,用于比较两个日期时间对象的先后顺序。

isBefore()

  • 此方法用于判断当前对象是否早于另一个日期时间对象。
  • 如果当前对象的时间点在参数对象之前,则返回 true;否则返回 false
LocalDate date1 = LocalDate.of(2024, 3, 11);
LocalDate date2 = LocalDate.of(2024, 3, 12);
boolean isEarlier = date1.isBefore(date2); // 返回 true,因为 date1 在 date2 之前

isAfter()

  • 此方法用于判断当前对象是否晚于另一个日期时间对象。
  • 如果当前对象的时间点在参数对象之后,则返回 true;否则返回 false
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 包中,大部分日期时间类如 LocalDateLocalTimeLocalDateTimeZonedDateTime 都实现了 Comparable 接口,从而可以直接使用 compareTo() 方法进行比较。compareTo() 方法用于比较两个日期时间对象的先后顺序,返回值含义如下:

  • 如果当前对象早于(时间点在前)参数对象,返回负数。
  • 如果当前对象等于参数对象,返回0。
  • 如果当前对象晚于(时间点在后)参数对象,返回正数。
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 包中,各个日期时间类如 LocalDateLocalTimeLocalDateTime 提供了一系列 get 方法,用于获取特定字段的值。

获取日期中的特定字段:

LocalDate date = LocalDate.of(2024, 3, 11);
int dayOfMonth = date.getDayOfMonth(); // 获取当月的第几天,此处返回11
int monthValue = date.getMonthValue(); // 获取月份值,此处返回3
Month month = date.getMonth(); // 获取Month枚举,此处返回March
int year = date.getYear(); // 获取年份,此处返回2024

对于时间部分,类似地可以获取小时、分钟、秒和纳秒:

LocalTime time = LocalTime.of(19, 30, 45);
int hour = time.getHour(); // 获取小时数,此处返回10
int minute = time.getMinute(); // 获取分钟数,此处返回30
int second = time.getSecond(); // 获取秒数,此处返回45
int nano = time.getNano(); // 获取纳秒数

在SpringBoot中使用

SpringBoot默认集成了Jackson作为JSON处理库,Jackson已经能自动处理 LocalDateLocalTime 和 LocalDateTime 类型。

如果需要使用自定义日期时间格式,我们有两种方式:

手动更改全局配置: 如果需要自定义日期格式,可以通过 ObjectMapper 的配置类来注册自定义的日期格式化器:

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-json</artifactId>
</dependency>
@Configuration
public 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请求参数到控制器方法参数中,包括 LocalDateLocalTime 和 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中使用

在MyBatis中查询MySQL数据库时,使用Java 8的 java.time.LocalDatejava.time.LocalTime 和 java.time.LocalDateTime类型。

  1. 数据库表结构: 在MySQL数据库中,通常需要使用适合的日期时间类型来存储这些Java 8的日期时间对象。例如:
    • LocalDate 对应MySQLDATE类型。
    • LocalTime 对应MySQL 的 TIME 类型。
    • LocalDateTime 对应MySQL的 DATETIMETIMESTAMP类型。
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 = '日期时间测试';
  1. 实体类映射: 在Java实体类中,对应字段应声明为 LocalDateLocalTime 或 LocalDateTime 类型。
@Data
public class TestDate implements Serializable {
    /**
    * 自增主键
    */
    private Long id;

    private LocalDate testLocalDate;

    private LocalTime testLocalTime;

    private LocalDateTime testLocalDateTime;

    private static final long serialVersionUID = 1L;
}

3.MyBatis配置

  • 自动类型转换:如果你使用的是较新的MyBatis版本(>=3.4.5),MyBatis已经内置了对Java 8日期时间类型的处理。这意味着在执行SQL查询时,MyBatis会自动将数据库中的日期时间字段转换为相应的Java8类型。
@Test
public 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);
}

@Test
public 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());
}

  • 自定义TypeHandler:如果MyBatis版本较低或者需要自定义日期时间格式,你可能需要自定义 TypeHandler 来处理 LocalDateLocalTime 和 LocalDateTime 与数据库字段间的转换。关于Mybatis使用自定义TypeHandler,请异步:玩转Mybatis:自定义TypeHandler,轻松应对Mysql的JSON类型操作

结论

综上所述,本文深入探讨了Java 8引入的全新日期时间API相较于传统的Date和Calendar类的优势及实际应用。鉴于Java 8新日期时间API在设计上的先进性和易用性,我们强烈建议开发者积极采纳并替换掉陈旧的Date和Calendar类,转而采用如LocalDate、LocalDateTime、ZonedDateTime等现代日期时间类。

Java 8新日期时间API提供了更为清晰、直观的操作接口,支持不可变对象设计模式,增强了类型安全性,并具备丰富的日期时间运算、解析与格式化功能,显著提高了代码质量与可读性。此外,新API对日期时间单位的精确度控制、时区管理以及与其他日期时间规范的兼容性等方面均表现出卓越的表现力和灵活性,使得开发者在处理各类复杂日期时间逻辑时能够更加得心应手,提升开发效率。

因此,无论是处于对代码现代化改造的需求,还是出于提高开发效率和程序稳定性的考量,迁移到Java 8的新日期时间API无疑是明智之举。通过充分利用这些强大且功能完备的工具,开发者将在日期时间处理领域实现飞跃,为项目的长期维护和发展打下坚实基础。

本文已收录于我的个人博客:码农Academy的博客,专注分享Java技术干货,包括Java基础、Spring Boot、Spring Cloud、Mysql、Redis、Elasticsearch、中间件、架构设计、面试题、程序员攻略等文章来源地址https://www.toymoban.com/news/detail-841143.html

到了这里,关于还在用Calendar操作Date?Java8都弃用了,还不知道它的这款强大的工具吗?的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • Java中的日期时间类详解(Date、Calendar、DateFormat)

    Hi i,m JinXiang ⭐ 前言 ⭐ 本篇文章主要介绍Java时间的几种常见方法以及部分理论知识 🍉欢迎点赞 👍 收藏 ⭐留言评论 📝私信必回哟😁 🍉博主收将持续更新学习记录获,友友们有任何问题可以在评论区留言 在JDK1.0中,Date类是唯一的一个代表时间的类,但是由于Date类不

    2024年02月05日
    浏览(12)
  • Java String、Date、Calendar、LocalDate和LocalDateTime互转

    目录 一、String和Date互转 1.1 String时间转为Date 1.2 Date转为String 二、String和Calendar互转 2.1 String时间转为Calendar 2.2 Calendar转为String 三、String和LocalDate(LocalDateTime)互转 3.1 String时间转为LocalDate(LocalDateTime) 3.2 LocalDate(LocalDateTime)转为String 四、Date和Calendar互转 4.1 Date转为Calen

    2024年02月06日
    浏览(17)
  • Java 常见的日期转换(Timestamp、Calendar、Date、String)

    日期类型: Timestamp: java.sql.Timestamp Calendar: java.util.Calendar Date: java.util.Date String: java.lang.String 1、Timestamp 转 Calendar 2、Timestamp 转 Date 3、Timestamp 转 String 4、Calendar 转 Timestamp 5、Calendar 转 Date 6、Calendar 转 String 7、Date 转 Calendar 8、Date 转 Timestamp

    2024年02月04日
    浏览(17)
  • java中LocalDateTime、Calendar、Date三种时间类型进行加减

    java中LocalDateTime、Calendar、Date三种时间类型进行加减

    介绍了三种日期类型以及他们的日期加减 什么是LocalDateTime LocalDateTime是Java 8中的一个类,表示不带时区的日期和时间。它是java.time包中的一个类,用于处理日期和时间的操作。 LocalDateTime包含日期和时间的信息,不包含时区信息。它提供了各种方法来获取和操作日期和时间的

    2024年02月04日
    浏览(13)
  • Java8 时间处理API(Date Time API)详解

    在Java8之前,我们通常使用 java.util.Date 和 java.util.Calendar 类来处理日期和时间相关的操作,例如: 这种方式虽然可以实现功能,但是有一些缺点: 可变性: Date 和 Calendar 类都是可变的,这意味着它们的值可以被修改,这可能会导致一些线程安全和逻辑错误的问题。 可读性差

    2024年02月11日
    浏览(14)
  • 【JAVA语言-第12话】API中的工具类 之 Date,DateFormat,SimpleDateFormat,Calendar类的详细解析

    【JAVA语言-第12话】API中的工具类 之 Date,DateFormat,SimpleDateFormat,Calendar类的详细解析

    目录 日期和时间 1.1 Date类 1.1.1 概述 1.1.2 常用方法  1.1.3 案例 1.2 DateFormat类  1.2.1 概述 1.2.2 常用方法 1.3 SimpleDateFormat类 1.3.1 概述 1.3.2 构造方法 1.3.3 模式字符  1.3.4 日期转字符串 1.3.5 字符串转日期 1.4 Calendar类 1.4.1 概述 1.4.2 静态方法 1.4.3 常用成员方法 1.4.4 成员方法中参数

    2024年02月02日
    浏览(12)
  • Java8 掌握Date与Java.time转换的核心思路,轻松解决各种时间转换问题

    Java8时区时间运用详解,2万字助你通关java.time包 各种时间类的转换核心只有一个流程: 时间类 - 时间戳 - 时间类 Date和Instant它们存储的都是时间戳信息 LocalDateTime、LocalDate、LocalTime、ZonedDateTime、OffsetDateTime、OffsetTime、Year等其它Java8时间类则是 年、月、日、时、分、秒、纳秒

    2024年02月02日
    浏览(10)
  • 【Java基础教程】(三十七)常用类库篇 · 第七讲:日期时间处理类——三万字全面讲解 LocalDateTime和 DateTimeFormatter,Date、Calendar ~

    【Java基础教程】(三十七)常用类库篇 · 第七讲:日期时间处理类——三万字全面讲解 LocalDateTime和 DateTimeFormatter,Date、Calendar ~

    Java的时间处理相关类主要是为了提供灵活、可靠地处理日期和时间的能力,以满足各种应用程序中对时间的需求。这些类旨在简化日期和时间的操作,并提供一致的方法和功能来处理时间上的计算、格式化和解析。 🔍 时间日期及处理类汇总 将Java中所有用于表示日期时间的

    2024年02月16日
    浏览(19)
  • 日期与时间【Date/SimpleDateFormat/Calendar】

    视频链接:https://www.bilibili.com/video/BV1Cv411372m?p=121vd_source=9140dcc493e34a9f4e95ca2f8f71bbd3 Date类的对象在java中代表的是当前所在系统的此刻日期时间。 Date的构造器 public Date():创建一个Date对象,代表的是系统当前此刻日期时间。 Date的常用方法 public long getTime():获取时间对象的毫秒值

    2024年02月03日
    浏览(14)
  • Flutter之日期选择器 calendar_date_picker2

    Flutter之日期选择器 calendar_date_picker2

    calendar_date_picker2主要由两个小部件组成: 1、 CalendarDatePicker2 ,此小部件仅包含日历 UI,并且每当用户点击不同的日期时就会发出事件。 2、 CalendarDatePicker2WithActionButtons ,此小部件包括日历 UI 和操作按钮(取消和确定)。仅当用户点击“确定”按钮时,此小部件才会发出更新

    2024年02月07日
    浏览(24)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包