从JDK1.8开始,Java提供了java.time 包,该包用于描述ISO日历系统中的日期和时间。通过该包下的类可以实现分别访问日期、时间和时间戳等对象。 不像java.util.Date , 一个类包含日期和时间所有信息。比如你可以使用LocalDate对象来表示日期,使用LocalTime对象表示时间,用LoalDateTime对象表示日期时间。
概述
? 从JDK1.8开始,Java增加了java.time 包,此包下定义了代表日期时间概念的类,包括瞬间、持续时间、日期、时间、时区和周期。 这些类基于ISO日历系统定义的,他们都是不可变的且线程安全的。java.time 包中定义了很多用于描述时间的类,这里我们选择其中一部分进行讲解,首先我们看下他们的类关系图,如下所示:
- Temporal:日期、时间和偏移时间的接口类,它定义了Temporal接口对象的加减运算、创建对象等方法。
- TemporalAccessor:定义了访问Temporal对象的方法,比如获取日期对象中的年、月、日属性。
- TemporalAdjuster:修改Temporal的接口。
- LocalDate:用于表示日期,比如“2022-3-30”。
- LocalTime:用于表示时间,比如“16:15:45.326657”。
- LocalDateTime:用于表示日期和时间,但不包含时区,时间精确到纳秒,比如“2022-03-30T16:15:45.836657”。
- ZoneId:用于表示时区ID,有三种表达形式:
- 以字符’ Z’或者’+’、’-‘开头,其中’Z’表示0时区,’+‘表示东时区,’-'表示西时区,比如"+8"表示东八区;
- 以’UTC’、‘GMT’、'UT’为前缀,加上偏移时间,比如"UTC+8"表示东八区;
- 基于地区定义的ID,比如"Asia/Shanghai";
- Instant:用于表示某个瞬间,可以理解为时间戳;
- Clock:用于使用时区访问瞬间、日期和时间的时钟对象;Clock是抽象类,有四个嵌套子类:FixedClock、OffsetClock、SystemClock、TickClock。
- ZonedDateTime:用于表示包含时区信息的日期时间类。LocalDateTime不存储时区信息,如果想要对象保存时区信息,那么可该用ZonedDateTime对象。从上面的类图也可看出该类依赖LocalDateTime、ZoneId和ZoneOffset;
- ZoneOffset:用于描述与格林威治/UTC 的时区偏移量,比如我国的时区偏移量为 “+08:00”。
接下来我们来了解下其中的几个类,他们分别是LocalDate, LocalTime和LocalDateTime(备注,文中的代码是基于JDK11,有部分方法在JDK1.8上未定义)。
LocalDate
? LocalDate 是用于描述ISO-8601日历系统中日期,但不包括时区时间信息。LocalDate 正如其名,此类不存储时间或时区,也就是说不能通过LocalDate对象获取时间或者时区;它只用于描述日期,通常为“年-月-日”。通过LocalDate提供的实例方法也可以访问其他日期字段,比如一年中的某一天、一周中的某一天和一年中某一周。LocalDate对象是不可以变对象,正因如此它也是线程安全的。
常见方法
now()
? now() 有三个重载方法,他们最终是通过ofInstant方法实现创建LocalDate对象,具体细节在此不展开,感兴趣的同学可以看下源码,我们主要看下这三个重载方法的使用。
private void localDate() {
LocalDate localDate = LocalDate.now();
Log.print("localDate: " + localDate);
LocalDate localDateByClock = LocalDate.now(Clock.system(ZoneId.of("UTC-10")));
Log.print("localeDate by clock: " + localDateByClock);
LocalDate localDateByZoneId = LocalDate.now(ZoneId.of("UT-9"));
Log.print("localeDate by zone id: " + localDateByZoneId);
}
输出结果如下:
localDate: 2022-03-27
localeDate by clock: 2022-03-26
localeDate by zone id: 2022-03-27
代码运行的时间是2022年3月27日17:30,所在的时区为“UTC+8”,所以第一行打印是"2022-03-27",localDateByClock 对象指定的时区为“UTC-10”,所以与localDate 所在的时区相差18个小时,所以第二行打印为"2022-03-26",localDateByZoneId 指定的时区与localDate 所在的时区相差17个小时,所以第三行打印为"2022-03-27"。(备注,文中的Log.print() 是对系统打印的简单封装)
ofxxx()
LocalDate有多个ofxxx()方法,我们通过例子来看下其中的几个:
private void localeDateOf() {
LocalDate localDate = LocalDate.of(2022, 2, 28);
Log.print("localDate: " + localDate);
LocalDate localDateOf = LocalDate.of(2022, Month.FEBRUARY, 28);
Log.print("localDateOf: " + localDateOf);
LocalDate localDateYearOfDay = LocalDate.ofYearDay(2022, 60);
LocalDate localDate2020OfDay = LocalDate.ofYearDay(2020, 60);
Log.print("localDateYearOfDay: " + localDateYearOfDay + ", localDate2020OfDay: " + localDate2020OfDay);
LocalDate localDateByInstant = LocalDate.ofInstant(Instant.now(), ZoneId.of("UTC-10"));
LocalDate localDateByInstantDefaultZoneDate = LocalDate.ofInstant(Instant.now(),
ZoneId.systemDefault());
Log.print("localDateByInstant: " + localDateByInstant
+ ", localDateByInstantDefaultZoneDate: " + localDateByInstantDefaultZoneDate);
}
localDate: 2022-02-28
localDateOf: 2022-02-28
localDateYearOfDay: 2022-03-01, localDate2020OfDay: 2020-02-29
localDateByInstant: 2022-03-26, localDateByInstantDefaultZoneDate: 2022-03-27
运算
? 运算包括对年、月、周、日的加减操作,对LocalDate对象的加减操作返回的是LocalDate对象的副本,LocalDate对象本身不会发生变化,因为LocalDate对象是不可变的。具体看实际例子
private void localDateOperation() {
LocalDate localDate = LocalDate.now();
Log.print("localDate: " + localDate + ", plus day: " + localDate.plusDays(1)
+ ", plus week: " + localDate.plusWeeks(1)
+ ", plus month: " + localDate.plusMonths(1)
+ ", plus year: " + localDate.plusYears(1));
Log.print("localDate: " + localDate + ", minus day: " + localDate.minusDays(1)
+ ", minus week: " + localDate.minusWeeks(1)
+ ", minus month" + localDate.minusMonths(1)
+ ", minus year: " + localDate.minusYears(1));
}
localDate: 2022-03-27, plus day: 2022-03-28, plus week: 2022-04-03, plus month: 2022-04-27, plus year: 2023-03-27
localDate: 2022-03-27, minus day: 2022-03-26, minus week: 2022-03-20, minus month2022-02-27, minus year: 2021-03-27
从上述的打印也能看出,对localDate 中的属性加减运算,并没有改变localDate 。
格式化
? 可将LocalDate对象格格式化,也可通过LocalDate.parse()方法将格式化后的字符生成LocalDate对象。关于格式化字符模式可参考DateTimeFormatterBuilder#appendPattern(String pattern) 方法注释
private void localDateFormat() {
LocalDate localDate = LocalDate.now();
String formated = localDate.format(DateTimeFormatter.ofPattern("YYYY-MM-dd"));
Log.print("formated date: " + formated);
LocalDate parsedDate = LocalDate.parse(formated);
Log.print("parsed date: " + parsedDate);
}
formated date: 2022-03-27
parsed date: 2022-03-27
其他
LocalDate还提供了其他一些类和实例的方法,这里我们通过下面的例子了解下:
private void localDateOther() {
LocalDate localDate = LocalDate.now();
int year = localDate.getYear();
int month = localDate.getMonthValue();
int day = localDate.getDayOfMonth();
int dayOfYear = localDate.getDayOfYear();
DayOfWeek dayOfWeek = localDate.getDayOfWeek();
Log.print("localeDate: " + localDate + ", year: " + year
+ ", month: " + month + ", day of month: " + day
+ ", dayOfYear: " + dayOfYear + ", dayOfWeek: " + dayOfWeek
);
int lenOfYear = localDate.lengthOfYear();
int lenOfMonth = localDate.lengthOfMonth();
boolean leap = localDate.isLeapYear();
Log.print("length of year: " + lenOfYear + ", length of month: " + lenOfMonth
+ ", is leap year: " + leap);
LocalDate dayLocalDate = localDate.withDayOfMonth(12);
LocalDate monthLocalDate = localDate.withMonth(2);
LocalDate yearLocalDate = localDate.withYear(2020);
Log.print("dayLocalDate: " + dayLocalDate
+ ", monthLocalDate: " + monthLocalDate
+ ", yearLocalDate: " + yearLocalDate
);
LocalDate from = LocalDate.from(LocalDate.of(2022, 3, 27));
Log.print("from: " + from);
}
输出结果如下:
localeDate: 2022-03-27, year: 2022, month: 3, day of month: 27, dayOfYear: 86, dayOfWeek: SUNDAY
length of year: 365, length of month: 31, is leap year: false
dayLocalDate: 2022-03-12, monthLocalDate: 2022-02-27, yearLocalDate: 2020-03-27
from: 2022-03-27
LocalTime
? LocalTime 是用于描述ISO-8601日历系统中不含时区的时间,比如:18:15:30。和LocalDate一样,LocalTime也是不可变对象,LocalTime精确到纳秒,比如"13:45.30.123456789"。同样,LocalTime也不存储时区信息,也就是说不能通过LocalTime对象获时区信息。
常见方法
now()
now() 有三个重载方法,最终是通过ofInstant方法创建LocalTime对象。
private void localTime() {
LocalTime localTime = LocalTime.now();
Log.print("localTime: " + localTime);
LocalTime localTimeByClock = LocalTime.now(Clock.system(ZoneId.of("UTC+9")));
Log.print("localTimeByClock: " + localTimeByClock);
LocalTime localTimeById = LocalTime.now(ZoneId.of("UT+7"));
Log.print("localTimeById: " + localTimeById);
}
输出结果:
localTime: 16:19:40.943196300
localTimeByClock: 17:19:40.968182600
localTimeById: 15:19:40.968182600
ofxxx()
同样,LocalTime也有多个ofxxx()方法,我们通过例子来看下其中的几个:
private void localTimeOf() {
LocalTime hM = LocalTime.of(14, 20);
LocalTime hMS = LocalTime.of(14, 20, 30);
LocalTime hMSN = LocalTime.of(14, 20, 30, 5000_0000);
LocalTime instantTime = LocalTime.ofInstant(Instant.now(), ZoneId.systemDefault());
LocalTime ofSecondOfDay = LocalTime.ofSecondOfDay(60);
LocalTime from = LocalTime.from(LocalDateTime.now());
Log.print("hM: " + hM + ", hMS: " + hMS + ", hMSN: " + hMSN
+ ", instantTime: " + instantTime
+ ", ofSecondOfDay: " + ofSecondOfDay
+ ", from: " + from
);
}
输出结果:
hM: 14:20, hMS: 14:20:30, hMSN: 14:20:30.050, instantTime: 16:19:40.968182600, ofSecondOfDay: 00:01, from: 16:19:40.969182
运算
LocalTime也定义了对时、分、秒和毫秒的加减运算,同样对LocalTime的加减运算并不会改变对象本身,运算结果返回的是原对象的副本。
private void localTimeOperation() {
LocalTime localTime = LocalTime.now();
Log.print("localTime: " + localTime + ", plus hour: " + localTime.plusHours(1)
+ ", plus minute: " + localTime.plusMinutes(1)
+ ", plus second: " + localTime.plusSeconds(1)
+ ", plus nanos: " + localTime.plusNanos(1));
Log.print("localTime: " + localTime + ", minus hour: " + localTime.minusHours(1)
+ ", minus minute: " + localTime.minusMinutes(1)
+ ", minus second" + localTime.minusSeconds(1)
+ ", minus nanos: " + localTime.minusNanos(1));
}
输出结果
localTime: 17:27:35.313468800, plus hour: 18:27:35.313468800, plus minute: 17:28:35.313468800, plus second: 17:27:36.313468800, plus nanos: 17:27:35.313468801
localTime: 17:27:35.313468800, minus hour: 16:27:35.313468800, minus minute: 17:26:35.313468800, minus second17:27:34.313468800, minus nanos: 17:27:35.313468799
格式化
可将LocalTime对象格格式化,也可将格式化的时间通过LocalTime.parse()方法将格式化的字符转换成LocalTime对象。
private void localTimeFormat() {
LocalTime localTime = LocalTime.now();
String formatTime = localTime.format(DateTimeFormatter.ofPattern("HH:mm:ss"));
LocalTime parsedTime = LocalTime.parse(formatTime);
Log.print("formatTime: " + formatTime + ", parsedTime: " + parsedTime);
}
输出结果
formatTime: 16:19:41, parsedTime: 16:19:41
和LocalDate类似,LocalTime实例也有获取和替换时间属性(时、分、秒、纳秒)方法以及一些其他方法,这里就不展开,直接看个例子
private void localTimeOther() {
LocalTime localTime = LocalTime.now();
int hour= localTime.getHour();
int minute = localTime.getMinute();
int second = localTime.getSecond();
int nanos = localTime.getNano();
Log.print("localTime: " + localTime + ", hour: " + hour
+ ", minute: " + minute
+ ", second: " + second
+ ", nanos: " + nanos
);
Log.print("localTime: " + localTime + ", with hour: " + localTime.withHour(20)
+ ", with minute: " + localTime.withMinute(30)
+ ", with second: " + localTime.withSecond(30)
+ ", with nanos: " + localTime.withNano(30)
);
}
输出结果:
localTime: 17:35:08.411439700, hour: 17, minute: 35, second: 8, nanos: 411439700
localTime: 17:35:08.411439700, with hour: 20:35:08.411439700, with minute: 17:30:08.411439700, with second: 17:35:30.411439700, with nanos: 17:35:08.000000030
LcalDateTime
? LocalDateTime是LocalDate和LocalTime的组合,从它的命名也能猜出它是用于表示日期和时间的,比如“2022-03-29 17:42:30.988799200”,同样LocalDateTime 也是一个不可变的日期时间对象。也可以通过LocalDateTime对象访问其日期和时间字段,比如年、月、日、时、分、秒。LocalDateTime也不存储时区信息,也就是说不能通过LocalDateTime对象获时区信息。如果想要包含时区的日期时间对象,那么可使用ZoneDateTime对象。
常见方法
now()
now() 有三个重载方法,最终是通过ofInstant方法创建LocalDateTime对象。
private void localDateTime() {
LocalDateTime localDateTime = LocalDateTime.now();
Log.print("localDateTime: " + localDateTime);
LocalDateTime localDateTimeByClock = LocalDateTime.now(Clock.system(ZoneId.of("UTC+9")));
Log.print("localDateTimeByClock: " + localDateTimeByClock);
LocalDateTime localDateTimeById = LocalDateTime.now(ZoneId.of("UT+7"));
Log.print("localTimeById: " + localDateTimeById);
}
输出结果:
localDateTime: 2022-03-29T17:47:34.988799200
localDateTimeByClock: 2022-03-29T18:47:35.026780500
localTimeById: 2022-03-29T16:47:35.026780500
对于上面的输出,在日期和时间之间存在字母T ,这是因为LocalDateTime#toString() 方法中在日期和时间加上了字母T 。
ofxxx()
同样,LocalDateTime也有多个ofxxx()方法,我们通过例子来看下其中的几个:
private void localDateTimeOf() {
LocalDateTime yMDHM = LocalDateTime.of(2022, 3, 21, 14, 20);
LocalDateTime yMDHS = LocalDateTime.of(2022, 3, 21, 14, 20, 20);
LocalDateTime yMDHSN = LocalDateTime.of(2022, 3, 21, 14, 20, 20, 968182600);
LocalDateTime instantDateTime = LocalDateTime.ofInstant(Instant.now(), ZoneId.systemDefault());
Log.print("yMDHM: " + yMDHM + ", yMDHS: " + yMDHS
+ ", yMDHSN: " + yMDHSN
+ ", instantDateTime: " + instantDateTime
);
}
输出结果:
yMDHM: 2022-03-21T14:20, yMDHS: 2022-03-21T14:20:20, yMDHSN: 2022-03-21T14:20:20.968182600, instantDateTime: 2022-03-29T17:47:35.027777700
格式化
private void localDateTimeFormat() {
LocalDateTime localDateTime = LocalDateTime.now();
String formatDateTime = localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
LocalDateTime parsedDateTime = LocalDateTime.parse(formatDateTime,
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
Log.print("formatDateTime: " + formatDateTime + ", parsedTime: " + parsedDateTime);
}
输出结果:
formatDateTime: 2022-03-29 17:47:35, parsedDateTime: 2022-03-29T17:47:35
同样LocalDateTime也提供了访问和替换日期时间属性以及其他的一些方法,具体大家可查看源码学习,这里也不展开了。
转换
? 在对LocalDate、LocalTime和LoalDateTime有一定了解后,我们再来看下他们之间的转换。
LocalDate 与LocalDateTime之间的转换
LocalDate和LocalDateTime之间的转换实际上就是增加或减少一些属性;比如LocalDate对象转换成LocalDateTime,那就是在LocalDate对象的基础上增加时间信息,要实现这个功能可借助LocalDate#atTime() 、LocalDate.atStartOfDay() 方法。对于LocalDateTime转换成LocaDate对象,那就更简单了,只要获取它的LocalDate类型变量就可以了。
private void localDateLocalDateTime() {
LocalDate localDate = LocalDate.now();
LocalDateTime atTime = localDate.atTime(10, 20, 30);
LocalDateTime startOfDay = localDate.atStartOfDay();
Log.print("localDate: " + localDate + ", atTime: " + atTime + ", startOfDay: " + startOfDay);
LocalDateTime localDateTime = LocalDateTime.now();
LocalDate ld = localDateTime.toLocalDate();
Log.print("localDateTime: " + localDateTime + ", ld: " + ld);
}
输出结果
localDate: 2022-03-29, atTime: 2022-03-29T10:20:30, startOfDay: 2022-03-29T00:00
localDateTime: 2022-03-29T19:17:25.253960, ld: 2022-03-29
LocalTime 与LocalDateTime之间的转换
? LocalTime 与LocalDateTime之间的转换与上面的情况类似,LocalTime借LocalTime#atDate() 方法转换成LocalDateTime对象;LocalDateTime获取其LocalTime类型属性即可。
private void localTimeLocalDateTime() {
LocalTime localTime = LocalTime.now();
LocalDateTime localDateTime = localTime.atDate(LocalDate.now());
LocalTime lt = localDateTime.toLocalTime();
Log.print("localDateTime: " + localDateTime + ", lt: " + lt);
}
输出结果
localDateTime: 2022-03-29T19:27:25.267052, lt: 19:27:25.267052
LocalDateTime 与时间戳之间的转换
? LocalDateTime与时间戳(从1970-01-01-00:00:00经过的时间)之间的转换需要借助Instant对象,且需要指定时区,因为LocalDateTime对象是没有存储时区信息。他们之间的转换需要注意:当把LocalDateTime通过指定的时区转换成时间戳之后,这个时间戳就和该时区关联了;如果再把转换后得到的时间戳转换为LocalDateTime,那就需要使用同样的时区,否则转换后得到的LocalDateTime和之前的所对应的日期时间就不一样了,这一点可以通过下面的例子得到验证。
private void localDateTimeMillis() {
ZoneOffset zoneOffset6 = ZoneOffset.of("+6");
LocalDateTime localDateTime = LocalDateTime.now(zoneOffset6);
Log.print("localDateTime: " + localDateTime);
long millis = localDateTime.toInstant(zoneOffset6).toEpochMilli();
ZoneOffset zoneOffset8 = ZoneOffset.of("+8");
Instant instant = Instant.ofEpochMilli(millis);
LocalDateTime ldSameZoneOffset = LocalDateTime.ofInstant(instant, zoneOffset6);
LocalDateTime ldDiffZoneOffset = LocalDateTime.ofInstant(instant, zoneOffset8);
Log.print("instant millis: " + millis + ", ldSameZoneOffset: "
+ ldSameZoneOffset + ", ldDiffZoneOffset: " + ldDiffZoneOffset);
}
输出结果:
localDateTime: 2022-03-30T13:40:26.222052300
instant millis: 1648626026222, ldSameZoneOffset: 2022-03-30T13:40:26.222, ldDiffZoneOffset: 2022-03-30T15:40:26.222
如打印所示:ldSameZoneOffset采用的是和locaDateTime转换时间戳相同的ZoneOffset,所以得到的日期时间和localDateTime相同;而ldDiffZoneOffset采用的是和locaDateTime转换时间戳相同的ZoneOffset,所以得到的日期时间和localDateTime不同。
|