java 8 新的日期和时间API
LocalDate、LocalTime、Instant、Duration和Period
使用LocalDate和LocalTime
LocalDate是一个不可变对象,提供了简单的日期,不包含时间,也没有附带任何时区相关的信息。你可以通过静态方法of创建一个LocalDate实例。LocalDate提供了很多实用的方法来读取常用的值,比如年份、月份、星期几等。
LocalDate date = LocalDate.of(2014,3,18);
int year = date.getYear(); // 2014
Month month = date.getMonth(); // MARCH
int day = month.dayOfMonth(); // 18
DayOfWeek dow = date.getDayOfWeek(); // TUESDAY
int len = date.lengthOfMonth(); // 31(days in March)
boolean leap = date.isLeapYear(); // false
// 也可以通过工厂方法从系统时钟获取当前日期
LocalDate today = LocalDate.now();
你还可以传递一个TemporalField参数拿到同样的信息。TemporalField是一个接口,定义了如何访问tempoal对象某个字段的值。ChronoFIeld枚举实现了这个接口,所以你可以用get方法得到枚举元素的值。
int year = date.get(ChronoField.YEAR);
int month = date.get(ChronoField.MONTH_OF_YEAR);
int day = date.get(ChronoField.DAY_OF_MONTH);
可以使用LocalTime来表示时间,可以使用of重载的两个工厂方法创建它的实例。第一个重载函数接收小时和分钟,第2个重载函数同时还接受秒。同LocalDate一样,LocalTime也提供了一些getter方法来访问这些变量的值。
LocalTime time = LocalTime.of(16,55,32);
int hour = time.getHour();
int minute = time.getMinute();
int second = time.getSecond();
int _hour = time.get(ChronoField.HOUR_OF_DAY);
LocalTime _time = LocalTime.now();
合并日期和时间
这个符合类名叫LocalDateTime,它同时表示了日期和时间,但不带时区信息。你可以直接创建,也可以通过合并日期和时间对象构造。
LocalDateTime dt1 = LocalDateTime.of(2018,9,15,16,50,30); // 直接创建
LocalDateTime dt2 = LocalDateTime.of(date,time); // 复合LocalDate和LocalTime
LocalDateTime dt3 = date.atTime(13,59,39); // 通过日期指定时间
LocalDateTime dt4 = time.atDate(date); // 通过时间指定日期
LocalDateTime dt5 = date.atTime(time);
LocalDate date = dt1.toLocalDate(); // 得到LocalDate
LocalTime time = dt1.toLocalTime(); // 得到LocalTime
机器的日期和时间格式
java.time.Instant是给机器使用的,包含秒和纳秒构成的数字,它是以Unix元年时间(UTC时区1970年1月1日午夜时分)开始所经历的秒数进行计算。你可以向静态方法ofEpochSecond传递一个代表秒数的值创建一个实例。它还有一个增强版本的方法,它接收第二个以纳秒为单位的参数,对传入的描述进行调整。重载的版本会调整纳秒参数,确保保存的纳秒分片在0到999 999 999之间。
下面的这些调用会返回几乎相同的Instant对象。
Instant.ofEpochSecond(3);
Instant.ofEpochSecond(3,0);
Instant.ofEpochSecond(2,1_000_000_000); // 2秒之后再加上100万纳秒(1秒)
Instant.ofEpochSecond(4,-1_000_000_000); // 4秒之前的100万纳秒(1秒)
日期/时间间隔
表示2个日期/时间的间隔,可以使用Duration或Period。
LocalTime time1 = LocalTime.of(15,23);
LocalTime time2 = LocalTime.of(5,29);
Duration d1 = Duration.between(time1,time2);
LocalDate date1 = LocalDate.now();
LocalDate date2 = LocalDate.of(2019,1,1);
Duration d2 = Duration.between(date1,date2);
LocalDateTime dateTime1 = LocalDateTime.of(date1,time1);
LocalDateTime dateTime2 = LocalDateTime.of(date2,time2);
Duration d3 = Duration.between(dateTime1,dateTime2);
Instant instant1 = Instant.ofEpochSecond(3);
Instant instant2 = Instant.ofEpochSecond(5,2_000_000_000);
Duration d4 = Duration.between(instant1,instant2);
Duration threeMiniutes = Duration.ofMinutes(3);
Duration _threeMinutes = Duration.of(3, ChronoUnit.MINUTES);
Period tenDyas = Period.ofDays(10);
Period threeWeeks = Period.ofWeeks(3);
Period twoYearsSixMonthsOneDay = Period.of(2,6,1);
日期-时间内中表示时间间隔的通用方法
方法名 | 是否静态方法 | 方法描述 |
---|---|---|
between | 是 | 创建两个时间点之间的interval |
from | 是 | 由一个临时节点创建interval |
of | 是 | 由它的组成部分创建interval |
parse | 是 | 由字符串创建interval |
addTo | 否 | 创建该interval的副本,并将其叠加到某个指定的temporal对象 |
get | 否 | 读取该interval的状态 |
isNegative | 否 | 检查该interval是否是负值,不包含0 |
iszero | 否 | 检查该interval的时长是否为0 |
minus | 否 | 通过减去一定的时间创建该interval的副本 |
multipliedBy | 否 | 将interval的值乘以某个标量创建该interval的副本 |
negated | 否 | 以忽略某个时长的方式创建该interval的副本 |
plus | 否 | 以增加某个指定时长的方式创建该interval的副本 |
subtrctFrom | 否 | 从指定的temporal对象中减去该interval |
上面介绍的这些日期-时间对象都是不可变的,是为了更好的支持函数式编程,确保线程安全,保持领域模式一致性。
操作、解析和格式化日期
如果已经有了一个LocalDate对象,如果想要修改日期,可以使用它的withAttribute和with方法,会创建一个副本,并按照需要修改的属性。
LocalDate _date1 = LocalDate.of(2019,5,15);
LocalDate _date2 = _date1.withYear(2018); // 2018-05-15
// 使用withAttribute方法
LocalDate _date3 = _date1.withDayOfMonth(20); // 2019-05-20
// 使用with方法
LocalDate _date4 = _date1.with(ChronoField.MONTH_OF_YEAR,2); // 2019-02-15
上面是比较直接的修改方式,也可以以相对方式修改日期,比如:
LocalDate _date5 = _date1.plusYears(1); // 2020-05-15
LocalDate _date6 = _date1.minusWeeks(1); // 2019-05-08
表示时间点的日期-时间类的通用方法
方法名 | 是否静态方法 | 方法描述 |
---|---|---|
from | 是 | 根据传入的Temporal对象创建实例 |
now | 是 | 根据系统时钟创建Temporal实例 |
of | 是 | 由Temporal对象的某个部分创建该对象的实例 |
parse | 是 | 由字符串创建Temporal对象的实例 |
atOffset | 否 | 将Temporal对象和某个时区偏移相结合 |
atZone | 否 | 将Temporal对象和某个时区相结合 |
format | 否 | 使用某个指定的格式器将Temporal对象转换为字符串 |
get | 否 | 读取Temporal对象某一部分的值 |
minus | 否 | 通过将当前Temporal对象减去一定的时长创建副本 |
plus | 否 | 通过将当前Temporal对象增加一定的时长创建副本 |
with | 否 | 以该Temporal对象为模板,将某些状态进行修改创建该对象的副本。 |
使用TemporalAdjuster
前面的日期操作都是相对比较直接的,你还可使用TemporalAdjuster来进行一些更复杂的操作。
比如:
LocalDate _date7 = _date1.with(nextOrSame(DayOfWeek.SUNDAY)); // 2019-05-19
TemporalAdjuster的工厂方法
方法名 | 方法描述 |
---|---|
dayOfWeekInMonth | 创建一个新的日期,它的值为同一个月中每一周的第几天 |
firstDayOfMonth | 创建一个新的日期,它的值为当月的第一天 |
firstDayOfNextMonth | 创建一个新的日期,它的值为下个月的第一天 |
firstDayOfNextYear | 创建一个新的日期,它的值为明年的第一天 |
firstDayOfYear | 创建一个新的日期,它的值为当年的第一天 |
firstInMonth | 创建一个新的日期,它的值为同一个月中,第一个符合星期几要求的值 |
lastDayOfMonth | 创建一个新的日期,它的值为当月的最后一天 |
lastDayOfNextMonth | 创建一个新的日期,它的值为下个月的最后一天 |
lastDayOfNextYear | 创建一个新的日期,它的值为明年的最后一天 |
lastDayOfYear | 创建一个新的日期,它的值为当年的今年的最后一天 |
lastInMonth | 创建一个新的日期,它的值为同一个月中,最后一个符合星期几要求的值 |
next/previous | 创建一个新的日期,并将其值设定为日期调整后/前,第一个符合指定星期几要求的日期 |
nextOrSame/preivousOrSave | 创建一个新的日期,并将其值设定为日期调整后/前,第一个符合指定星期几要求的日期。如果该日期已经符合要求,直接返回该对象 |
如果上面的方法还不能满足你的要求,实现自己的TemporalAdjuster也很简单。实现TemporalAdjuster的adjustInto接口即可。
下面实现一个自定义的TemporalAdjuster,能够计算明天的日期,并过滤掉周六周日。
class NextWorkingDayAdjuster implements TemporalAdjuster {
@Override
public Temporal adjustInto(Temporal temporal) {
DayOfWeek dayOfWeek = DayOfWeek.of(temporal.get(ChronoField.DAY_OF_WEEK));
int dayToAdd = 1;
if (dayOfWeek == DayOfWeek.FRIDAY) {
dayToAdd = 3;
}
else if (dayOfWeek == DayOfWeek.SATURDAY) {
dayToAdd = 2;
}
return temporal.plus(dayToAdd, ChronoUnit.DAYS);
}
}
NextWorkingDayAdjuster adjuster = new NextWorkingDayAdjuster();
LocalDate _date8 = _date1.with(adjuster);
日期解析和格式化
java8日期格式化的类在java.time.format包中,其中最重要的类是DateTimeFormatter。DateTimeFormatter提供了大量的预定义格式的DateTimeFormatter。
比如:
格式化:
String s1 = _date1.format(DateTimeFormatter.BASIC_ISO_DATE);
String s2 = _date1.format(DateTimeFormatter.ISO_LOCAL_DATE);
解析:
LocalDate dd1 = LocalDate.parse("20190112",DateTimeFormatter.BASIC_ISO_DATE);
这些DateTimeFormmater的实例都是线程安全的,所以你能够以单例的形式创建Formatter实例,并在多个线程之间共享。
你还可以通过DateTimeFormatter的ofPattern方法创建。比如:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
另外,你还可以通过DateTimeFormatterBuilder来创建。
比如
BASIC_ISO_DATE = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.appendValue(YEAR, 4)
.appendValue(MONTH_OF_YEAR, 2)
.appendValue(DAY_OF_MONTH, 2)
.optionalStart()
.appendOffset("+HHMMss", "Z")
.toFormatter(ResolverStyle.STRICT, IsoChronology.INSTANCE);
处理不同的时区
新的java.time.ZoneId是对老的java.util.TimeZone的替代,大大简化了时区处理的操作。
你可以按照ZoneId.of(地区ID标识)来得到指定时区。
ZoneId romeZone = ZoneId.of("Europe/Rome");
或者通过toZoneId()来将老的时区对象转换为ZoneId。
ZoneId zoneId = TimeZone.getDefault().toZoneId();
有了ZoneId,你可以将它和LocalDate、LocalDateTime或Instant整合起来,构造为一个ZoneDateTime实例,它代表了相对于指定时区的时间点。
ZonedDateTime zonedDateTime = date.atStartOfDay(zoneId);
zonedDateTime = dateTime1.atZone(romeZone);
zonedDateTime = instant.atZone(romeZone);
ZoneOffset是ZoneId的一个子类,表示的是当前时间与伦敦格林尼治子午线时间的差异。比如纽约落后于伦敦5小时,可以使用下面的方式表示:
ZoneOffset zoneOffset = ZoneOffset.of("-05:00"); // 美国东部时间偏移量
LocalDateTime localDateTime = LocalDateTime.of(2018,9,19,15,30,29);
OffsetDateTime offsetDateTime = OffsetDateTime.of(localDateTime,zoneOffset);
-05:00的偏差实际上对应的是美国东部标准时间。注意:这种方式定义的ZoneOffset没有考虑夏令时的影响,所以大多数情况下,不推荐使用。
参考《java8实战》
还没有评论,来说两句吧...