查询两个日期间隔天数怎么算_大厂都是怎么用Java8代替SimpleDateFormat?

1 SimpleDateFormat 之坑

1.1 格式化

1.1.1 案例

  • 初始化一个Calendar,设置日期2020年12月29日

38678da294ec87abed23467e006b1f9c.png

cae32b59b8e4bec7dc45dcd59b1e80b0.png

  • 日志

a6e4ab9561a4e6215417767751692808.png

cae32b59b8e4bec7dc45dcd59b1e80b0.png

这是由于混淆SimpleDateFormat的各种格式化模式:

  • 小写y是年
  • 大写Y是week year,即所在的周属于哪一年

一年第一周的判断方式

getFirstDayOfWeek()开始,完整的7天,并且包含那一年至少getMinimalDaysInFirstWeek()天。
该计算方式和区域相关,对zh_CN区域,2020年第一周条件:从周日开始的完整7天,2020年包含1天即 可。显然,2019年12月27日周日到2020年1月2日周六是2020年第一周,得出的week year就是2021年。

若把区域改为法国

Locale.setDefault(Locale.FRANCE);

cae32b59b8e4bec7dc45dcd59b1e80b0.png

则week yeay就还是2020年,因为一周的第一天从周一开始算,2020年的第一周是2019年12月28日周一开始,27日还是属于去年:

a3d4c6d0d448bb6eb331d025a6c4b194.png

cae32b59b8e4bec7dc45dcd59b1e80b0.png

小结

无特殊需求,针对年份的日期格式化,应该一律使用 “y” 而非 “Y”。

线程安全问题

使用一个100线程的线程池,循环20次把时间格式化任务提交到线程池处理,每个任务中又循环10次解析2020-01-01 11:12:13这样一个时间表示:

  • 运行程序后大量报错,即使没有报错的输出结果也不正常,比如2020年解析成57728年

47aa58089da53d5f7d53cff2d20142f5.png

cae32b59b8e4bec7dc45dcd59b1e80b0.png

SimpleDateFormat 用于定义解析和格式化日期时间的模式。看起来是一次性工作,应该复用,但它的解析和格式化操作都非线程安全。

分析源码

9ea8dcb77cfd60208dd81a61fae2c3f2.png

cae32b59b8e4bec7dc45dcd59b1e80b0.png

SimpleDateFormat继承自DateFormat,DateFormat有字段Calendar;

  • SimpleDateFormat#parse调用CalendarBuilder#establish构建Calendar

20304c19659a4e71f45d4f50cf02d55c.png

cae32b59b8e4bec7dc45dcd59b1e80b0.png

establish方法内部先清空Calendar再构建Calendar,整个操作没有加锁。

0adc566614b2fd67c8425ced2fe9dacb.png

cae32b59b8e4bec7dc45dcd59b1e80b0.png

显然,若使用线程池调用parse,即多线程并发操作一个Calendar,就可能会产生一个线程还没来得及处理Calendar就被另一个线程清空。format方法同理,不再赘述。因此只能在同一个线程复用SimpleDateFormat,

解决方案

通过ThreadLocal来存放SimpleDateFormat:

  • 日志输出全部正确

6c1fc675169103148d145790a8d9a891.png

cae32b59b8e4bec7dc45dcd59b1e80b0.png

1.2 当需要解析的字符串和格式不匹配,SimpleDateFormat还是能得到结果

案例

使用yyyyMM解析20160901字符串:

0cb75a3e4e67ea8a5b73494e2a732d8c.png

cae32b59b8e4bec7dc45dcd59b1e80b0.png

  • 居然输出2112年,这是因为把 1111当成月份

d1d7aa0969aa9775542363bd6fd2b2a2.png

cae32b59b8e4bec7dc45dcd59b1e80b0.png

对于SimpleDateFormat的这些坑,使用Java 8中的DateTimeFormatter即可避免。

2 Java 8中的DateTimeFormatter

2.1 格式化字符串

首先,使用DateTimeFormatterBuilder定义格式化字符串,无需死记大写Y还是小写y,大写M还是小写m:

4a627388c8da9cef4c23f1897f4fc40a.png

cae32b59b8e4bec7dc45dcd59b1e80b0.png

2.2 线程安全

可定义为static使用

2.3 待解析字符串和格式不匹配时就报错

