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
相关文章
|
8天前
|
数据挖掘 API 开发者
深度解析!淘宝商品详情 API 接口的高效调用与实战应用
淘宝商品详情API为开发者提供高效获取商品信息的途径,支持名称、价格、销量等详细数据的提取。接口通过GET/POST请求方式调用,需携带商品ID与授权信息(如AppKey)。其特点包括数据全面、实时性强及安全性高,满足电商应用、数据分析等需求。本文还提供了Python调用示例,涵盖签名生成、参数构建及请求发送全流程,助力开发者快速集成淘宝商品数据至自身系统中。
|
16天前
|
存储 人工智能 Java
Spring AI与DeepSeek实战四:系统API调用
在AI应用开发中,工具调用是增强大模型能力的核心技术,通过让模型与外部API或工具交互,可实现实时信息检索(如天气查询、新闻获取)、系统操作(如创建任务、发送邮件)等功能;本文结合Spring AI与大模型,演示如何通过Tool Calling实现系统API调用,同时处理多轮对话中的会话记忆。
256 57
|
3天前
|
JSON API 开发者
天猫商品详情 API 接口:功能、调用与实战攻略
天猫商品详情API为电商从业者、开发者和数据分析人员提供高效的商品数据获取途径。通过商品ID,该接口可返回包括基本信息、价格、库存及图片等详细内容,具有高准确性、易集成和功能丰富的特点。示例代码展示了如何用Python调用此API,生成签名确保请求安全,助力用户优化定价策略、开发应用或分析市场趋势。
33 10
|
8天前
|
JSON API 数据格式
一文读懂天猫商品详情 API 接口:功能、调用与实战攻略
天猫商品详情API为电商从业者、开发者和数据分析人员提供高效的商品数据获取途径。通过输入商品ID,可获取商品基本信息(名称、品牌等)、价格信息(售价、促销价等)、库存状态、商品描述及图片链接等详细内容。本文还提供了Python调用示例,包含签名生成、参数构建与请求发送等功能,帮助用户快速集成API,满足定价优化、市场分析等需求。使用时需替换示例中的AppKey与商品ID,并遵守平台规范。
85 16
|
7天前
|
数据采集 JSON API
Python 实战:用 API 接口批量抓取小红书笔记评论,解锁数据采集新姿势
小红书作为社交电商的重要平台,其笔记评论蕴含丰富市场洞察与用户反馈。本文介绍的小红书笔记评论API,可获取指定笔记的评论详情(如内容、点赞数等),支持分页与身份认证。开发者可通过HTTP请求提取数据,以JSON格式返回。附Python调用示例代码,帮助快速上手分析用户互动数据,优化品牌策略与用户体验。
|
1天前
|
监控 测试技术 数据库连接
RunnerGo API 性能测试实战:从问题到解决的全链路剖析
API性能测试是保障软件系统稳定性与用户体验的关键环节。本文详细探讨了使用RunnerGo全栈测试平台进行API性能测试的全流程,涵盖测试计划创建、场景设计、执行分析及优化改进。通过电商平台促销活动的实际案例,展示了如何设置测试目标、选择压测模式并分析结果。针对发现的性能瓶颈,提出了代码优化、数据库调优、服务器资源配置和缓存策略等解决方案。最终,系统性能显著提升,满足高并发需求。持续关注与优化API性能,对系统稳定运行至关重要。
|
7天前
|
数据采集 JSON API
Python 实战!利用 API 接口获取小红书笔记详情的完整攻略
小红书笔记详情API接口帮助商家和数据分析人员获取笔记的详细信息,如标题、内容、作者信息、点赞数等,支持市场趋势与用户反馈分析。接口通过HTTP GET/POST方式请求,需提供`note_id`和`access_token`参数,返回JSON格式数据。以下是Python示例代码,展示如何调用该接口获取数据。使用时请遵守平台规范与法律法规。
|
3月前
|
存储 监控 Java
【Java并发】【线程池】带你从0-1入门线程池
欢迎来到我的技术博客!我是一名热爱编程的开发者,梦想是编写高端CRUD应用。2025年我正在沉淀中,博客更新速度加快,期待与你一起成长。 线程池是一种复用线程资源的机制,通过预先创建一定数量的线程并管理其生命周期,避免频繁创建/销毁线程带来的性能开销。它解决了线程创建成本高、资源耗尽风险、响应速度慢和任务执行缺乏管理等问题。
214 60
【Java并发】【线程池】带你从0-1入门线程池
|
22天前
|
Java 中间件 调度
【源码】【Java并发】从InheritableThreadLocal和TTL源码的角度来看父子线程传递
本文涉及InheritableThreadLocal和TTL,从源码的角度,分别分析它们是怎么实现父子线程传递的。建议先了解ThreadLocal。
57 4
【源码】【Java并发】从InheritableThreadLocal和TTL源码的角度来看父子线程传递
|
2月前
|
存储 网络协议 安全
Java网络编程,多线程,IO流综合小项目一一ChatBoxes
**项目介绍**:本项目实现了一个基于TCP协议的C/S架构控制台聊天室,支持局域网内多客户端同时聊天。用户需注册并登录,用户名唯一,密码格式为字母开头加纯数字。登录后可实时聊天,服务端负责验证用户信息并转发消息。 **项目亮点**: - **C/S架构**:客户端与服务端通过TCP连接通信。 - **多线程**:采用多线程处理多个客户端的并发请求,确保实时交互。 - **IO流**:使用BufferedReader和BufferedWriter进行数据传输,确保高效稳定的通信。 - **线程安全**:通过同步代码块和锁机制保证共享数据的安全性。
101 23

热门文章

最新文章

下一篇
oss创建bucket