为什么JDK8会又新增时间相关类呢?
① JDK7的时间对象如果需要比较大小的话,必须都先转换成毫秒值;JDK8则不需要,可以直接比较。
② JDK7的时间对象可以修改,在多线程环境下就会导致数据不安全;JDK8不能修改,不会发生安全问题。
JDK8时间类一共有10个类,
前3个类似于JDK7中的Date类,
第4个类似于JDK7中的 SimpleDateFormat 类,
第5~7类类似于JDK7中的 Calendar 类,
最后三个类用于计算时间间隔。
一、ZoneId时区类
1. getAvailableZoneIds方法
public class Demo {
public static void main(String[] args) {
//1.getAvailableZoneIds 获取所有的时区名称
Set<String> zoneIds = ZoneId.getAvailableZoneIds();
System.out.println(zoneIds.size());//603个时区
System.out.println(zoneIds);
}
}
运行结果:
2. systemDefault方法
public class Demo {
public static void main(String[] args) {
//2.systemDefault 获取系统默认时区
ZoneId zoneId = ZoneId.systemDefault();
System.out.println(zoneId);//Asia/Shanghai
}
}
如果想要更改系统默认时区:
① 点击系统设置
② 点击 “时间和语言”
③ 点击 "日期和时间"
④ 选择时区
比如将时区修改为 台北:
再次获取系统默认时区:
3. of 方法
public class Demo {
public static void main(String[] args) {
//3.of 获取指定的时区
ZoneId zoneId = ZoneId.of("Asia/Pontianak");
System.out.println(zoneId);//Asia/Pontianak
}
}
获取到时区之后就可以结合后续类进行操作了。
二、Instant时间戳类
1.now方法
public class Demo {
public static void main(String[] args) {
//1.now 获取当前时间的时间戳对象(标准时间)
Instant now=Instant.now();
System.out.println(now);
}
}
细节:
now方法获取的是标准时间(不含时区),如果想获得我国的时间,还要在此基础上+8h。
2. ofxxx方法
public class Demo {
public static void main(String[] args) {
//2.ofxxx 根据指定的(秒/毫秒/纳秒)获取时间戳对象
Instant instant1 = Instant.ofEpochMilli(0L);//距时间原点0ms
System.out.println(instant1);
Instant instant2 = Instant.ofEpochSecond(1L);//距时间原点1s
System.out.println(instant2);
Instant instant3 = Instant.ofEpochSecond(1L, 1000000000L);
//距时间原点1s + 10^9ns = 2s
System.out.println(instant3);
}
}
运行结果:
细节:
用ofxxx方法获取指定时间的时间戳对象,是不带时区的。
3.atZone方法
public class Demo {
public static void main(String[] args) {
//3.atZone 获取指定时区的时间戳对象
ZonedDateTime time = Instant.now().atZone(ZoneId.of("Asia/Shanghai"));
System.out.println(time);
}
}
运行结果:
细节:
atZone方法不是静态方法,不能通过类名调用,需要通过 对象名.方法名 调用。
4.isxxx方法
public class Demo {
public static void main(String[] args) {
//4.isxxx 用于时间的判断
Instant instant1 = Instant.ofEpochMilli(0L);
Instant instant2 = Instant.ofEpochMilli(1000L);
//isbefore 判断调用者(instant1)代表的时间是否在参数(instant2)代表的时间之前
boolean result1 = instant1.isBefore(instant2);
System.out.println(result1);//true
//isAfter 判断调用者(instant1)代表的时间是否在参数(instant2)代表的时间之前之后
boolean result2 = instant1.isAfter(instant2);
System.out.println(result2);//false
}
}
细节:
使用isxxx方法无需将时间转换成毫秒值,就可以直接对两个时间对象进行比较了。
5.minusxxx方法和plusxxx方法
public class Demo {
public static void main(String[] args) {
//5.minusxxx 减少时间
Instant instant1 = Instant.ofEpochMilli(3000L);
System.out.println(instant1);//1970-01-01T00:00:03Z
//minusSeconds:减少xx秒
//minusMillis:减少xx毫秒
//minusNanos:减少xx纳秒
Instant instant2 = instant1.minusSeconds(1L);
System.out.println(instant2);//1970-01-01T00:00:02Z
//6.plusxxx 增加时间(方法和minusxxx同理)
Instant instant3=instant1.plusSeconds(1L);
System.out.println(instant3);//1970-01-01T00:00:04Z
}
}
细节:
JDK8之后,创建的时间对象是不可能发生改变的。
所以,增加/减少后,原有时间对象并没有改变,而是创建了一个新的时间对象。
三、ZoneDateTime带时区的时间类
1.now方法和of方法
public class Demo {
public static void main(String[] args) {
//1.now 获取当前的时间对象(带时区)
ZonedDateTime now = ZonedDateTime.now();
System.out.println(now);
//2.of 获取指定的时间对象(带时区)
//① 指定方式一 --> 年,月,日,时,分,秒,纳秒,时区
ZonedDateTime time1 = ZonedDateTime.of(2024, 5, 20,
13, 14, 20, 0, ZoneId.of("Asia/Shanghai"));
System.out.println(time1);
//② 指定方式二 --> Instant + 时区
Instant instant = Instant.ofEpochMilli(0L);
ZoneId zoneId = ZoneId.of("Asia/Shanghai");
ZonedDateTime time2 = ZonedDateTime.ofInstant(instant, zoneId);
System.out.println(time2);
}
}
运行结果:
细节:
① 通过运行结果可以看出,ZonedDateTime类创建的时间对象是带时区的
② of 方法有两种指定方式:
一种是 年,月,日,时,分,秒,纳秒,时区
另一种是 Instant + 时区
2.withxxx方法、minusxxx方法和plusxxx方法
public class Demo {
public static void main(String[] args) {
Instant instant = Instant.ofEpochMilli(0L);
ZoneId zoneId = ZoneId.of("Asia/Shanghai");
ZonedDateTime time1 = ZonedDateTime.ofInstant(instant, zoneId);
System.out.println("time1:"+time1);
//3.withxxx 修改时间
ZonedDateTime time2 = time1.withYear(2000);
System.out.println("time2:"+time2);
//4.minusxxx 减少时间
ZonedDateTime time3 = time2.minusYears(1);
System.out.println("time3:"+time3);
//5.plusxxx 增加时间
ZonedDateTime time4 = time3.plusYears(1);
System.out.println("time4:"+time4);
}
}
细节:
① 无论是修改,减少还是增加时间,都没有修改原有的时间对象,而是创建了一个新的对象
② 修改,减少,增加都有很多的方法,可以修改各种各样的时间,如下:
四、DateTimeFormatter类
public class Demo {
public static void main(String[] args) {
//获取时间对象
ZonedDateTime time= Instant.now().atZone(ZoneId.of("Asia/Shanghai"));
//1.ofPattern 获取格式对象
DateTimeFormatter dtf=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss EE a");
//2.format 按照指定方式格式化
String str= dtf.format(time);
System.out.println(str);//2024-05-26 21:36:06 周日 下午
}
}
细节:
DateTimeFormatter 类和 SimpleDateFormat 类一样,都是用于时间的格式化和解析。
但:
DateTimeFormatter 类是不可变的,一旦创建就不可更改。
SimpleDateFormat 类是可变的,可以通过方法调用更改其状态。
所以 DateTimeFormatter 类也解决了因为多线程环境而造成的安全性问题。
五、LocalDate类、LocalTime类和LocalDateTime类
该3个类类似于JDK7中的 Calendar 类,使用方法也极其相似。
可以获得时间(日历)对象,取出某个字段值,或者修改、减少/增加某个字段值。
注意:
在JDK7种,月份范围是0~11,与实际月份差1。
而在JDK8种,月份范围是1~12,与实际月份相同。
除此之外,LocalDateTime 对象还可以转换成 LocalDate 对象和 LocalTime 对象
1.LocalDate类
public class LocalDateDemo {
public static void main(String[] args) {
//1.获取当前时间的日历对象(包含:年月日)
LocalDate nowDate = LocalDate.now();
System.out.println("今天的日期:" + nowDate);//2024-05-26
System.out.println("----------------------------------");
//2.获取指定的时间的日历对象
LocalDate ldDate = LocalDate.of(2023, 1, 1);
System.out.println("指定日期:" + ldDate);//2023-01-01
System.out.println("----------------------------------");
//3.get系列方法获取日历中的每一个属性值
int year = ldDate.getYear();
System.out.println("year:" + year);
System.out.println("----------------------------------");
//获取月
//方式一
Month m = ldDate.getMonth();
System.out.println(m);//JANUARY
System.out.println(m.getValue());
System.out.println("----------------------------------");
//方式二
int month = ldDate.getMonthValue();
System.out.println("month:" + month);
System.out.println("----------------------------------");
//获取日
int day = ldDate.getDayOfMonth();
System.out.println("day:" + day);
System.out.println("----------------------------------");
//获取一年的第几天
int dayOfYear = ldDate.getDayOfYear();
System.out.println("dayOfYear:" + dayOfYear);
System.out.println("----------------------------------");
//获取星期
DayOfWeek dayOfWeek = ldDate.getDayOfWeek();
System.out.println(dayOfWeek);//SUNDAY
System.out.println(dayOfWeek.getValue());
System.out.println("----------------------------------");
//4.is开头的方法表示判断
System.out.println(ldDate.isBefore(ldDate));
System.out.println(ldDate.isAfter(ldDate));
System.out.println("----------------------------------");
//5.with开头的方法表示修改,只能修改年月日
LocalDate withLocalDate = ldDate.withYear(2000);
System.out.println(withLocalDate);
System.out.println("----------------------------------");
//6.minus开头的方法表示减少,只能减少年月日
LocalDate minusYears = ldDate.minusYears(1);
System.out.println(minusYears);
System.out.println("----------------------------------");
//7.plus开头的方法表示增加,只能增加年月日
LocalDate plusYears = ldDate.plusYears(10);
System.out.println(plusYears);
System.out.println("----------------------------------");
}
}
细节:
① 通过getMonth 和 getDayOfWeek 方法,获取到的都是对象,不是值。
② LocalDate类的with、minus和plus方法,都只能修改年月日。
③ 修改后不会改变原对象(调用者)的值,而是创建了一个新的对象。
2.LocalTime类
public class LocalTimeDemo {
public static void main(String[] args) {
// 获取本地时间的日历对象。(包含 时分秒)
LocalTime nowTime = LocalTime.now();
System.out.println("今天的时间:" + nowTime);
int hour = nowTime.getHour();//时
System.out.println("hour: " + hour);
int minute = nowTime.getMinute();//分
System.out.println("minute: " + minute);
int second = nowTime.getSecond();//秒
System.out.println("second:" + second);
int nano = nowTime.getNano();//纳秒
System.out.println("nano:" + nano);
System.out.println("------------------------------------");
System.out.println(LocalTime.of(8, 20));//时分
System.out.println(LocalTime.of(8, 20, 30));//时分秒
System.out.println(LocalTime.of(8, 20, 30, 150));//时分秒纳秒
LocalTime mTime = LocalTime.of(8, 20, 30, 150);
//is系列的方法
System.out.println(nowTime.isBefore(mTime));
System.out.println(nowTime.isAfter(mTime));
//with系列的方法,只能修改时、分、秒
System.out.println(nowTime.withHour(10));
//minus系列的方法,只能减少时、分、秒
System.out.println(nowTime.minusHours(10));
//plus系列的方法,只能增加时、分、秒
System.out.println(nowTime.plusHours(10));
}
}
细节:
① LocalTime类的get方法只能获取时分秒(纳秒)。
② LocalTime类的with、minus和plus方法,都只能修改时分秒(纳秒)。
③ 修改后不会改变原对象(调用者)的值,而是创建了一个新的对象。
3.LocalDateTime类
public class LocalDateTimeDemo {
public static void main(String[] args) {
// 当前时间的的日历对象(包含年月日时分秒)
LocalDateTime nowDateTime = LocalDateTime.now();
System.out.println("今天是:" + nowDateTime);//今天是:
System.out.println(nowDateTime.getYear());//年
System.out.println(nowDateTime.getMonthValue());//月
System.out.println(nowDateTime.getDayOfMonth());//日
System.out.println(nowDateTime.getHour());//时
System.out.println(nowDateTime.getMinute());//分
System.out.println(nowDateTime.getSecond());//秒
System.out.println(nowDateTime.getNano());//纳秒
// 日:当年的第几天
System.out.println("dayofYear:" + nowDateTime.getDayOfYear());
//星期
System.out.println(nowDateTime.getDayOfWeek());
System.out.println(nowDateTime.getDayOfWeek().getValue());
//月份
System.out.println(nowDateTime.getMonth());
System.out.println(nowDateTime.getMonth().getValue());
//转换成LocalDate对象
LocalDate ld = nowDateTime.toLocalDate();
System.out.println(ld);
//转换成LocalTime对象
LocalTime lt = nowDateTime.toLocalTime();
System.out.println(lt.getHour());
System.out.println(lt.getMinute());
System.out.println(lt.getSecond());
}
}
细节:
① LocalDateTime 类的信息最全,包含年月日时分秒
② 修改后不会改变原对象(调用者)的值,而是创建了一个新的对象。
③ LocalDateTime 对象还可以转换成 LocalDate 对象和 LocalTime 对象。
六、工具类
1.Period类
public class PeriodDemo {
public static void main(String[] args) {
//当前本地 年月日
LocalDate today=LocalDate.now();
System.out.println(today);//2024-05-28
//出生日期 年月日
LocalDate birth=LocalDate.of(2001,1,1);
Period period= Period.between(birth,today);
System.out.println("相差的时间间隔对象"+ period);//P23Y4M27D
//相差的年月日
System.out.println(period.getYears());//23
System.out.println(period.getMonths());//4
System.out.println(period.getDays());//27
//相差的总月份
System.out.println(period.toTotalMonths());//280
}
}
细节:
① 在调用between方法计算时间间隔时,是第二个参数减去第一个参数 。
② Period 类侧重于年月日的计算,可以通过getxxx方法,获取 Period 时间间隔对象的年月日。
③ 可以通过 toxxx 方法转换成总共相差的年/月/日。
2.Duration类
public class DurationDemo {
public static void main(String[] args) {
//当前本地时间对象
LocalDateTime today = LocalDateTime.now();
System.out.println(today);//2024-05-28T15:21:29.975429400
//出生的日期时间对象
LocalDateTime birth = LocalDateTime.of(2000, 1, 1, 00, 00, 00);
Duration duration = Duration.between(birth, today);
System.out.println("相差的时间间隔对象" + duration);//PT213951H21M29.9754294S
//获取时间间隔对象的纳秒
System.out.println(duration.getNano());
//相差的总天数/小时数/分钟数/秒数/毫秒数/纳秒数
System.out.println(duration.toDays());//8914
System.out.println(duration.toHours());
System.out.println(duration.toMinutes());
System.out.println(duration.toSeconds());
System.out.println(duration.toMillis());
System.out.println(duration.toNanos());
}
}
细节:
① 在调用between方法计算时间间隔时,是第二个参数减去第一个参数 。
② Duration类侧重于秒和纳秒的计算,相对来说比较精确一点,可以通过getNano方法,获取 Period 时间间隔对象的纳秒值。
③ 可以通过 toxxx 方法转换成总共相差的 天数/小时数/分钟数/秒数/毫秒数/纳秒数。
3.ChronoUnit类
public class ChronoUnitDemo {
public static void main(String[] args) {
// 当前本地时间对象
LocalDateTime today = LocalDateTime.now();
System.out.println(today);//2024-05-28T15:42:34.595451500
// 出生的日期时间对象
LocalDateTime birthDate = LocalDateTime.of(2000, 1, 1,
0, 0, 0);
//ChronoUnit.XXX.between() 可以计算两个时间相差的XXX
System.out.println("相差的年数:" + ChronoUnit.YEARS.between(birthDate, today));
System.out.println("相差的月数:" + ChronoUnit.MONTHS.between(birthDate, today));
System.out.println("相差的周数:" + ChronoUnit.WEEKS.between(birthDate, today));
System.out.println("相差的天数:" + ChronoUnit.DAYS.between(birthDate, today));
System.out.println("相差的时数:" + ChronoUnit.HOURS.between(birthDate, today));
System.out.println("相差的分数:" + ChronoUnit.MINUTES.between(birthDate, today));
System.out.println("相差的秒数:" + ChronoUnit.SECONDS.between(birthDate, today));
System.out.println("相差的毫秒数:" + ChronoUnit.MILLIS.between(birthDate, today));
System.out.println("相差的微秒数:" + ChronoUnit.MICROS.between(birthDate, today));
System.out.println("相差的纳秒数:" + ChronoUnit.NANOS.between(birthDate, today));
System.out.println("相差的半天数:" + ChronoUnit.HALF_DAYS.between(birthDate, today));
System.out.println("相差的十年数:" + ChronoUnit.DECADES.between(birthDate, today));
System.out.println("相差的世纪(百年)数:" + ChronoUnit.CENTURIES.between(birthDate, today));
System.out.println("相差的千年数:" + ChronoUnit.MILLENNIA.between(birthDate, today));
System.out.println("相差的纪元数:" + ChronoUnit.ERAS.between(birthDate, today));
}
}
细节:
① 最为常用,因为其能计算的单位非常多,是最全面的。
② 通过 ChronoUnit.XXX.between() 可以计算两个时间相差的XXX