9468d8a9eac8a47b75081507f5e0b0c3.png

cae32b59b8e4bec7dc45dcd59b1e80b0.png

  • 日志

2020/11/11 11:11:11.789 Exception in thread "main" java.time.format.DateTimeParseException: Text '20201111' could not be parsed at index 0 at java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1949) at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1777) at org.javaedge.time.commonmistakes.datetime.dateformat.CommonMistakesApplication.better(CommonMistakesApplication.java:96) at org.javaedge.time.commonmistakes.datetime.dateformat.CommonMistakesApplication.main(CommonMistakesApplication.java:47)

cae32b59b8e4bec7dc45dcd59b1e80b0.png

3 Java8计算日期时间

  • 有人喜欢使用时间戳进行计算,比如希望得到当前时间后30天:把new Date().getTime得到的时间戳加30天对应毫秒数

542f76e9a4be0cba6ea2ad6f23eaee68.png

cae32b59b8e4bec7dc45dcd59b1e80b0.png

  • 得到的日期居然比当前日期还要早,根本不是后30天

bb174dec5f897186707446e3e0996159.png

cae32b59b8e4bec7dc45dcd59b1e80b0.png

因为int发生了溢出!。

  • 应将30改为30L,使其为long:

8c1dcebb65f317a388c6834b5321739e.png

cae32b59b8e4bec7dc45dcd59b1e80b0.png

  • 正确输出

a031f2cc9776f207e6adef0ab45b42b8.png

cae32b59b8e4bec7dc45dcd59b1e80b0.png

  • Java 8前代码,建议使用Calendar:

2a9ae7eab99b127d2bc9e31e27230c7d.png

cae32b59b8e4bec7dc45dcd59b1e80b0.png

  • 使用Java 8的日期时间类型,可以直接进行各种计算,更加简洁和方便:

6529d0e578a44901f5ee40c61049089e.png

cae32b59b8e4bec7dc45dcd59b1e80b0.png

对日期时间做计算操作,日期时间API会比Calendar功能强大很多。

3.1 minus/plus直接对日期加减

5a716f5be57f5b51d005c20bf6923873.png

cae32b59b8e4bec7dc45dcd59b1e80b0.png

3.2 with快捷时间调节

  • TemporalAdjusters.firstDayOfMonth得到当前月的第一天
  • TemporalAdjusters.firstDayOfYear()得到当前年的第一天
  • TemporalAdjusters.previous(DayOfWeek.SATURDAY)得到上一个周六
  • TemporalAdjusters.lastInMonth(DayOfWeek.FRIDAY)得到本月最后一个周五

e08d873c4b52731d2d45b8db636019bc.png

cae32b59b8e4bec7dc45dcd59b1e80b0.png

3.3 使用lambda自定义的时间调整

  • 为当前时间增加100天以内的随机天数:

5e28c5823bd6fb19182d0dd70d97daf6.png

cae32b59b8e4bec7dc45dcd59b1e80b0.png

判断日期是否符合某个条件

908ca0ce63233cdc9e98a8c8c852ca59.png

cae32b59b8e4bec7dc45dcd59b1e80b0.png

query查询是否匹配条件

0f4e0dc2aa067c4d014930b80a59ac58.png

cae32b59b8e4bec7dc45dcd59b1e80b0.png

使用Java 8操作和计算日期时间虽然方便,但计算两个日期差时可能会踩坑:Java 8中有一个专门的类Period定义了日期间隔,通过Period.between得到了两个LocalDate的差,返回的是两个日期差几年零几月零几天。
如果希望得知两个日期之间差几天,直接调用Period的getDays()方法得到的只是最后的“零几天”,而不是算总的间隔天数。

比如,计算2020年12月12日和2020年10月1日的日期间隔,很明显日期差是2个月零11天,但获取getDays方法得到的结果只是11天,而不是72天:

f23fe75c21f758e8efef6a0f016459c2.png

cae32b59b8e4bec7dc45dcd59b1e80b0.png

可使用ChronoUnit.DAYS.between解决这个问题:

c11e804cbf36429a8f47a12044871327.png

cae32b59b8e4bec7dc45dcd59b1e80b0.png

4 总结

也许你认为java.util.Date类似于新API中的LocalDateTime。其实不是,虽然它们都没时区概念

  • java.util.Date类是因为使用UTC表示,所以没有时区概念,其本质是时间戳
  • LocalDateTime,严格上可以认为是一个日期时间的表示,而不是一个时间点

