JDK8 日期处理
日期类型
机器时间观点的API
Instant类
用以代表自定义的Java epoch之后的某个时间点历经的毫秒数,精确度基本上是毫秒。使用Instant的静态方法now()取得代表Java epoch毫秒数的Instant实例,取得Instant实例后,可以使用plusSeconds()、plusMillis()、plusNanos()、minusSeconds()、minusMillis()、minusNanos()来做时间轴上的运算,Instant实例本身不会变动,这些操作会返回新的Instant实例,代表运算后的瞬时。 Instant类是有时区,如果需要和不同时区的时间进行运算,需要先转换成统一时区的时间。
人类时间观点的API
1. LocalDateTime、LocalDate和LocalTime
LocalDateTime、LocalDate和LocalTime 这些类基于ISO 8601年历系统,是不具失时区的时间与日期定义。LocalDateTime、LocalDate和LocalTime 等类名称开头为Local,表示它们都只是对时间的描述,并没有时区信息。
2. ZonedDateTime和OffsetDateTime
ZonedDateTime和OffsetDateTime间可以通过toXXX()方法互转,Instant通过atZone()与atOffset()转为ZonedDateTime和OffsetDateTime,ZonedDateTime和OffsetDateTime也都可以通过toInstant()取得Instant,ZonedDateTime和OffsetDateTime都有toLocalDate()、toLocalTime()、toLocalDateTime()方法可以取得LocalDate、LocalTime和LocalDateTime。
3. Year、YearMonth、Month和MonthDay
Month是enum类型,想要取得代表月份的数字,不要使用oridinal()方法,需要使用getValue()方法。
时区API
1. ZoneId
时区类,of方法支持多种内容解析为时区。例如:
ZoneId.of("Asia/Shanghai")
ZoneId.of("UTC+8")
ZoneId.of("GMT+8")
运算API
1. Duration 、Period
一个Duration、Period 对象表示两个Instant间的一段时间,是在Java 8中加入的新功能。 一个Duration、Period 实例是不可变的,当创建出对象后就不能改变它的值了。你只能通过Duration的计算方法,来创建出一个新的Durtaion对象。 Duration 只能获取到单位为Day的日期时间运算结果,Period 可以到年月日。
LocalDateTime date1 = LocalDateTime.of(2018, 5, 4, 12, 00,35);
LocalDate localDate1 = date1.toLocalDate();
LocalDateTime date2 =LocalDateTime.of(2001, 4, 1, 10, 30,55);
LocalDate localDate2 = date2.toLocalDate();
System.out.println("LocalDate:"+date1);
System.out.println("LocalDate+1Day:"+date1.plusDays(1));
System.out.println("LocalDate-1Day:"+date1.plusDays(-1));
System.out.println("LocalDate+1Hours:"+date1.plus(1, ChronoUnit.HOURS));
System.out.println("LocalDate-1Hours:"+date1.plus(-1, ChronoUnit.HOURS));
Duration d = Duration.between(date2, date1);
System.out.println("Duration Days:"+d.toDays());
System.out.println("Duration Hours:"+d.toHours());
System.out.println("Duration Minutes:"+d.toMinutes());
System.out.println("Duration Seconds:"+d.toSeconds());
System.out.println("Duration Millis:"+d.toMillis());
System.out.println("Duration Nanos:"+d.toNanos());
Period period = Period.between(localDate2, localDate1);
System.out.println("Period Years:"+period.getYears());
System.out.println("Period Months:"+period.getMonths());
System.out.println("Period Days:"+period.getDays());
执行结果:
LocalDate:2018-05-04T12:00:35
LocalDate+1Day:2018-05-05T12:00:35
LocalDate-1Day:2018-05-03T12:00:35
LocalDate+1Hours:2018-05-04T13:00:35
LocalDate-1Hours:2018-05-04T11:00:35
Duration Days:6242
Duration Hours:149809
Duration Minutes:8988569
Duration Seconds:539314180
Duration Millis:539314180000
Duration Nanos:539314180000000000
Period Years:17
Period Months:1
Period Days:3
2. ChronoField
获取日期和时间时指定的单位,比如:
//一年中第多少日,平年范围1到365,闰年是1到366
Instant.now().getLong(ChronoField.DAY_OF_YEAR);
//一天中第几秒,范围是0 到 (24 * 60 * 60) - 1
Instant.now().getLong(ChronoField.SECOND_OF_DAY);
注意:ChronoField.NANO_OF_SECOND
包含 ChronoField.MICRO_OF_SECOND
包含ChronoField.MILLI_OF_SECOND
即 NANO_OF_SECOND
值范围是0
到 999,999,999
;MICRO_OF_SECOND
值范围是0
到999,999
; MILLI_OF_SECOND
值范围是0
到999
。
格式化API
1. DateTimeFormatter
DateTimeFormatter 自带了ISO标准的一套日期格式,同时支持多种格式的自定日期格式。对于自定义日期格式可以通过new DateTimeFormatterBuilder().appendPattern()
方式构建一个一个日期格式器也可以通过DateTimeFormatter.ofPattern()
方法构建一个日期格式器。差别在于new DateTimeFormatterBuilder().appendPattern()
方法可以通过parseDefaulting
方法直接设置指定日期位置的默认值。从而让一个LocalDateTime
支持2018-5-20
这种日期的解析。对于这种日期格式,如果不指定时分秒的默认值,在解析的时候将会报错。
注意事项:
- 通过
parseDefaulting
方法设置指定位置的默认值时,年月日是不能设置默认值为0
的,因为年月日不能为0
。报错的信息为:java.time.format.DateTimeParseException: Text '1806' could not be parsed: Invalid value for DayOfMonth (valid values 1 - 28/31): 0
- 字符串解析日期的时候,字符串的长度必须和自定义的日期格式的长度一致,否则会报错:
java.time.format.DateTimeParseException: Text '18' could not be parsed at index 2
NANO_OF_SECOND
/MICRO_OF_SECOND
/MILLI_OF_SECOND
默认值不支持独立设置。即指定日期格式为yyyy-MM-dd HH:mm:ss.SSS
去解析日期字符串2018-04-25 02:44:19.123
。并指定默认的NANO_OF_SECOND
/MICRO_OF_SECOND
值为999
,那么得到日期值仍为2018-04-25 02:44:19.123
而不是2018-04-25 02:44:19.123999999
。 如果指定日期格式为yyyy-MM-dd HH:mm:ss
去解析日期字符串2018-04-25 02:44:19
。并指定默认的NANO_OF_SECOND
/MICRO_OF_SECOND
值为999
,那么得到的日期值为2018-04-25 02:44:19.000999999
DateTimeFormatter format2 =new DateTimeFormatterBuilder().appendPattern("yyyy-MM-dd HH:mm:ss.SSS") // .parseDefaulting(ChronoField.MILLI_OF_SECOND, 0) .parseDefaulting(ChronoField.MICRO_OF_SECOND, 999) .parseDefaulting(ChronoField.NANO_OF_SECOND, 999) .toFormatter().withZone(ZoneId.of("UTC+8")); LocalDateTime localDate2 = LocalDateTime.parse("2018-04-25 02:44:19.123",format2); System.out.println("测试默认值的日期格式化器"+localDate2);
自定义日期格式
/**
* 时区id-东八区
*/
public static final ZoneId UTC_8 = ZoneId.of("UTC+8");
/**
* 日期格式器-年月日,例如:2018/04/18
*/
public static final DateTimeFormatter FORMAT_DATE = new DateTimeFormatterBuilder()
.appendPattern("yyyy/MM/dd")
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
.parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
.parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
.parseDefaulting(ChronoField.MILLI_OF_SECOND, 0)
.parseDefaulting(ChronoField.MICRO_OF_SECOND, 0)
.parseDefaulting(ChronoField.NANO_OF_SECOND, 0)
.toFormatter()
.withZone(UTC_8);
/**
* 日期格式器-年月日时分秒,例如2018/04/18 15:08:19
*/
public static final DateTimeFormatter FORMAT_DATE_TIME = new DateTimeFormatterBuilder()
.appendPattern("yyyy/MM/dd HH:mm:ss")
.parseDefaulting(ChronoField.MILLI_OF_SECOND, 0)
.parseDefaulting(ChronoField.MICRO_OF_SECOND, 0)
.parseDefaulting(ChronoField.NANO_OF_SECOND, 0)
.toFormatter()
.withZone(UTC_8);
/**
* 日期格式器-年月日时分,例如2018/04/18 15:08
*/
public static final DateTimeFormatter FORMAT_DATE_SHORT_TIME = new DateTimeFormatterBuilder()
.appendPattern("yyyy/MM/dd HH:mm")
.parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
.parseDefaulting(ChronoField.MILLI_OF_SECOND, 0)
.parseDefaulting(ChronoField.MICRO_OF_SECOND, 0)
.parseDefaulting(ChronoField.NANO_OF_SECOND, 0)
.toFormatter()
.withZone(UTC_8);
/**
* 日期格式器-年月日时分秒毫秒,例如2018/04/18 15:08:19.156
*/
public static final DateTimeFormatter FORMAT_TIMESTAMP = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss.SSS").withZone(UTC_8);
/**
* 日期格式器-年月日时分秒毫秒微秒纳秒,例如2018/04/18 15:08:19.156000567
*/
public static final DateTimeFormatter FORMAT_TIMESTAMP_ALL = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss.SSSSSSSSS").withZone(UTC_8);
转换字符串为LocalDateTime
int length = string.length();
switch (length) {
case 10:
return LocalDateTime.parse(string, SystemConstant.FORMAT_DATE);
case 16:
return LocalDateTime.parse(string, SystemConstant.FORMAT_DATE_SHORT_TIME);
case 19:
return LocalDateTime.parse(string, SystemConstant.FORMAT_DATE_TIME);
case 23:
return LocalDateTime.parse(string, SystemConstant.FORMAT_TIMESTAMP);
default:
return LocalDateTime.parse(string, SystemConstant.FORMAT_TIMESTAMP_ALL);
}
转换long毫秒为LocalDateTime
return LocalDateTime.ofInstant(Instant.ofEpochMilli(longValue), UTC_8);
使用
//获取本机系统时钟的默认时区的瞬时时间
Instant instantNow = Instant.now();
//Gets the number of seconds from the Java epoch of 1970-01-01T00:00:00Z.
long second = instantNow.getEpochSecond();
//获取毫秒部分的值,只是毫秒部分,值的范围是0-999
long milliOfSecond = instantNow.getLong(ChronoField.MILLI_OF_SECOND);
//获取当前时间的从1970-01-01T00:00Z 至今的毫秒数
long milliSecond = second*1000+milliOfSecond;
//转换标准LocalDateTime为只有年月日值的LocalDateTime
LocalDateTime date1 = LocalDateTime.now(); // LocalDateTime1:2018-06-04T20:10:14.723046200
LocalDate localDate1 = date1.toLocalDate(); //LocalDate:2018-06-04
LocalDateTime date2 =localDate1.atStartOfDay(); //LocalDateTime2:2018-06-04T00:00
//转换标准LocalDateTime为只有时分秒值的LocalTime
LocalDateTime date1 = LocalDateTime.now(); //LocalDateTime1:2018-06-04T20:14:34.669008900
LocalTime localTime1 = date1.toLocalTime(); // LocalTime:20:14:34.669008900