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_SECONDNANO_OF_SECOND 值范围是0999,999,999;MICRO_OF_SECOND值范围是0999,999; MILLI_OF_SECOND值范围是0999

格式化API

1. DateTimeFormatter

DateTimeFormatter 自带了ISO标准的一套日期格式,同时支持多种格式的自定日期格式。对于自定义日期格式可以通过new DateTimeFormatterBuilder().appendPattern()方式构建一个一个日期格式器也可以通过DateTimeFormatter.ofPattern()方法构建一个日期格式器。差别在于new DateTimeFormatterBuilder().appendPattern()方法可以通过parseDefaulting方法直接设置指定日期位置的默认值。从而让一个LocalDateTime支持2018-5-20这种日期的解析。对于这种日期格式,如果不指定时分秒的默认值,在解析的时候将会报错。

注意事项:

  1. 通过parseDefaulting方法设置指定位置的默认值时,年月日是不能设置默认值为0的,因为年月日不能为0。报错的信息为:
    java.time.format.DateTimeParseException: Text '1806' could not be parsed: Invalid value for DayOfMonth (valid values 1 - 28/31): 0
    
  2. 字符串解析日期的时候,字符串的长度必须和自定义的日期格式的长度一致,否则会报错:
    java.time.format.DateTimeParseException: Text '18' could not be parsed at index 2
    
  3. 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