因此,在把Date转换为LocalDateTime的时候,需要通过Date的toInstant方法得到一个UTC时间戳进行转换,并需要提供当前的时区,这样才能把UTC时间转换为本地日期时间(的表示)。反过来,把LocalDateTime的时间表示转换为Date时,也需要提供时区,用于指定是哪个时区的时间表示,也就是先通过atZone方法把LocalDateTime转换为ZonedDateTime,然后才能获得UTC时间戳:

Date in = new Date(); LocalDateTime ldt = LocalDateTime.ofInstant(in.toInstant(), ZoneId.systemDefault()); Date out = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant());

cae32b59b8e4bec7dc45dcd59b1e80b0.png

有人说新API很麻烦,还需要考虑时区,真麻烦。但并非因为API强行设计繁琐,而是UTC时间要变为当地时间,必须考虑时区!

---------------------

为什么阿里巴巴的程序员成长速度这么快?

霸榜GitHub的Offer来了原理篇+框架篇,开放分享;

50W年薪程序员需要的技术栈分析

看完三件事❤️

如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:

点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。

关注公众号 『 Java斗帝 』,不定期分享原创知识。

同时可以期待后续文章ing

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/301353.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Vertex Texture Fetch(VTF) Fragment Texture Fetch ( FTF )

在vertex shader里也可以检索纹理。我本来觉得这没什么好奇怪的,因为我一直也觉得这很当然可以啊~当初橙书(OpenGL Shading Language Edtion2)也说过texture2D这类函数不是fragment shader专用的,倒还有texture2DLod这种在vertex shader里专用的&#xf…

面向.NET开发人员的Dapr——机密

目录:面向.NET开发人员的Dapr——前言面向.NET开发人员的Dapr——分布式世界面向.NET开发人员的Dapr——俯瞰Dapr面向.NET开发人员的Dapr——入门面向.NET开发人员的Dapr——参考应用程序面向.NET开发人员的Dapr——状态管理面向.NET开发人员的Dapr——发布和订阅面…

没找到rpm命令_Mysql的命令总结和PyMysql

Author:RunsenDate:2019/2/27作者介绍:Runsen目前大三下学期,专业化学工程与工艺,大学沉迷日语,Python, Java和一系列数据分析软件。导致翘课严重,专业排名中下。.在大学60%的时间&a…

银联再发道歉声明;印度发布吃鸡禁令;甘肃全省校园禁售辣条;波音面临50亿罚款;女子撕扯民警头发罚两百;这就是今天的大新闻。...

今天是3月18日农历二月十二今天星期一最近天气多变大家出门注意穿衣带伞下面是今天的大新闻银联再发道歉声明(央视财经)315晚会就“闪付”功能存在“隔空盗刷”的风险对广大消费者进行消费预警。3月16日下午,银联回应此事,表示隔空…

面向.NET开发人员的Dapr——可观察性

目录:面向.NET开发人员的Dapr——前言面向.NET开发人员的Dapr——分布式世界面向.NET开发人员的Dapr——俯瞰Dapr面向.NET开发人员的Dapr——入门面向.NET开发人员的Dapr——参考应用程序面向.NET开发人员的Dapr——状态管理面向.NET开发人员的Dapr——发布和订阅面…

antd request 通过jsessionid传参数_Umi-request源码阅读

最近参照antd-pro脚手架进行开发,因此接触到了umi-request。umijs/umi-request​github.comumi-request对fetch进行了封装,简化了api的使用,结合了fetch和axios的特点,具体可参照umi-request的readme介绍。文件结构核心文件夹为sr…

随手能做194个实验,不呆板,轻松撬动大智慧

▲数据汪特别推荐点击上图进入玩酷屋玩具和学习看似是两个对立的东西,孩子天性爱玩,家长却希望孩子能多学习。不一定非要啃课本才能汲取知识,有时候,在轻松有趣的游戏中也能学到课堂上学不到的知识。让学习变得有趣、高效——给孩…

面向.NET开发人员的Dapr——绑定

目录:面向.NET开发人员的Dapr——前言面向.NET开发人员的Dapr——分布式世界面向.NET开发人员的Dapr——俯瞰Dapr面向.NET开发人员的Dapr——入门面向.NET开发人员的Dapr——参考应用程序面向.NET开发人员的Dapr——状态管理面向.NET开发人员的Dapr——发布和订阅Th…

