引言
在Java 8之前,日期和时间的管理一直是Java开发中的痛点。java.time
包的推出彻底改变了这一局面,提供了一套全新的日期和时间API,解决了旧API如Date
和Calendar
中存在的问题。
概述
Date
类和Calendar
类存在问题:可变性、偏移性、Calendar
无法格式化、线程不安全、无法处理闰秒等
- 新的API:
java.time
,包含:本地日期(LocalDate
)、本地时间(LocalTime
)、本地日期时间(LocalDateTime
)、时区(ZonedDateTime
)、持续时间(Duration
)
- Date类也增加了
toInstant()
方法用于把Date
转换成新的表示形式
注:无特殊说明一般LocalDateTime
实例的方法在LocalDate
或LocalTime
中有相同的或类似的,由于篇幅原因只列举LocalDateTime
的API
。
创建日期时间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public void test() { LocalDateTime time1 = LocalDateTime.now(); LocalDateTime time2 = LocalDateTime.now(ZoneId.systemDefault()); LocalDateTime time3 = LocalDateTime.now(Clock.systemDefaultZone());
LocalDateTime time4 = LocalDateTime.of(2022, 2, 2, 13, 34, 56, 233);
LocalDateTime time5 = LocalDateTime.parse("2022-03-21T09:23:55.233"); LocalDateTime time6 = LocalDateTime.parse("2022-02-23 07:12:45", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
LocalDateTime time7 = LocalDateTime.ofInstant(Instant.now(), ZoneId.systemDefault()); LocalDateTime time8 = Instant.ofEpochMilli(1656866702010L).atZone(ZoneId.systemDefault()).toLocalDateTime(); }
|
获取年月日时分秒
以时间2022-02-15T21:23:54.401
为例
| public void test() { LocalDateTime localDateTime = LocalDateTime.now(); int year = localDateTime.getYear(); Month month = localDateTime.getMonth(); int monthValue = localDateTime.getMonthValue(); int dayOfYear = localDateTime.getDayOfYear(); int dayOfMonth = localDateTime.getDayOfMonth(); DayOfWeek dayOfWeek = localDateTime.getDayOfWeek(); int hour = localDateTime.getHour(); int minute = localDateTime.getMinute(); int second = localDateTime.getSecond(); int nano = localDateTime.getNano(); }
|
修改日期时间
LocalDateTime
的修改并不是修改原有的时间,而是返回一个新的想同类型的日期时间,可直接修改对应的年月日时分秒,也可使用with
,接收指定日期进行修改
| public void test() { LocalDateTime localDateTime = LocalDateTime.now(); LocalDateTime localDateTime1 = localDateTime.withYear(2023); LocalDateTime localDateTime2 = localDateTime.withMonth(3); LocalDateTime localDateTime3 = localDateTime.withDayOfMonth(23); LocalDateTime localDateTime4 = localDateTime.withHour(12); LocalDateTime localDateTime5 = localDateTime.withMinute(23); LocalDateTime localDateTime6 = localDateTime.withSecond(33); LocalDateTime localDateTime7 = localDateTime.withNano(233000000); LocalDateTime localDateTime8 = localDateTime.withDayOfYear(233); }
|
时间矫正器
根据一个日期获取指定的时间,如本月第一天、最后一天、下个月第一天等等指定日期,都是返回新的本地时间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| public void test() { localDateTime.with(TemporalAdjusters.firstDayOfMonth()); localDateTime.with(TemporalAdjusters.lastDayOfMonth()); localDateTime.with(TemporalAdjusters.firstDayOfNextMonth()); localDateTime.with(TemporalAdjusters.firstDayOfYear()); localDateTime.with(TemporalAdjusters.lastDayOfYear()); localDateTime.with(TemporalAdjusters.firstDayOfNextYear()); localDateTime.with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY)); localDateTime.with(TemporalAdjusters.lastInMonth(DayOfWeek.MONDAY)); localDateTime.with(TemporalAdjusters.dayOfWeekInMonth(-1, DayOfWeek.MONDAY)); localDateTime.with(TemporalAdjusters.next(DayOfWeek.MONDAY)); localDateTime.with(TemporalAdjusters.nextOrSame(DayOfWeek.TUESDAY)); localDateTime.with(TemporalAdjusters.previous(DayOfWeek.MONDAY)); localDateTime.with(TemporalAdjusters.previousOrSame(DayOfWeek.TUESDAY)); }
|
日期比较
两个日期比较,是否在前或在后
| public void test() { LocalDateTime now = LocalDateTime.now(); LocalDateTime future = LocalDateTime.of(2022, 4, 2, 9, 18, 22, 233); System.out.println(now.isBefore(future)); System.out.println(now.isAfter(future)); }
|
时间量
注意时间量有正负之分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| public void test() { LocalDateTime now = LocalDateTime.now(); LocalDateTime future = LocalDateTime.parse("2022-6-30 23:59:59", DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)); Duration duration1 = Duration.between(now, future); Duration duration2 = Duration.ofDays(20); Duration duration3 = Duration.of(10, ChronoUnit.DAYS); Duration duration4 = Duration.parse("PT1089H45M"); Duration duration5 = Duration.between(future, now); Duration duration6 = duration5.abs(); int i = duration6.compareTo(duration5); boolean equals = duration6.equals(duration5); Duration duration7 = duration6.dividedBy(2); long nano = duration6.get(ChronoUnit.NANOS); long second = duration6.get(ChronoUnit.SECONDS); boolean negative = duration6.isNegative(); boolean isZero = duration6.isZero(); Duration duration8 = duration6.minus(duration7); Duration duration9 = duration6.minusDays(2); Duration duration10 = duration6.minus(3, ChronoUnit.DAYS); Duration negated = duration6.negated(); Duration duration11 = duration6.plus(2, ChronoUnit.DAYS); Duration duration12 = duration6.plusHours(3); Duration duration13 = duration6.plus(duration2); long days = duration6.toDays(); long nanos = duration6.toNanos(); Duration duration14 = duration6.withSeconds(20); Duration duration15 = duration6.withNanos(2333); }
|
时期时间量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| public void test() { LocalDate now = LocalDate.now(); LocalDate future = LocalDate.parse("2022-12-31", DateTimeFormatter.ISO_DATE); Period period = now.until(future); Period period1 = Period.ofDays(10); Period period2 = Period.of(2, 3, 1); Period period3 = Period.ofWeeks(2); Period period4 = Period.parse("P2M2D"); boolean equals = period4.equals(period1); Period period5 = period4.plus(period2); long l = period4.get(ChronoUnit.YEARS); int months = period4.getMonths(); int days = period4.getDays(); boolean negative = period4.isNegative(); boolean zero = period4.isZero(); Period minus = period4.minus(period1); Period period6 = period4.minusMonths(2); Period negated = minus.negated(); Period period7 = period4.normalized(); Period period8 = period4.plus(period5); Period period9 = period4.plusDays(2); LocalDate localDate = (LocalDate) period4.subtractFrom(LocalDate.now()); Period period10 = period4.withMonths(4); }
|
时间加减
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| public void test() { LocalDateTime now = LocalDateTime.now(); LocalDateTime localDateTime1 = now.plusYears(2); LocalDateTime localDateTime11 = now.minusYears(2); LocalDateTime localDateTime2 = now.plusMonths(2); LocalDateTime localDateTime12 = now.minusMonths(2); LocalDateTime localDateTime3 = now.plusWeeks(2); LocalDateTime localDateTime13 = now.minusWeeks(2); LocalDateTime localDateTime4 = now.plusDays(2); LocalDateTime localDateTime14 = now.minusDays(2); LocalDateTime localDateTime5 = now.plusHours(2); LocalDateTime localDateTime15 = now.minusHours(2); LocalDateTime localDateTime6 = now.plusMinutes(2); LocalDateTime localDateTime16 = now.minusMinutes(2); LocalDateTime localDateTime7 = now.plusSeconds(2); LocalDateTime localDateTime17 = now.minusSeconds(2); LocalDateTime localDateTime8 = now.plusNanos(2); LocalDateTime localDateTime18 = now.minusNanos(2); LocalDateTime LocalDateTime9 = now.plus(20, ChronoUnit.DAYS); LocalDateTime LocalDateTime19 = now.minus(20, ChronoUnit.DAYS); LocalDateTime localDateTime0 = now.plus(Duration.ofSeconds(200)); LocalDateTime localDateTime10 = now.minus(Duration.ofSeconds(200)); }
|
时区时间
| public void test() { ZonedDateTime zonedDateTime1 = ZonedDateTime.now(); ZonedDateTime zonedDateTime2 = ZonedDateTime.now(ZoneId.systemDefault()); ZonedDateTime newYorkTime = ZonedDateTime.now(ZoneId.of("America/New_York")); ZonedDateTime shanghaiTime = newYorkTime.withZoneSameInstant(ZoneId.of("Asia/Shanghai")); LocalDateTime localDateTime = newYorkTime.toLocalDateTime(); }
|
时间戳
| public void test() { Instant now = Instant.now(); OffsetDateTime offsetDateTime = now.atOffset(ZoneOffset.ofHours(8)); long epochMilli = now.toEpochMilli(); long epochSecond = now.getEpochSecond(); int nano = now.getNano(); Instant instant = Instant.ofEpochSecond(1675888882); ZonedDateTime zonedDateTime = instant.atZone(ZoneId.systemDefault()); }
|
时间转换
| public void test12() { LocalDateTime localDateTime = LocalDateTime.now(); Instant instant = localDateTime.atZone(ZoneId.systemDefault()).toInstant(); LocalDate localDate = localDateTime.toLocalDate(); LocalTime localTime = localDateTime.toLocalTime(); LocalDateTime localDateTime1 = localDateTime.truncatedTo(ChronoUnit.DAYS); LocalDateTime localDateTime2 = localDateTime.truncatedTo(ChronoUnit.HOURS); }
|
格式化
可使用系统默认的格式化,一般其中FormatStyle.MEDIUM
格式为 yyyy-MM-dd HH:mm:ss
,也可以自定义格式,用于格式化或解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| public void test() { LocalDateTime now = LocalDateTime.now(); DateTimeFormatter.ISO_DATE_TIME.format(now); DateTimeFormatter.ISO_DATE.format(now); DateTimeFormatter.ISO_TIME.format(now); DateTimeFormatter.BASIC_ISO_DATE.format(now); DateTimeFormatter.ISO_WEEK_DATE.format(now); DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT).format(now); DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM).format(now); DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG).format(now); DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL).withZone(ZoneId.systemDefault()).format(now); DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(now); DateTimeFormatter.ofPattern("yyyy-MM-dd EE HH:mm:ss", Locale.CHINA).format(now); DateTimeFormatter.ISO_INSTANT.format(Instant.now()); }
|
转换
API
之间的转换
![](https://picgo-1304850123.cos.ap-guangzhou.myqcloud.com/TimeConversionAPI.png)
新旧日期时间类之间的转换
类 |
To 遗留类 |
From 遗留类 |
Instant 与 Date |
Date.from(instant) |
date.toInstant() |
Instant 与 Timestamp |
Timestamp.from(instant) |
timstamp.toInstant() |
ZonedDateTime 与 GregorianCanlendar |
GregorianCalendar.from(zonedDateTime) |
gregorianCalendar.toZonedDateTime() |
LocalDate 与 Time(sql) |
Date.valueOf(localDate) |
date.toLocalDate() |
LocalTime 与 Time(sql) |
Date.valueOf(localDate) |
date.toLocalTime() |
LocalTime 与 Timestamp |
Timestamp.valueOf(localDateTime) |
timestamp.toLocalDateTime() |
ZonedId 与 TimeZond |
Timezone.getTimeZone(id) |
timeZone.toZoneId() |
DateTimeFormatter 与 DateFormat |
formatter.toFormat() |
无 |
其他
不可变性和线程安全
- 新的
java.time
类是不可变的,并且天生是线程安全的。这意味着在多线程环境中,不需要额外的同步操作,从而可以减少线程阻塞和管理开销,提高性能。
- 旧的
Date
和Calendar
类是可变的,如果在多线程环境中使用,需要额外的同步措施,这可能导致性能下降。
API设计和内部实现
- 新
API
使用更清晰和更直观的方法来处理日期和时间,这使得操作更加高效。例如,LocalDateTime
, ZonedDateTime
, Instant
等类提供了精确到纳秒的时间表示,而Calendar
类的时间精度只到毫秒。
- 新日期时间类通常执行更快,因为它们依赖于更现代的
Java
语言特性,如流处理和Lambda
表达式。
性能测试
- 性能测试表明,在执行大量的日期时间计算时,
java.time
通常比旧的Date
和Calendar
更快。这是由于新API
在内部实现上更优化,以及不可变对象可以更好地利用现代多核处理器。
- 在日期解析和格式化方面,
java.time.format.DateTimeFormatter
是线程安全的,而旧的SimpleDateFormat
则不是。在高并发场景下,DateTimeFormatter
无需像SimpleDateFormat
那样进行同步,因此表现出更好的性能。
内存使用
- 新
API
的类设计为不可变对象,有助于减少内存泄漏的风险和优化垃圾收集过程。尽管每个操作都返回一个新对象,但Java
虚拟机(JVM)
的优化和现代垃圾回收机制可以有效管理这些短生命周期的对象。
总结
新的日期和时间API设计精良,易于理解和使用,同时解决了线程安全问题,提高了日期和时间操作的效率。它支持多种日期和时间操作,包括时区处理、持续时间计算等,极大地丰富了Java在日期和时间管理方面的能力。对于所有需要精确处理日期和时间的应用,使用新API无疑是一个更优的选择。