前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >JAVA8实战 - 日期API

JAVA8实战 - 日期API

作者头像
阿东
发布于 2021-08-16 08:27:42
发布于 2021-08-16 08:27:42
1.6K00
代码可运行
举报
运行总次数:0
代码可运行

JAVA8实战 - 日期API

前言

这一节我们来讲讲JAVA8的日期类,源代码的作者其实就是Joda-Time,所以可以看到很多代码的API和Joda类比较像。日期类一直是一个比较难用的东西,但是JAVA8给日期类提供了一套新的API让日期类更加好用。

本文代码较多,建议亲自运行代码理解。

(不建议公众号阅读,为了阅读体验,请点击底部“阅读原文”并在PC端进行阅读)

内容概述:

  1. 关于JDK8日期的三个核心类:LocalDate、LocalTime、LocalDateTime的相关介绍
  2. 机器时间和日期格式Instant等关于细粒度的时间操作介绍
  3. TemporalAdjusters 用于更加复杂的日期计算,比如计算下一个工作日的时候这个类提供了一些实现
  4. DateTimeFormatter 格式化器,非常的灵活多变,属于SimpleDateFormat的替代品。
  5. 日期API的一些个人工具封装举例,以及在使用JDK8的时候一些个人的踩坑

最后希望通过本文能帮你摆脱new Date()

什么是ISO-8601?

日期离不开ISO-8601,下面对ISO-8601简单描述一下,参考自百度百科:

  1. ISO-8601: 国际标准化组织制定的日期和时间的表示方法,全称为《数据存储和交换形式·信息交换·日期和时间的表示方法》,简称为ISO-8601。
  2. 日的表示:小时、分和秒都用2位数表示,对UTC时间最后加一个大写字母Z,其他时区用实际时间加时差表示。如UTC时间下午2点30分5秒表示为14:30:05Z或143005Z,当时的北京时间表示为22:30:05+08:00或223005+0800,也可以简化成223005+08。
  3. 日期和时间的组合表示:合并表示时,要在时间前面加一大写字母T,如要表示北京时间2004年5月3日下午5点30分8秒,可以写成2004-05-03T17:30:08+08:00或20040503T173008+08。

LocalDate、LocalTime、LocalDateTime

JDK8把时间拆分成了三个大部分,一个是时间,代表了年月日的信息,一个是日期,代表了时分秒的部分,最后是这两个对象总和具体的时间。

LocalDate