每日一笑 | 为什么Python比Java更受欢迎?

全世界只有3.14 % 的人关注了数据与算法之美(图片来源于网络,侵权删)

传递集合对象_面试必备——Java集合框架

Java集合框架面试题常见集合集合可以看作是一种容器,用来存储对象信息。数组和集合的区别:(1)数组长度不可变化而且无法保存具有映射关系的数据;集合类用于保存数量不确定的数据,以及保存具有映射关系的数据。(2)数组元素既可以是…

面向.NET开发人员的Dapr——服务调用

目录:面向.NET开发人员的Dapr——前言面向.NET开发人员的Dapr——分布式世界面向.NET开发人员的Dapr——俯瞰Dapr面向.NET开发人员的Dapr——入门面向.NET开发人员的Dapr——参考应用程序面向.NET开发人员的Dapr——状态管理The Dapr service invocation building b…

快速入门深度学习,其实并不难!

深度学习的概念源于人工神经网络的研究,而深度学习的过程就是使用多个处理层对数据进行高层抽象,得到多重非线性变换函数的过程。虽然深度学习的概念看似高大上,让人有种莫名的距离感,实际上它在日常生活中随处可见,比…

插入始终是1_插入式电磁流量计特点与应用

一、本文引言首先,插入式电磁流量计是电磁流量计中的一种安装类型,电磁流量计的安装方式有多种,包括法兰式安装,螺纹安装,卡箍式安装,插入式安装等四种类型,法兰式安装是常见普遍都在使用的安装…

面向.NET开发人员的Dapr——发布和订阅

目录:面向.NET开发人员的Dapr——前言面向.NET开发人员的Dapr——分布式世界面向.NET开发人员的Dapr——俯瞰Dapr面向.NET开发人员的Dapr——入门面向.NET开发人员的Dapr——参考应用程序面向.NET开发人员的Dapr——状态管理面向.NET开发人员的Dapr——服务调用The …

bootstrap的表单验证 vue_第45天:Web表单

在了解了 Flask Bootstrap 基本框架之后,我们来了解一下 Flask 框架的 表单( form ),以帮助我们创建交互式的 Web 应用,最后会有个提交个人信息的例子。Flask-WTF 是 Flask 框架的一个扩展,用来做表单的交互,是对 WTFo…

超火GitHub项目!一夜获得5000星,竟是微软开源的计算器

全世界只有3.14 % 的人关注了数据与算法之美昨天,微软在 MIT License 下开源了一个 GitHub 项目——Windows 计算器,短短 24 小时内就获得了接近 5000 颗星,并引发大量讨论。这个项目爆红的速度可能连微软自己都始料未及。科技媒体 The Verge…

面向.NET开发人员的Dapr——目录

今天上午的主题就是Dapr,别错过了哦。本系列部分翻译自 .NET团队编写的Dapr for .NET Developers | Microsoft Docs(https://docs.microsoft.com/en-us/dotnet/architecture/dapr-for-net-developers/),大部内容复制于面向 .NET 开发人员的 Dapr | Micro…

人体工学腰垫,保腰神器,改善久坐腰酸背痛

▲数据汪特别推荐点击上图进入玩酷屋之前小木推荐“德国MINICUTE人体工学腰垫”受到了大家一致的好评和争相购买。小木为什么推荐这一款原因是:据统计,我国腰椎病患者已经突破2亿人。30~40岁人群中,患有颈腰椎病的占比59.1%!而且有…

和flag_不怕立Flag,就怕没有Flag

转眼间就已经是今年的最后一天了,回想去年给自己立下的flag,看着自己手机上的减肥计划和体测成绩上的超重,我陷入了沉思,到底是哪个环节出问题了呢?我慢悠悠地在宿舍里转圈,看见舍友递过来的薯片&#xff0…

面向.NET开发人员的Dapr——状态管理

目录:面向.NET开发人员的Dapr——前言面向.NET开发人员的Dapr——分布式世界面向.NET开发人员的Dapr——俯瞰Dapr面向.NET开发人员的Dapr——入门面向.NET开发人员的Dapr——参考应用程序The Dapr state management building blockDapr 状态管理构建基块Distributed…