JAVA8实战 - 日期API(下)

简介: JAVA8实战 - 日期API(下)

实战 - 封装日期工具类



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


注意事项:


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


/**
     * 生成默认的格式器
     *
     * @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);
    }
复制代码


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


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


/**
     * 获取指定时间的上一个工作日
     *
     * @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不要混淆了。


/**
     * 使用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自身的方法进行二次封装。


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


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


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


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如下的形式所示,比如我们


LocalDateTime parse2 = LocalDateTime.parse("2021-11-11", DateTimeFormatter.ofPattern("yyyy-MM-dd"));
复制代码


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


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
复制代码


下面来说一下解决办法:

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


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();
}
复制代码


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


/**
     * 生成默认的格式器
     *
     * @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方法


/**
     * 生成默认的格式器
     *
     * @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选项,这也是比较坑的地方


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”是毫无意义的,如果我们的格式化参数不符合下面的规则,此方法会抛出异常并且说明不支持对应的格式化操作。


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);
    }
复制代码


  1. 格式化问题:
    调用DateFomatter 有可能的报错,基本是由于使用错误到格式或者使用错误的时间类
    Error java.time.format.DateTimeParseException: could not be parsed, unparsed text found at index 10
相关文章
|
1月前
|
缓存 监控 前端开发
顺企网 API 开发实战:搜索 / 详情接口从 0 到 1 落地(附 Elasticsearch 优化 + 错误速查)
企业API开发常陷参数、缓存、错误处理三大坑?本指南拆解顺企网双接口全流程,涵盖搜索优化、签名验证、限流应对,附可复用代码与错误速查表,助你2小时高效搞定开发,提升响应速度与稳定性。
|
1月前
|
缓存 自然语言处理 API
阿里巴巴国际站关键字搜索 API 实战:3 步搞定多语言适配 + 限流破局,询盘量提升 40%
跨境电商API开发常陷合规、多语言、限流等坑。本文详解从国际合规(GDPR/CCPA)到参数优化、数据结构化及区域化搜索的全链路方案,附Python代码模板与缓存重试架构,助力提升调用成功率至99%+,精准询盘增长42%。
|
1月前
|
安全 Java 开发者
告别NullPointerException:Java Optional实战指南
告别NullPointerException:Java Optional实战指南
243 119
|
1月前
|
Cloud Native 算法 API
Python API接口实战指南:从入门到精通
🌟蒋星熠Jaxonic,技术宇宙的星际旅人。深耕API开发,以Python为舟,探索RESTful、GraphQL等接口奥秘。擅长requests、aiohttp实战,专注性能优化与架构设计,用代码连接万物,谱写极客诗篇。
Python API接口实战指南:从入门到精通
|
1月前
|
开发者 API 机器学习/深度学习
淘宝 / 1688 / 义乌购图搜 API 实战指南:接口调用与商业场景应用
本文详解淘宝、1688、义乌购三大平台图片搜索接口的核心特点、调用流程与实战代码。涵盖跨平台对比、参数配置、响应解析及避坑指南,支持URL/Base64上传,返回商品ID、价格、销量等关键信息,助力开发者快速实现商品识别与比价功能。
淘宝 / 1688 / 义乌购图搜 API 实战指南:接口调用与商业场景应用
|
2月前
|
人工智能 Java API
Java AI智能体实战:使用LangChain4j构建能使用工具的AI助手
随着AI技术的发展,AI智能体(Agent)能够通过使用工具来执行复杂任务,从而大幅扩展其能力边界。本文介绍如何在Java中使用LangChain4j框架构建一个能够使用外部工具的AI智能体。我们将通过一个具体示例——一个能获取天气信息和执行数学计算的AI助手,详细讲解如何定义工具、创建智能体并处理执行流程。本文包含完整的代码示例和架构说明,帮助Java开发者快速上手AI智能体的开发。
1019 8
|
1月前
|
存储 人工智能 算法
从零掌握贪心算法Java版:LeetCode 10题实战解析(上)
在算法世界里,有一种思想如同生活中的"见好就收"——每次做出当前看来最优的选择,寄希望于通过局部最优达成全局最优。这种思想就是贪心算法,它以其简洁高效的特点,成为解决最优问题的利器。今天我们就来系统学习贪心算法的核心思想,并通过10道LeetCode经典题目实战演练,带你掌握这种"步步为营"的解题思维。
|
2月前
|
人工智能 运维 监控
阿里云 API 聚合实战:破解接口碎片化难题,3 类场景方案让业务响应提速 60%
API聚合破解接口碎片化困局,助力开发者降本增效。通过统一中间层整合微服务、第三方接口与AI模型,实现调用次数减少60%、响应提速70%。阿里云实测:APISIX+函数计算+ARMS监控组合,支撑百万级并发,故障定位效率提升90%。
271 0
|
2月前
|
JSON API 调度
Midjourney 技术拆解与阿里云开发者实战指南:从扩散模型到 API 批量生成
Midjourney深度解析:基于优化Stable Diffusion,实现文本到图像高效生成。涵盖技术架构、扩散模型原理、API调用、批量生成系统及阿里云生态协同,助力开发者快速落地AIGC图像创作。
503 0
|
1月前
|
JSON 算法 API
Python采集淘宝商品评论API接口及JSON数据返回全程指南
Python采集淘宝商品评论API接口及JSON数据返回全程指南