旧版jdk对时间和日期的设计非常差
1、设计不合理 在java.util和Java.sql 包中都有日期类,java.util.Data同时包含日期和时间 而Java.sql.Date仅仅包含日期 此外用于格式化和解析的类在java.text包下 new Date()年份从1900算起
2、非线程安全 所有的日期类都是可变的 这是Java日期类最大的问题之一 用SimpleFormatter在多线程格式化时会出现问题
3、时区处理麻烦 日期类并不提供国际化 没有时区支持
新的日期API
JDK 8中增加了一套全新的日期时间API,这套API设计合理,是线程安全的。新的日期及时间API位于java.time包中,下面是一些关键类。
LocalDate | 表示日期,包含年月日,格式为2019-10-16 |
LocalTime | 表示时间,包含时分秒,格式为16:38:54.158549300 |
LocalDateTime | 表示日期时间,包含年月日,时分秒,格式为2018-09-06T15:33:56.750 |
DateTimeFormatter | 日期时间格式化类。 |
lnstant | 时间戳,表示一个特定的时间瞬间。 |
Duration | 用于计算2个时间(LocalTime,时分秒)的距离 |
Period | 用于计算2个日期(LocalDate,年月日)的距离 |
ZonedDateTime | 包含时区的时间 |
Java中使用的历法是ISO8601日历系统,它是世界民用历法,也就是我们所说的公历。
平年有365天,闰年是366天。此外Java8还提供了4套其他历法,分别是:
ThaiBuddhistDate:泰国佛教历
MinguoDate:中华民国历
JapaneseDate:日本历
HijrahDate:伊斯兰历
正文将通过对这8个类的详细使用介绍来学习新日期时间API的用法
1、LocalDate:
初始化日期:
-
创建指定日期 localDate.of(int,int,int)
LocalDate date = LocalDate.of(2022,12,5);
System.out.println("date = " + date);
//sout: date = 2022-12-05
-
获取当前日期 LocalDate.now()
LocalDate now = LocalDate.now();
System.out.println("now = " + now);
//sout: now = 2022-06-18
-
获取日期信息
LocalDate date = LocalDate.of(2022,12,5);
int year = date.getYear();
// 得到年份:year = 2022
Month month = date.getMonth();
// 得到月份 month = DECEMBER(Month是枚举类型 获取数值可通过getValue )
int monthGetValue = date.getMonth().getValue();
// 得到月份 monthGetValue = 12
int monthValue = date.getMonthValue();
// 得到月份 monthValue = 12 相当于date.getMonth().getValue();
int dayOfYear = date.getDayOfYear();
// 得到一天在该年中的第多少天 dayOfYear = 339
int dayOfMonth = date.getDayOfMonth();
// 得到一天在月中的第多少天 dayOfMonth = 5 也就是经常说的几号
DayOfWeek dayOfWeek = date.getDayOfWeek();
// 得到星期 dayOfWeek = MONDAY(DayOfWeek 是枚举类型 获取数值可通过getValue)
int dayOfWeekGetValue = date.getDayOfWeek().getValue();
// 得到一天在改星期的第多少天 dayOfWeekGetValue = 1 也就是常说的礼拜几
2、LocalTime
-
获取指定时间
LocalTime localTime = LocalTime.of(10, 30, 59);
System.out.println("localTime = " + localTime);
//sout: localTime = 10:30:59
-
获取当前时间
LocalTime now = LocalTime.now();
System.out.println("now = " + now);
//sout: now = 10:04:49.918
-
获取时间信息
LocalTime localTime = LocalTime.of(10, 30, 59);
int hour = localTime.getHour();
// hour = 10
int minute = localTime.getMinute();
// minute = 30
int second = localTime.getSecond();
// second = 59
int nano = localTime.getNano();
// nano = 1120 返回纳秒级字段。此方法返回一个介于0到999,999,999之间的整数值,即一秒的纳秒数
3、LocalDateTime
-
获取指定日期时间
LocalDateTime localDateTime = LocalDateTime.of(2022, 6, 18, 10, 16, 55);
System.out.println("localDateTime = " + localDateTime);
//sout: localDateTime = 2022-06-18T10:16:55
-
获取当前日期时间
LocalDateTime now = LocalDateTime.now();
System.out.println("now = " + now);
//sout: now = 2022-06-18T10:19:43.398
-
获取日期时间信息
和LocalDate获取日期 LocalTime获取时间用法一致,可想像成两个的结合体
LocalDateTime now = LocalDateTime.now();
System.out.println("年:" + now.getYear());
System.out.println("月:" + now.getMonthValue());
System.out.println("日:" + now.getDayOfMonth());
System.out.println("时:" + now.getHour());
System.out.println("分:" + now.getMinute());
System.out.println("秒:" + now.getSecond());
/*
年:2022
月:6
日:18
时:10
分:41
秒:46
-
修改日期时间
对日期时间的修改,对已存在的LocalDateTime对象 按照原来的模板创建新的对象 并不会修改原来的日期对象
with-- 对某个值进行修改(替换)
withYear(int year);
withMonth(int month);
withDayOfMonth(int dayOfMonth);
withDayOfYear(int dayOfYear);
withHour(int hour);
withMinute(int minute);
withSecond(int second);
withNano(int nanoOfSecond)
LocalDateTime now = LocalDateTime.now(); System.out.println("年:" + now.getYear()); // 年:2022 System.out.println("now = " + now); // now = 2022-06-18T11:06:20.318 LocalDateTime localDateTime1 = now.withYear(1998); System.out.println("localDateTime1 = " + localDateTime1); //localDateTime1 = 1998-06-18T11:04:56.474 System.out.println("年:" + localDateTime1.getYear()); // 年:1998
plus-- 对某个值进行增加
plusYears(long years);
plusMonths(long months);
plusWeeks(long weeks);
plusDays(long days);
plusHours(long hours);
plusMinutes(long minutes);
plusSeconds(long seconds);
plusNanos(long nanos);
LocalDateTime now = LocalDateTime.now(); System.out.println("年:" + now.getYear()); LocalDateTime localDateTime = now.plusYears(1); System.out.println("年:" + localDateTime.getYear()); /* 年:2022 年:2023
minus-- 对某个值进行减少
方法和用法跟plus如出一辙
-
日期时间的比较
boolean isAfter(ChronoLocalDateTime<?> other) 是否在参数日期时间之后
boolean isBefore(ChronoLocalDateTime<?> other) 是否在参数日期时间之前
boolean isEqual(ChronoLocalDateTime<?> other) 是否等于参数日期时间
LocalDateTime now = LocalDateTime.now(); LocalDateTime plusDays = now.plusDays(1); LocalDateTime minusDays = now.minusDays(1); boolean after = plusDays.isAfter(now); System.out.println("after = " + after); // after = true boolean before = minusDays.isBefore(now); System.out.println("before = " + before); // before = true boolean equal = plusDays.isEqual(minusDays); System.out.println("equal = " + equal); // equal = false
4、格式化和解析操作
在jdk8中我们可以通过java.time.format.DateTimeFormatter 类可以进行日期的解析和格式化操作
格式化输出 String format(DateTimeFormatter formatter)
LocalDateTime now = LocalDateTime.now();
// 指定格式
DateTimeFormatter isoLocalDateTime = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
String format = now.format(isoLocalDateTime);
System.out.println("format = " + format);
//sout: format = 2022-06-18T14:04:43.506
// 自定义格式
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String s = now.format(dateTimeFormatter);
System.out.println("s = " + s);
//sout: s = 2022-06-18 14:04:43
字符串解析成日期对象
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime parse = LocalDateTime.parse("2022-05-20 22:20:20", dateTimeFormatter);
System.out.println("parse = " + parse);
// sout:parse = 2022-05-20T22:20:20
5、Instant类
在jdk8中新增一个Instant类 (时间戳) 1970-1-1 00:00:00 以来和秒数或者纳秒数
Instant now = Instant.now();
System.out.println("now = " + now);
// sout:now = 2022-06-18T06:19:50.220Z
// 获取从1970-1-1 00:00:00到现在的秒
long epochSecond = now.getEpochSecond();
System.out.println("epochSecond = " + epochSecond);
// sout:epochSecond = 1655533190
Thread.sleep(5000);
Instant after = Instant.now();
System.out.println("间隔时间 = " + (after.getEpochSecond() - now.getEpochSecond()));
// sout: 间隔时间 = 5
6、计算日期时间差
JDK8中提供两个工具类Duration、Period 来计算时间差
-
Duration 用来计算两个时间差 (LocalTime)Duration between(Temporal startInclusive, Temporal endExclusive)
-
Period 用来计算两个日期差(LocalDate) Period between(LocalDate startDateInclusive, LocalDate endDateExclusive)
// 通过Duration来计算时间差
LocalTime now = LocalTime.now();
LocalTime plusHours = now.plusHours(1).plusMinutes(12).plusSeconds(33);
Duration duration = Duration.between(now, plusHours);
System.out.println("duration = " + duration.toHours()+"小时"+duration.toMinutes()+"分钟"+duration.toMillis()+"秒");
// sout: duration = 1小时72分钟4353000秒
// 通过Period来计算日期差
LocalDate nowDate = LocalDate.now();
LocalDate date = LocalDate.of(2022, 10, 12);
Period between = Period.between(nowDate, date);
System.out.println("period = " + between.getYears()+"年"+between.getMonths()+"月"+between.getDays()+"日");
// sout:period = 0年3月24日
7、时间矫正器
TemporalAdjuster类是一个函数式接口,提供了一个抽象方法adjustInto 该方法被多个类实现 每个实现类下也有不同的操作 本文就不作详细展开了
LocalDateTime now = LocalDateTime.now();
System.out.println("now = " + now);
// sout: now = 2022-06-18T15:17:13.481
TemporalAdjuster adjuster = t -> t.plus(Period.ofDays(1)); // 加一天
LocalDateTime with1 = now.with(adjuster);
System.out.println("with1 = " + with1);
// sout: with1 = 2022-06-19T15:17:13.481
TemporalAdjuster adjuster1 = (temporal)->{
LocalDateTime dateTime = (LocalDateTime) temporal;
LocalDateTime localDateTime = dateTime.plusMonths(1).withDayOfMonth(1); //下个月第一天
return localDateTime;
};
LocalDateTime with = now.with(adjuster1);
System.out.println("with = " + with);
// sout: with = 2022-07-01T15:17:13.481
TemporalAdjusters类中预定义实现
TemporalAdjusters类有很多预定义的static方法返回TemporalAdjuster对象,使用不同方式调节Temporal对象而与Temporal实现无关。
dayOfWeekInMonth(2,DayOfWeek.TUESDAY) 一周中的某一天,例如,三月中第二个星期二
firstDayOfMonth() 当前月的第一天
firstDayOfNextMonth() 下一个月的第一天
firstDayOfNextYear() 下一年的第一天
firstDayOfYear() 当年的第一天
lastDayOfMonth() 当月的最后一天
lastDayOfYear() 当年的最后一天
previous(DayOfWeek.MONDAY) 上周周一
previousOrSame(DayOfWeek.MONDAY) 上周或当天发生的一周中的周一
next(DayOfWeek.MONDAY) 下周周一
nextOrSame(DayOfWeek.MONDAY) 下周或当天发生的一周中的周一
8、日期时间的时区
JDK8中加入了对时区的支持,LocalDate、LocalTime、LocalDateTime 是不带时区的,带时区的日期时间类型分别是 ZoneDate、ZoneTime、ZoneDateTime。
其中每个时区都对应着不同的ID ID的格式为“区域、城市” 例如 Asia/ShangHai等
ZoneId : 该类中包含了所有的时区信息
// 获取所有的时区id
ZoneId.getAvailableZoneIds().forEach(s->{
if (s.contains("Shang")){
System.out.println("s = " + s);
}
});
// 获取当前时间 中国使用的 东八区的时区 比标准时间早8小时
LocalDateTime now = LocalDateTime.now();
System.out.println("now = " + now);
// 获取标准时间
ZonedDateTime zonedDateTime = ZonedDateTime.now(Clock.systemUTC());
System.out.println("标准时区时间 = " + zonedDateTime);
// 使用计算机默认的时区 创建日期时间
ZonedDateTime zonedDateTime1 = ZonedDateTime.now();
System.out.println("默认时区时间 = " + zonedDateTime1);
// 使用指定的时区创建日期时间
ZonedDateTime zonedDateTime2 = ZonedDateTime.now(ZoneId.of("America/Marigot"));
System.out.println("自定义时区时间 = " + zonedDateTime2);
s = Asia/Shanghai
now = 2022-06-18T16:24:34.836
标准时区时间 = 2022-06-18T08:24:34.836Z
默认时区时间 = 2022-06-18T16:24:34.836+08:00[Asia/Shanghai]
自定义时区时间 = 2022-06-18T04:24:34.836-04:00[America/Marigot]