LocalDate:类表示一个具体的日期,但不包含具体时间,也不包含时区信息。可以通过LocalDate的静态方法of()创建一个实例,LocalDate也包含一些方法用来获取年份,月份,天,星期几等,下面是LocalDate的常见使用方式:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

    @Test
    public void localDateTest() throws Exception {
        // 创建一个LocalDate:
        LocalDate of = LocalDate.of(2021, 8, 9);
        // 获取当前时间
        LocalDate now = LocalDate.now();
        // 格式化
        LocalDate parse1 = LocalDate.parse("2021-05-11");
        // 指定日期格式化
        LocalDate parse2 = LocalDate.parse("2021-05-11", DateTimeFormatter.ofPattern("yyyy-MM-dd"));

        // 下面的代码会出现格式化异常
        // java.time.format.DateTimeParseException: Text '2021-05-11 11:53:53' could not be parsed, unparsed text found at index 10
//        LocalDate parse3 = LocalDate.parse("2021-05-11 11:53:53", DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        // 正确的格式化方法
        LocalDate parse3 = LocalDate.parse("2021-05-11 11:53:53", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        // 当前时间
        System.out.println("now() => "+ now);
        // 获取月份
        int dayOfMonth = parse1.getDayOfMonth();
        System.out.println("dayOfMonth => " + dayOfMonth);
        // 获取年份
        int dayOfYear = parse1.getDayOfYear();
        System.out.println("getDayOfYear => " + dayOfYear);
        // 获取那一周,注意这里获取的是对象
        DayOfWeek dayOfWeek = parse1.getDayOfWeek();
        System.out.println("getDayOfWeek => " + dayOfWeek);
        // 获取月份数据
        int monthValue = parse3.getMonthValue();
        System.out.println("getMonthValue => " + monthValue);
        // 获取年份
        int year = parse3.getYear();
        System.out.println("getYear => " + year);
        // getChronology 获取的是当前时间的排序,这里输出结果是 ISO
        System.out.println("getChronology => " + parse3.getChronology());
        System.out.println("getEra => " + parse3.getEra());


        // 使用timeField获取值:TemporalField 是一个接口,定义了如何访问 TemporalField 的值,ChronnoField 实现了这个接口
        /*
        LocalDate 支持的格式如下:
         case DAY_OF_WEEK: return getDayOfWeek().getValue();
        case ALIGNED_DAY_OF_WEEK_IN_MONTH: return ((day - 1) % 7) + 1;
        case ALIGNED_DAY_OF_WEEK_IN_YEAR: return ((getDayOfYear() - 1) % 7) + 1;
        case DAY_OF_MONTH: return day;
        case DAY_OF_YEAR: return getDayOfYear();
        case EPOCH_DAY: throw new UnsupportedTemporalTypeException("Invalid field 'EpochDay' for get() method, use getLong() instead");
        case ALIGNED_WEEK_OF_MONTH: return ((day - 1) / 7) + 1;
        case ALIGNED_WEEK_OF_YEAR: return ((getDayOfYear() - 1) / 7) + 1;
        case MONTH_OF_YEAR: return month;
        case PROLEPTIC_MONTH: throw new UnsupportedTemporalTypeException("Invalid field 'ProlepticMonth' for get() method, use getLong() instead");
        case YEAR_OF_ERA: return (year >= 1 ? year : 1 - year);
        case YEAR: return year;
        case ERA: return (year >= 1 ? 1 : 0);
        * */
        // Unsupported field: HourOfDay
//        System.out.println("ChronoField.HOUR_OF_DAY => " + parse1.get(ChronoField.HOUR_OF_DAY));
        // Unsupported field: MinuteOfHour
//        System.out.println("ChronoField.MINUTE_OF_HOUR => " + parse1.get(ChronoField.MINUTE_OF_HOUR));
        // Unsupported field: MinuteOfHour
//        System.out.println("ChronoField.SECOND_OF_MINUTE => " + parse1.get(ChronoField.SECOND_OF_MINUTE));
        System.out.println("ChronoField.YEAR => " + parse1.get(ChronoField.YEAR));
        // Unsupported field: MinuteOfHour
//        System.out.println("ChronoField.INSTANT_SECONDS => " + parse1.get(ChronoField.INSTANT_SECONDS));

    }/*运行结果:
    now() => 2021-08-08
    dayOfMonth => 11
    getDayOfYear => 131
    getDayOfWeek => TUESDAY
    getMonthValue => 5
    getYear => 2021
    getChronology => ISO
    getEra => CE
    ChronoField.YEAR => 2021
    */

「TemporalField」 是一个接口,定义了如何访问 TemporalField 的值,「ChronnoField」 实现了这个接口 ❞

LocalTime

LocalTime:和LocalDate类似,区别在于包含具体时间,同时拥有更多操作具体时间时间的方法,下面是对应的方法以及测试:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 @Test
    public void localTimeTest() throws Exception {
        LocalTime now = LocalTime.now();
        System.out.println("LocalTime.now() => "+  now);
        System.out.println("getHour => "+ now.getHour());
        System.out.println("getMinute => "+ now.getMinute());
        System.out.println("getNano => "+ now.getNano());
        System.out.println("getSecond => "+ now.getSecond());

        LocalTime systemDefault = LocalTime.now(Clock.systemDefaultZone());
        // ZoneName => java.time.format.ZoneName.zidMap 从这个map里面进行获取
        LocalTime japan = LocalTime.now(Clock.system(ZoneId.of("Japan")));
        // 或者直接更换时区
        LocalTime japan2 = LocalTime.now(ZoneId.of("Japan"));
        // 格式化时间
        LocalTime localTime = LocalTime.of(15, 22);
        // from 从另一个时间进行转化,只要他们接口兼容
        LocalTime from = LocalTime.from(LocalDateTime.now());
        // 范湖纳秒值
        LocalTime localTime1 = LocalTime.ofNanoOfDay(1);
        LocalTime localTime2 = LocalTime.ofSecondOfDay(1);
        // 越界异常 Invalid value for MinuteOfHour (valid values 0 - 59): 77
//        LocalTime.of(15, 77);
        // 获取本地的默认时间
        System.out.println("LocalTime.now(Clock.systemDefaultZone()) => "+ systemDefault);
        // 获取日本时区的时间
        System.out.println("LocalTime.now(Clock.system(ZoneId.of(\"Japan\"))) => "+ japan);
        System.out.println("LocalTime.now(ZoneId.of(\"Japan\")) => "+ japan2);
        System.out.println("LocalTime.of(15, 22) => "+ localTime);
        System.out.println("LocalTime.from(LocalDateTime.now()) => "+ from);
        System.out.println("LocalTime.ofNanoOfDay(1) => "+ localTime1);
        System.out.println("LocalTime.ofSecondOfDay(1) => "+ localTime2);
    }/*运行结果:
    LocalTime.now() => 12:58:13.553
    getHour => 12
    getMinute => 58
    getNano => 553000000
    getSecond => 13
    LocalTime.now(Clock.systemDefaultZone()) => 12:58:13.553
    LocalTime.now(Clock.system(ZoneId.of("Japan"))) => 13:58:13.553
    LocalTime.now(ZoneId.of("Japan")) => 13:58:13.553
    LocalTime.of(15, 22) => 15:22
    LocalTime.from(LocalDateTime.now()) => 12:58:13.553
    LocalTime.ofNanoOfDay(1) => 00:00:00.000000001
    LocalTime.ofSecondOfDay(1) => 00:00:01
    */

LocalDateTime

LocalDateTimeLocalDateTime类是LocalDateLocalTime「结合体」,可以通过of()方法直接创建,也可以调用LocalDateatTime()方法或LocalTimeatDate()方法将LocalDateLocalTime合并成一个LocalDateTime,下面是一些简单的方法测试,由于篇幅有限,后续会结合这些内容编写一个工具类的代码。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 @Test
    public void localDateTimeTest() throws Exception {
        //Text '2021-11-11 15:30:11' could not be parsed at index 10
//        LocalDateTime parse = LocalDateTime.parse("2021-11-11 15:30:11");
        // 默认使用的是ISO的时间格式
        LocalDateTime parse1 = LocalDateTime.parse("2011-12-03T10:15:30");
        // 如果要自己的格式,需要手动格式化
        LocalDateTime parse = LocalDateTime.parse("2021-11-11 15:30:11", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        System.out.println("LocalDateTime.parse(....) => "+ parse1);
        System.out.println("LocalDateTime.parse(....) => "+ parse);

        LocalDateTime of = LocalDateTime.of(LocalDate.now(), LocalTime.now());
        LocalDateTime japan = LocalDateTime.now(ZoneId.of("Japan"));
        System.out.println("LocalDateTime.of(LocalDate.now(), LocalTime.now()) => "+ of);
        System.out.println("LocalDateTime.now(ZoneId.of(\"Japan\")) => "+ japan);
    }/*运行结果:
    LocalDateTime.parse(....) => 2011-12-03T10:15:30
    LocalDateTime.parse(....) => 2021-11-11T15:30:11
    LocalDateTime.of(LocalDate.now(), LocalTime.now()) => 2021-08-08T13:22:59.697
    LocalDateTime.now(ZoneId.of("Japan")) => 2021-08-08T14:22:59.697
    */

细粒度机器时间操作

JDK8还对机器的时间进行了分类,比如像下面这样

Instant

Instant用于表示一个时间戳,它与我们常使用的System.currentTimeMillis()有些类似,不过Instant可以精确到纳秒(Nano-Second)

❝注意:内部使用了两个常量,seconds表示从1970-01-01 00:00:00开始到现在的秒数,nanos表示纳秒部分(nanos的值不会超过999,999,999) ❞

下面是一些具体的测试用例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Test
    public void instantTest() throws Exception {
        Instant now = Instant.now();
        // Unable to obtain Instant from TemporalAccessor: 2021-08-08T13:37:34.403 of type java.time.LocalDateTime
//        Instant from = Instant.from(LocalDateTime.now());
        Instant instant = Instant.ofEpochSecond(3, 0);
        Instant instant1 = Instant.ofEpochSecond(5, 1_000_000_000);
        System.out.println("Instant.now() => "+ now);
//        System.out.println("Instant.from(LocalDateTime.now()) => "+ from);
        System.out.println("Instant.ofEpochSecond => "+ instant);
        System.out.println("Instant.ofEpochSecond => "+ instant1);
        System.out.println("Instant.get(ChronoField.NANO_OF_SECOND) => "+ now.get(ChronoField.NANO_OF_SECOND));
    }/*运行结果:
    Instant.now() => 2021-08-08T05:42:42.465Z
    Instant.ofEpochSecond => 1970-01-01T00:00:03Z
    Instant.ofEpochSecond => 1970-01-01T00:00:06Z
    Instant.get(ChronoField.NANO_OF_SECOND) => 465000000

    */

Duration

Duration的内部实现与Instant类似,也是包含两部分:seconds表示秒,nanos表示纳秒。两者的区别是Instant用于表示一个时间戳(或者说是一个时间点),而Duration表示一个时间段,比如想要获取两个时间的差值:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 @Test
    public void durationTest() throws Exception {
        // Text '201-08-08T10:15:30' could not be parsed at index 0
        Duration between = Duration.between(LocalDateTime.parse("2011-12-03T10:15:30"), LocalDateTime.parse("2021-08-08T10:15:30"));
        System.out.println("Duration.between(LocalDateTime.parse(\"2011-12-03T10:15:30\"), LocalDateTime.parse(\"2021-08-08T10:15:30\")) => "+ between);

        Duration duration = Duration.ofDays(7);
        System.out.println("Duration.ofDays(7) => "+ duration);
    }

Period

Period在概念上和Duration类似,区别在于Period是以「年月日」来衡量一个时间段(比如2年3个月6天),下面是对应单元测试以及相关的代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Test
    public void periodTest() throws Exception {
        Period between = Period.between(LocalDate.parse("2011-12-03"), LocalDate.parse("2021-08-08"));
        Period period = Period.ofWeeks(53);
        Period period1 = Period.ofWeeks(22);
        System.out.println("Period.between(LocalDate.parse(\"2011-12-03\"), LocalDate.parse(\"2021-08-08\")) => "+ between);
        System.out.println("Period.ofWeeks(53) => "+ period);
        System.out.println("Period.ofWeeks(53) getDays => "+ period.getDays());
        // 注意,这里如果没有对应值,会出现 0
        System.out.println("Period.ofWeeks(53) getMonths => "+ period.getMonths());
        System.out.println("Period.ofWeeks(22) getMonths => "+ period1.getMonths());
        System.out.println("Period.ofWeeks(22) getYears => "+ period1.getYears());
    }/*运行结果:
    Period.between(LocalDate.parse("2011-12-03"), LocalDate.parse("2021-08-08")) => P9Y8M5D
    Period.ofWeeks(53) => P371D
    Period.ofWeeks(53) getDays => 371
    Period.ofWeeks(53) getMonths => 0
    Period.ofWeeks(22) getMonths => 0
    Period.ofWeeks(22) getYears => 0
    */

TemporalAdjusters 复杂日期操作

这个类可以对于时间进行各种更加复杂的操作,比如下一个工作日,本月的最后一天,这时候我们可以借助with这个方法进行获取:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Test
public void testTemporalAdjusters(){
    LocalDate of = LocalDate.of(2021, 8, 1);
    // 获取当前年份的第一天
    LocalDate with = of.with(TemporalAdjusters.firstDayOfYear());
    System.out.println(" TemporalAdjusters.firstDayOfYear => "+ with);
    // 获取指定日期的下一个周六
    LocalDate with1 = of.with(TemporalAdjusters.next(DayOfWeek.SATURDAY));
    System.out.println(" TemporalAdjusters.next(DayOfWeek.SATURDAY) => "+ with1);
    // 获取当月的最后一天
    LocalDate with2 = of.with(TemporalAdjusters.lastDayOfMonth());
    System.out.println("TemporalAdjusters.lastDayOfMonth() => "+ with2);

}

下面从网络找到一份表,对应所有的方法作用

方法名

描述

dayOfWeekInMonth

返回同一个月中每周的第几天

firstDayOfMonth

返回当月的第一天

firstDayOfNextMonth

返回下月的第一天

firstDayOfNextYear

返回下一年的第一天

firstDayOfYear

返回本年的第一天

firstInMonth

返回同一个月中第一个星期几

lastDayOfMonth

返回当月的最后一天

lastDayOfNextMonth

返回下月的最后一天

lastDayOfNextYear

返回下一年的最后一天

lastDayOfYear

返回本年的最后一天

lastInMonth

返回同一个月中最后一个星期几

next / previous

返回后一个/前一个给定的星期几

nextOrSame / previousOrSame

返回后一个/前一个给定的星期几,如果这个值满足条件,直接返回

DateTimeFormatter 格式化器

这个类可以认为是用来替代SimpleDateFormat这个类,他拥有更加强大的定制化操作,同时他是线程安全的类,不用担心多线程访问会出现问题。

下面是根据DateTimeFormatter 构建一个本土化的格式化器,代码也十分的简单易懂:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private static DateTimeFormatter generateDefualtPattern(String timeFormat) {
    return new DateTimeFormatterBuilder().appendPattern(timeFormat)
        .parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
        .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
        .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
        .toFormatter(Locale.CHINA);
}

时区信息

时区信息一般用的比较少,在做和国际化相关的操作时候有可能会用到,比如最近个人从苹果买了一个东西,虽然我下单是在6号,但是电话说订单时间却是5号下单的,这里个人认为苹果的确切下单时间是按照美国时间算的。

JDK8日期类关于时区的强相关类(注意是JDK8才出现的类,不要误认为是对之前类的兼容),在之前的单元测试其实已经用到了相关时区的方法,在JDK8中使用了 ZoneId这个类来表示,但是我们有时候不知道怎么获取地区,可以参考下面的内容:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// ZoneName => java.time.format.ZoneName.zidMap 从这个map里面进行获取
LocalTime japan = LocalTime.now(Clock.system(ZoneId.of("Japan")));

实战 - 封装日期工具类

当然更加建议读者自己多动手实验,最好的办法就是多给几个需求给自己,强制自己用JDK8的方法去实现,你会发现你掌握这些API会特别快。

注意事项:

所有的工具代码都使用了同一个本地格式化器构建方法:generateDefualtPattern()

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
     * 生成默认的格式器
     *
     * @param timeFormat 指定格式
     * @return 默认时间格式器
     */
    private static DateTimeFormatter generateDefualtPattern(String timeFormat) {
        return new DateTimeFormatterBuilder().appendPattern(timeFormat)
                .parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
                .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
                .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
                .toFormatter(Locale.CHINA);
    }

获取指定时间的上一个工作日和下一个工作日

注意这个版本是不会判断节假日这些内容的,当然这里是手动实现的版本。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
     * 获取指定时间的上一个工作日
     *
     * @param time           指定时间
     * @param formattPattern 格式化参数
     * @return
     */
    public static String getPreWorkDay(String time, String formattPattern) {
        DateTimeFormatter dateTimeFormatter = generateDefualtPattern(formattPattern);
        LocalDateTime compareTime1 = LocalDateTime.parse(time, dateTimeFormatter);
        compareTime1 = compareTime1.with(temporal -> {
            // 当前日期
            DayOfWeek dayOfWeek = DayOfWeek.of(temporal.get(ChronoField.DAY_OF_WEEK));
            // 正常情况下,每次减去一天
            int dayToMinu = 1;
            // 如果是周日,减去2天
            if (dayOfWeek == DayOfWeek.SUNDAY) {
                dayToMinu = 2;
            }
            // 如果是周六,减去一天
            if (dayOfWeek == DayOfWeek.SATURDAY) {
                dayToMinu = 1;
            }
            return temporal.minus(dayToMinu, ChronoUnit.DAYS);
        });
        return compareTime1.format(dateTimeFormatter);
    }


    /**
     * 获取指定时间的下一个工作日
     *
     * @param time           指定时间
     * @param formattPattern 格式参数
     * @return
     */
    public static String getNextWorkDay(String time, String formattPattern) {
        DateTimeFormatter dateTimeFormatter = generateDefualtPattern(formattPattern);
        LocalDateTime compareTime1 = LocalDateTime.parse(time, dateTimeFormatter);
        compareTime1 = compareTime1.with(temporal -> {
            // 当前日期
            DayOfWeek dayOfWeek = DayOfWeek.of(temporal.get(ChronoField.DAY_OF_WEEK));
            // 正常情况下,每次增加一天
            int dayToAdd = 1;
            // 如果是星期五,增加三天
            if (dayOfWeek == DayOfWeek.FRIDAY) {
                dayToAdd = 3;
            }
            // 如果是星期六,增加两天
            if (dayOfWeek == DayOfWeek.SATURDAY) {
                dayToAdd = 2;
            }
            return temporal.plus(dayToAdd, ChronoUnit.DAYS);
        });
        return compareTime1.format(dateTimeFormatter);
    }

判断当前时间是否小于目标时间

判断当前时间是否小于目标时间,这里结合了之前我们学到的一些方法,注意这里的时区使用的是当前系统的时区,如果你切换别的时区,可以看到不同的效果。另外这里使用的是LocalDateTime不要混淆了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
     * 使用jdk 1.8 的日期类进行比较时间
     * 判断当前时间是否小于目标时间
     *
     * @param time   时间字符串
     * @param format 指定格式
     * @return 判断当前时间是否小于目标时间
     */
    public static boolean isBefore(String time, String format) {
        DateTimeFormatter dateTimeFormatter = generateDefualtPattern(format);
        LocalDateTime compareTime = LocalDateTime.parse(time, dateTimeFormatter);
        // getNowByNew 封装了 now()方法
        LocalDateTime current = LocalDateTime.parse(getNowByNew(format), dateTimeFormatter);
        long compare = Instant.from(compareTime.atZone(ZoneId.systemDefault())).toEpochMilli();
        long currentTimeMillis = Instant.from(current.atZone(ZoneId.systemDefault())).toEpochMilli();
        return currentTimeMillis < compare;
    }

获取指定时间属于星期几

属于对JDK8自身的方法进行二次封装。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
     * 获取指定时间属于星期几
     * 返回枚举对象
     *
     * @param date           日期
     * @param formattPattern 格式
     * @return
     */
public static DayOfWeek getDayOfWeek(String date, String formattPattern) {
    DateTimeFormatter dateTimeFormatter = generateDefualtPattern(formattPattern);
    return LocalDate.parse(date, dateTimeFormatter).getDayOfWeek();
}

获取开始日期和结束日期之间的日期

这里需要注意不是十分的严谨,最好是在执行之前日期的判断

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static final String yyyyMMdd = "yyyy-MM-dd";

/**
     * 获取开始日期和结束日期之间的日期(返回List<String>)
     *
     * @param startTime 开始日期
     * @param endTime   结束日期
     * @return 开始与结束之间的所以日期,包括起止
     */
public static List<String> getMiddleDateToString(String startTime, String endTime) {
    LocalDate begin = LocalDate.parse(startTime, DateTimeFormatter.ofPattern(yyyyMMdd));
    LocalDate end = LocalDate.parse(endTime, DateTimeFormatter.ofPattern(yyyyMMdd));
    List<LocalDate> localDateList = new ArrayList<>();
    long length = end.toEpochDay() - begin.toEpochDay();
    // 收集相差的天数
    for (long i = length; i >= 0; i--) {
        localDateList.add(end.minusDays(i));
    }
    List<String> resultList = new ArrayList<>();
    for (LocalDate temp : localDateList) {
        resultList.add(temp.toString());
    }
    return resultList;
}

日期API常见的坑:

LocalDateTime 的格式化yyyy-MM-dd报错:

第一次使用,最容易出现问题的diamante如下的形式所示,比如我们

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
LocalDateTime parse2 = LocalDateTime.parse("2021-11-11", DateTimeFormatter.ofPattern("yyyy-MM-dd"));

在运行的时候,会抛出如下的异常:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
java.time.format.DateTimeParseException: Text '2021-11-11' could not be parsed: Unable to obtain LocalDateTime from TemporalAccessor: {},ISO resolved to 2021-11-11 of type java.time.format.Parsed

下面来说一下解决办法:

第一种解决办法比较蛋疼,但是确实是一种非常稳妥的解决方法。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
try {
    LocalDate localDate = LocalDate.parse("2019-05-27", DateTimeFormatter.ofPattern("yyyy-MM-dd"));
    LocalDateTime localDateTime = localDate.atStartOfDay();
    System.out.println(localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
} catch (Exception ex) {
    ex.printStackTrace();
}

另外,还有一种方法是使用下面的方法,构建一个"中国化"的日期格式器:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
     * 生成默认的格式器
     *
     * @param timeFormat 指定格式
     * @return 默认时间格式器
     */
private static DateTimeFormatter generateDefualtPattern(String timeFormat) {
    return new DateTimeFormatterBuilder().appendPattern(timeFormat)
        .parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
        .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
        .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
        .toFormatter(Locale.CHINA);
}

调用format出现xx not be parsed, unparsed text found at index 10

问题原因:使用错误的格式去格式字符串,比如yyyy-MM-dd 格式化 2020-05-12 12:15:33 这种格式就会出现溢出,解决办法:使用正确的格式即可

对于上面几个问题的根本解决办法 原因:因为localdatetime 在进行格式化的时候如何case没有找到对应的格式,那么就会出现类似unsupport方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
     * 生成默认的格式器
     *
     * @param timeFormat 指定格式
     * @return
     */
    private static DateTimeFormatter generateDefualtPattern(String timeFormat) {
        return new DateTimeFormatterBuilder().appendPattern(timeFormat)
                .parseDefaulting(ChronoField.HOUR_OF_DAY, 1)
                .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 1)
                .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
                .toFormatter(Locale.CHINA);
    }

❝下面是其他的问题回答: StackFlow地址:DateTimeParseException: Text could not be parsed: Unable to obtain LocalDateTime from TemporalAccessor StackFlow地址:StackFlow无法解析文本:无法从TemporalAccessor获取LocalDateTime StackFlow地址:解析LocalDateTime(Java 8)时,无法从TemporalAccessor获取LocalDateTime ❞

DateTimeParseException一些小坑

参考了下面的异常日志,根本的原因是DateTimeFormatter格式化没有HH选项,这也是比较坑的地方

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
java.time.format.DateTimeParseException: Text '2017-02-02 08:59:12' could not be parsed: Unable to obtain LocalDateTime from TemporalAccessor: {MinuteOfHour=59, NanoOfSecond=0, SecondOfMinute=12, MicroOfSecond=0, MilliOfSecond=0, HourOfAmPm=8},ISO resolved to 2017-02-02 of type java.time.format.Parsed

总结:

在个人编写工具类的过程中,发现确实比之前的DateCalendar这两个类用起来好很多,同时JDK8的日期类都是「线程安全」的。当然JDK8对于国内使用不是十分友好,这也没有办法毕竟是老外的东西,不过解决办法也有不少,习惯了将解决套路之后也可以接受。最后,有条件最好使用谷歌的搜索引擎,不仅可以帮你把坑跨过去,老外很多大神还会给你讲讲原理,十分受用。

其他问题

  1. 关于LocalDate的一个坑

关于LocalDate一些源码分析

直接上源代码,LocalDate仅代表一个日期,而不代表DateTime。因此在格式化时“ 「HH:mm:ss」”是毫无意义的,如果我们的格式化参数不符合下面的规则,此方法会抛出异常并且说明不支持对应的格式化操作。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private int get0(TemporalField field) {
        switch ((ChronoField) field) {
            case DAY_OF_WEEK: return getDayOfWeek().getValue();
            case ALIGNED_DAY_OF_WEEK_IN_MONTH: return ((day - 1) % 7) + 1;
            case ALIGNED_DAY_OF_WEEK_IN_YEAR: return ((getDayOfYear() - 1) % 7) + 1;
            case DAY_OF_MONTH: return day;
            case DAY_OF_YEAR: return getDayOfYear();
            case EPOCH_DAY: throw new UnsupportedTemporalTypeException("Invalid field 'EpochDay' for get() method, use getLong() instead");
            case ALIGNED_WEEK_OF_MONTH: return ((day - 1) / 7) + 1;
            case ALIGNED_WEEK_OF_YEAR: return ((getDayOfYear() - 1) / 7) + 1;
            case MONTH_OF_YEAR: return month;
            case PROLEPTIC_MONTH: throw new UnsupportedTemporalTypeException("Invalid field 'ProlepticMonth' for get() method, use getLong() instead");
            case YEAR_OF_ERA: return (year >= 1 ? year : 1 - year);
            case YEAR: return year;
            case ERA: return (year >= 1 ? 1 : 0);
        }
        throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
    }

2. 格式化问题: 调用DateFomatter 有可能的报错,基本是由于使用错误到格式或者使用错误的时间类 Error java.time.format.DateTimeParseException: could not be parsed, unparsed text found at index 10

参考资料

❝侠说java8-LocalDateTime ❞

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-08-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 懒时小窝 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Dating Java8系列之新的日期和时间
在Java 1.0中,对日期和时间的支持只能依赖java.util.Date类。正如类名所表达的,这个类无法表示日期,只能以毫秒的精度表示时间。更糟糕的是它的易用性,由于某些原因和未知的设计决策,这个类的易用性被深深地损害了,比如:年份的起始选择是1900年,月份的起始从0开始。这意味着,如果你想要用Date表示Java 8的发布日期,即2014年3月18日,需要创建下面这样的Date实例:
翎野君
2024/01/05
1660
Dating Java8系列之新的日期和时间
Java 8 - 时间API
https://nowjava.com/docs/java-api-11/java.base/java/time/LocalDate.html
小小工匠
2021/08/17
4290
JAVA8新特性-----新时间与日期API
(以Unix元年: 1970年1月1日 00:00:00 到某个时间之间的毫秒值)
大忽悠爱学习
2021/11/15
2700
简单使用时间API(LocalDateTime)
JDK 8中增加了一套全新的日期时间API,这套API设计合理,是线程安全的。新的日期及时间API位于 java.time 包中,下面是一些关键类。
ha_lydms
2023/08/09
4280
死磕18个Java8日期处理,工作必用!
Java 8 推出了全新的日期时间API,在教程中我们将通过一些简单的实例来学习如何使用新API。
搜云库技术团队
2020/02/19
7050
18 个 Java8 日期处理的实践,太有用了!
Java 8 推出了全新的日期时间API,在教程中我们将通过一些简单的实例来学习如何使用新API。
xcbeyond
2021/03/16
8680
06-Java8新特性 新时间日期API
Instant : 时间戳(以Unix 元年: 1970年1月1日 00:00:00 到某个时间之间的毫秒值)
彼岸舞
2021/12/14
6700
Java8 Date API
Java8 在 java.time 包下新增了全新的日期和时间 API。新的 Date API 与 Joda-Time 库有点相似,但又有不一样。
happyJared
2019/08/02
8240
Java8新特性时间日期库DateTime API及示例
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
程序新视界
2019/10/22
1.2K0
Java基础学习|学JDK8中的时间类LocalDateTime
Java里的日期和时间格式化问题,在JDK8(Java SE Development Kit8,以下使用过程中,均称为JDK1.8或者JDK8)以前有很多问题,例如日期类并不提供国际化,没有时区支持,包分配的也比较混乱。所以在JDK8后进行了大改造,索性在rt.jar中新增了一个time包,这样我们就可以个性化设置日期时间的展示方式了。
六月暴雪飞梨花
2023/12/30
2090
Java基础学习|学JDK8中的时间类LocalDateTime
Java 8 日期时间 API
java 8 通过发布新的Date-Time API (JSR 310)来进一步加强对日期和时间的处理。
一滴水的眼泪
2020/09/21
1.1K0
Java8中日期的特性,别再用Date来处理日期啦~
01. 获取当前日期 格式:yyyy-MM-dd LocalDate today = LocalDate.now(); System.out.println("今天的日期:" + today); 02. 获取日期中的年/月/日 getMonthValue() 获取月 getDayOfMonth() 获取天 LocalDate today = LocalDate.now(); int year = today.getYear(); System.out.println("年:" + year);
笑凡尘
2020/07/31
1.8K0
java8的日期API总结(JSR310)
有了 java.util.Date 又搞出个java.sql.Date,改个名字不行?
pollyduan
2019/11/04
9960
Java 8 的时间日期 API
上一篇文章『Java 的时间日期 API』中,我们学习了由 Date、Calendar,DateFormat 等组成的「传统时间日期 API」,但是传统的处理接口设计并不是很友好,不易使用。终于,Java 8 借鉴第三方优秀开源库 Joda-time,重新设计了一套 API。 那么本篇文章就来简单学习一下新式的时间日期处理接口。 表示时刻的 Instant Instant 和 Date 一样,表示一个时间戳,用于描述一个时刻,只不过它较 Date 而言,可以描述更加精确的时刻。并且 Instant 是时区无
Single
2018/05/09
1.6K0
(转载)Java8新的日期API LocalDate, LocalTime
由于Java Date的各种问题,Java8推出了新的日期API,很受一拨人的追捧。
Ryan-Miao
2018/08/01
8440
基础篇:JDK8的日期处理API-必知必备
jdk8之前我们一直在用Date、Calender和SimpleDateFormat,但它的API不够强大,还存在线程安全问题,一直被人诟病。jdk8提供了新的时间API,在java.time包里的类是不可变且线程安全的,它和Joda是同一个作者实现的,因此借鉴了Joda很多特点,如果你用习惯Joda,可以很方便地切换到java.time类的使用
潜行前行
2021/01/21
2.6K0
java 8 日期操作,真香!
java 8 的日期类已经出来很长一段时间了,一直以来也没怎么用过,最近用到了感觉太好用了,真香。
索码理
2022/09/20
6090
Java8 Date Time API
Java8 带来了全新的处理日期和时间的方式。几乎所有人都有使用Java Date API痛苦的经历。因此有很多人切换到了Joda Time,但是Java8现在有了媲美Joda的时间API。在学习API前,先了解一下日期和时间的概念。Java日期遵循公历规则。表示时间和日期的类放在java.time包中。在这个包里比较重要的API有:
代码拾遗
2018/07/24
1.1K0
Java1.8新时间api - LocalDateTime
新时间对象: 1.LocalDate表示年月日 2.LocalTime表示时分秒 3.LocalDateTime表示年月日时分秒 4.Instant:表示时刻,不直接对应年月日信息,需要通过时区转换 5.ZonedDateTime: 表示特定时区的日期和时间 6.ZoneId/ZoneOffset:表示时区 1、获取对象的方法 //通过静态方法now()获取当前时间 LocalDate now1 = LocalDate.now(); LocalTime now2 = LocalTim
Java深度编程
2020/12/22
3.2K0
Java8新特性:新的时间和日期类库
Java 8另一个新增的重要特性就是引入了新的时间和日期API,它们被包含在java.time包中。借助新的时间和日期API可以以更简洁的方法处理时间和日期。
我的小碗汤
2019/07/30
1.7K0
推荐阅读
相关推荐
Dating Java8系列之新的日期和时间
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文