类型转换-Java 如何计算两个时间的差

news2024/11/29 12:39:28

一、背景:

今天一个小伙伴在开发中,常取不到数,像string转换int,int转换string 虽然好像只是倒过来了,但是实现的逻辑不一样,今天就是日期在计算过程中的转换做一个总结

二、步聚

1.JAVA中与日期时间相关的类

1.1java.util包中

1.2 java.time包中

JAVA8之后新增了java.time包,提供了一些与日期时间有关的新实现类:

具体每个类对应的含义说明梳理如下表:

2.时间间隔计算

2.1Period与Duration类

JAVA8开始新增的java.time包中有提供Duration和Period两个类,用于处理日期时间间隔相关的场景,两个类的区别点如下:

Duration与Period具体使用的时候还需要有一定的甄别,因为部分的方法很容易使用中被混淆,下面分别说明下。

  • Duration

Duration的最小计数单位为纳秒,其内部使用seconds和nanos两个字段来进行组合计数表示duration总长度。

Duration的常用API方法梳理如下:

关于Duration的主要API的使用,参见如下示意:

public void testDuration() {    LocalTime target = LocalTime.parse("00:02:35.700");    // 获取当前日期,此处为了保证后续结果固定,注掉自动获取当前日期,指定固定日期    // LocalDate today = LocalDate.now();    LocalTime today = LocalTime.parse("12:12:25.600");    // 输出:12:12:25.600    System.out.println(today);    // 输出:00:02:35.700    System.out.println(target);    Duration duration = Duration.between(target, today);    // 输出:PT12H9M49.9S    System.out.println(duration);    // 输出:43789    System.out.println(duration.getSeconds());    // 输出:900000000    System.out.println(duration.getNano());    // 输出:729    System.out.println(duration.toMinutes());    // 输出:PT42H9M49.9S    System.out.println(duration.plusHours(30L));    // 输出:PT15.9S    System.out.println(duration.withSeconds(15L));}
  • Period

Period相关接口与Duration类似,其计数的最小单位是天,看下Period内部时间段记录采用了年、月、日三个field来记录:

常用的API方法列举如下:

关于Period的主要API的使用,参见如下示意:

public void calculateDurationDays() {    
LocalDate target = LocalDate.parse("2021-07-11");    
// 获取当前日期,此处为了保证后续结果固定,注掉自动获取当前日期,指定固定日期    // LocalDate today = LocalDate.now();   
 LocalDate today = LocalDate.parse("2022-07-08");    // 输出:2022-07-08    System.out.println(today);    // 输出:2021-07-11    
System.out.println(target);    
Period period = Period.between(target, today);    // 输出:P11M27D, 表示11个月27天    System.out.println(period);    // 输出:0, 因为period值为11月27天,即year字段为0    System.out.println(period.getYears());    // 输出:11, 因为period值为11月27天,即month字段为11    
System.out.println(period.getMonths());    // 输出:27, 因为period值为11月27天,即days字段为27    
System.out.println(period.getDays());    // 输出:P14M27D, 因为period为11月27天,加上3月,变成14月27天    
System.out.println(period.plusMonths(3L));    // 输出:P11M15D,因为period为11月27天,仅将days值设置为15,则变为11月15天    
System.out.println(period.withDays(15));    // 输出:P2Y3M44D    System.out.println(Period.of(2, 3, 44));}

2.2.Duration与Period踩坑记

Duration与Period都是用于日期之间的计算操作。Duration主要用于秒、纳秒等维度的数据处理与计算。Period主要用于计算年、月、日等维度的数据处理与计算。

先看个例子,计算两个日期相差的天数,使用Duration的时候:

public void calculateDurationDays(String targetDate) {    LocalDate target = LocalDate.parse(targetDate);    LocalDate today = LocalDate.now();    System.out.println("today : " + today);    System.out.println("target: " + target);    long days = Duration.between(target, today).abs().toDays();    System.out.println("相差:"  + days + "天");}

运行后会报错:

today : 2022-07-07target: 2022-07-11Exception in thread "main" java.time.temporal.UnsupportedTemporalTypeException: Unsupported unit: Seconds at java.time.LocalDate.until(LocalDate.java:1614) at java.time.Duration.between(Duration.java:475) at com.veezean.demo5.DateService.calculateDurationDays(DateService.java:24)

点击看下Duration.between源码,可以看到注释上明确有标注着,这个方法是用于秒级的时间段间隔计算,而我们这里传入的是两个级别的数据,所以就不支持此类型运算,然后抛异常了。

再看下使用Period的实现:

public void calculateDurationDays(String targetDate) {    
LocalDate target = LocalDate.parse(targetDate);    
LocalDate today = LocalDate.now();    
System.out.println("today : " + today);    
System.out.println("target: " + target);    // 注意,此处写法错误!这里容易踩坑:    long days = Math.abs(Period.between(target, today).getDays());    System.out.println("相差:"  + days + "天")
;}

执行结果:

today : 2022-07-07target: 2021-07-07相差:0天

执行是不报错,但是结果明显是错误的。这是因为getDays()并不会将Period值换算为天数,而是单独计算年、月、日,此处只是返回天数这个单独的值。

再看下面的写法:

public void calculateDurationDays(String targetDate) { LocalDate target = LocalDate.parse(targetDate); LocalDate today = LocalDate.now(); System.out.println("today : " + today); System.out.println("target: " + target); Period between = Period.between(target, today); System.out.println("相差:" + Math.abs(between.getYears()) + "年" + Math.abs(between.getMonths()) + "月" + Math.abs(between.getDays()) + "天");}

结果为:

today : 2022-07-07target: 2021-07-11相差:0年11月26天

所以说,如果想要计算两个日期之间相差的绝对天数,用Period不是一个好的思路。

3.计算日期差

  • 通过LocalDate来计算

LocalDate中的toEpocDay可返回当前时间距离原点时间之间的天数,可以基于这一点,来实现计算两个日期之间相差的天数:

代码如下:

public void calculateDurationDays(String targetDate) {    
LocalDate target = LocalDate.parse(targetDate);   
 LocalDate today = LocalDate.now();    
System.out.println("today : " + today);    
System.out.println("target: " + target);    
long days = Math.abs(target.toEpochDay() - today.toEpochDay());    System.out.println("相差:" + days + "天");}

结果为:

today : 2022-07-07target: 2021-07-11相差:361天
  • 通过时间戳来计算

如果是使用的Date对象,则可以通过将Date日期转换为毫秒时间戳的方式相减然后将毫秒数转为天数的方式来得到结果。需要注意的是通过毫秒数计算日期天数的差值时,需要屏蔽掉时分秒带来的误差影响。

public void calculateDaysGap(Date start, Date end) {    
final long ONE_DAY_MILLIS = 1000L * 60 * 60 * 24;    // 此处要注意,去掉时分秒的差值影响,此处采用先换算为天再相减的方式    
long gapDays = Math.abs(end.getTime()/ONE_DAY_MILLIS - start.getTime()/ONE_DAY_MILLIS);    
System.out.println(gapDays);
}

输出结果:

today : 2022-07-08target: 2021-07-11相差:362天
  • 数学逻辑计算

分别算出年、月、日差值,然后根据是否闰年、每月是30还是31天等计数逻辑,纯数学硬怼方式计算。

不推荐、代码略...

4.计算接口处理耗时

在一些性能优化的场景中,我们需要获取到方法处理的执行耗时,很多人都是这么写的:

public void doSomething() { // 记录开始时间戳

long startMillis = System.currentTimeMillis(); // do something ... // 计算结束时间戳

long endMillis = System.currentTimeMillis(); // 计算相差的毫秒数 System.out.println(endMillis - startMillis);}

当然啦,如果你使用的是JDK8+的版本,你还可以这么写:

public void doSomething() {

// 记录开始时间戳

Instant start = Instant.now();

// do something ...

// 计算结束时间戳

Instant end = Instant.now();

// 计算相差的毫秒数

System.out.println(Duration.between(start, end).toMillis());

}

5.时间格式转换

项目中,时间格式转换是一个非常典型的日期处理操作,可能会涉及到将一个字符串日期转换为JAVA对象,或者是将一个JAVA日期对象转换为指定格式的字符串日期时间。

SimpleDataFormat实现

在JAVA8之前,通常会使用SimpleDateFormat类来处理日期与字符串之间的相互转换:

public void testDateFormatter() {    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");    // 日期转字符串    
String format = simpleDateFormat.format(new Date());    
System.out.println("当前时间:" + format);       
try {        
// 字符串转日期        
Date parseDate = simpleDateFormat.parse("2022-07-08 06:19:27");        System.out.println("转换后Date对象: " + parseDate);       
 // 按照指定的时区进行转换,可以对比下前面转换后的结果,会发现不一样        simpleDateFormat.setTimeZone(TimeZone.getTimeZone("GMT+5:00"));        
parseDate = simpleDateFormat.parse("2022-07-08 06:19:27");        System.out.println("指定时区转换后Date对象: " + parseDate);    
} catch (Exception e) {       
 e.printStackTrace();    }}

输出结果如下:

当前时间:2022-07-08 06:25:31转换后Date对象: Fri Jul 08 06:19:27 CST 2022指定时区转换后Date对象: Fri Jul 08 09:19:27 CST 2022

补充说明:

SimpleDateFormat对象是非线程安全的,所以项目中在封装为工具方法使用的时候需要特别留意,最好结合ThreadLocal来适应在多线程场景的正确使用。
JAVA8之后,推荐使用DateTimeFormat替代SimpleDateFormat。

6.DataTimeFormatter实现

JAVA8开始提供的新的用于日期与字符串之间转换的类,它很好的解决了SimpleDateFormat多线程的弊端,也可以更方便的与java.time中心的日期时间相关类的集成调用。

public void testDateFormatter() {   
 DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");    
LocalDateTime localDateTime = LocalDateTime.now();    
// 格式化为字符串   
 String format = localDateTime.format(dateTimeFormatter);    System.out.println("当前时间:" + format);    
// 字符串转Date    
LocalDateTime parse = LocalDateTime.parse("2022-07-08 06:19:27", dateTimeFormatter);    
Date date = Date.from(parse.atZone(ZoneId.systemDefault()).toInstant());    System.out.println("转换后Date对象: " + date);
}

输出结果:

当前时间:2022-07-08 18:37:46转换后Date对象: Fri Jul 08 06:19:27 CST 2022

7.日期时间格式模板

对于计算机而言,时间处理的时候按照基于时间原点的数字进行处理即可,但是转为人类方便识别的场景显示时,经常会需要转换为不同的日期时间显示格式,比如:

2022-07-08 12:02:342022/07/08 12:02:34.2382022年07月08日 12点03分48秒

在JAVA中,为了方便各种格式转换,提供了基于时间模板进行转换的实现能力:

时间格式模板中的字幕含义说明如下:

8.消失的8小时问题

日期字符串存入DB后差8小时

在后端与数据库交互的时候,可能会遇到一个问题,就是往DB中存储了一个时间字段之后,后面再查询的时候,就会发现时间数值差了8个小时,这个需要在DB的连接信息中指定下时区信息:

spring.datasource.druid.url=jdbc:mysql://127.0.0.1:3306/test?serverTimezone=Asia/Shanghai

界面时间与后台时间差8小时

在有一些前后端交互的项目中,可能会遇到一个问题,就是前端选择并保存了一个时间信息,再查询的时候就会发现与设置的时间差了8个小时,这个其实就是后端时区转换设置的问题。

SpringBoot的配置文件中,需要指定时间字符串转换的时区信息:

spring.jackson.time-zone=GMT+8

这样从接口json中传递过来的时间信息,jackson框架可以根据对应时区转换为正确的Date数据进行处理。

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

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

相关文章

聚合函数(基础版)

QUESTION ONE: # Write your MySQL query statement below select id,movie,description,rating from cinema where description <> boring and mod(id,2) 1 order by rating desc很简单的两个条件&#xff0c;一个通过 <> 解决不等于的情况&#xff0c;而确定奇…

java基础知识一

1、Java语言概述 1、java的出现标志着真正的分布式系统的到来 2、Java与c的区别 2.1、全局变量 Java中没有全局变量&#xff0c;使用类中的公共的静态变量作为这个类的全局变量 2.2、指针 Java中没有任何在指针操作 2.3、条件转移指令 Java中没有goto语句&#xff0c;通…

【day 01】初始vue

Vue的相关背景&#xff1a; vue2 > vue3 >vue实战 vue Taylor otwell (程序员中的网红) 框架 库 &#xff08;功能 方法&#xff09;axios 框架 生态 引入第三方功能库 社区 Vue2 Vue3 需要准备的小工具 vscode : snipper Vetur 浏览器需要准备 Vue Devtools Vue 引入方式…

nvm版本控制工具安装及使用

一、nvm介绍 nvm全英文也叫node.js version management&#xff0c;是一个nodejs的版本管理工具。nvm和n都是node.js版本管理工具&#xff0c;为了解决node.js各种版本存在不兼容现象可以通过它可以安装和切换不同版本的node.js。 二、nvm下载安装及使用 2.1 nvm下载 安装包…

【C++】容器篇(三)—— stack的基本介绍及其模拟实现

前言&#xff1a; 在之前的学习中我们已经了解了 vector 和 list &#xff0c;今天我将带领学习的是关于STL库中的 stack的学习&#xff01;&#xff01;&#xff01; 目录 &#xff08;一&#xff09;基本介绍 1、基本概念 2、容器适配器 &#xff08;二&#xff09;基本使…

Flutter 笔记 | Flutter 功能性组件

拦截返回键&#xff08;WillPopScope&#xff09; 为了避免用户误触返回按钮而导致 App 退出&#xff0c;在很多 App 中都拦截了用户点击返回键的按钮&#xff0c;然后进行一些防误触判断&#xff0c;比如当用户在某一个时间段内点击两次时&#xff0c;才会认为用户是要退出&a…

docker 服务环境搭建(mysql、rabbitmq、redis、nginx、springboot)

一般来说一个项目&#xff0c;百分之80都会用到以上这些&#xff0c;尤其是产品初期的demo 这些我都写过博客&#xff0c;但是我每次要去安装&#xff0c;都要去我不同的博客里面翻找&#xff0c;很烦&#xff0c;把他们聚在一起&#xff0c;方便我以后cv&#xff0c;就是这篇博…

注意力Transformer

注意力 注意力分为两步&#xff1a; 计算注意力分布 α \alpha α 其实就是&#xff0c;打分函数进行打分&#xff0c;然后softmax进行归一化 根据 α \alpha α来计算输入信息的加权平均&#xff08;软注意力&#xff09; 其选择的信息是所有输入向量在注意力下的分布 打…

基于中序有序的二叉搜索树

文章目录 什么是二叉搜索树二叉搜索树的中序遍历二叉搜索树的查找查找的非递归写法查找的递归写法 二叉搜索树的插入非递归递归 二叉搜索树的删除非递归递归 二叉搜索树的使用场景k模型kv模型 什么是二叉搜索树 二叉搜索树是普通二叉树的升级&#xff0c;普通二叉树除了存储数…

3、js - 垃圾回收机制

1、基础概念 &#xff08;1&#xff09;js内存的生命周期 -1- 内存分配&#xff1a;当声明变量、函数、对象时&#xff0c;系统会自动分配内存给它们 -2- 内存使用&#xff1a;即读写内存&#xff0c;也就是使用变量、函数 -3- 内存回收&#xff1a;使用完毕&#xff0c;由垃圾…

时间跟踪工具:Timemator Mac汉化版

Timemator是一款Mac平台上的时间跟踪工具&#xff0c;它可以帮助用户记录和分析日常工作和任务所花费的时间。Timemator提供了多种时间跟踪功能&#xff0c;包括自动跟踪应用程序和文档、手动添加时间记录、设置定时器等&#xff0c;让用户可以轻松记录和管理时间。 活动时间表…

互联网及SaaS行业如何落地体验管理?

3月25日&#xff0c;「体验家XMPlus」携手PMTalk成功举办了以“2023用户体验新趋势与数据增长实践”为主题的线下沙龙。 在本次活动中&#xff0c;有100多位来自不同企业的产品经理前来参与&#xff0c;大家聚集一起互相交流、分享经验。“体验家”的产品总监李若晨先生也进行了…

[RSA议题分析] Aikido Turning EDRs to malicious wipers using 0-day exploits

文章目录 简介议题分析wiperEDRWindows of Opportunity 总结 简介 本篇议题是由SafeBreach实验室的Or Yair带来的&#xff0c;主要通过利用EDR的删除恶意软件的能力和windows软连接与延迟删除文件的能力去制造一个可以在普通户权限下删除任意文件的恶意软件 - Aikido。这本质上…

科技云报道:汽车云,云计算换挡后的下一个“赛点”?

科技云报道原创。 从去年开始&#xff0c;汽车云似乎成为了云计算赛道的“香饽饽”。 尤其是在下半年&#xff0c;无论是阿里、腾讯、华为三大云巨头&#xff0c;还是百度云、京东云、字节云等奋力争夺“第四朵云”的玩家&#xff0c;均已各种形式“刷新”了其汽车云战略。玩…

Revit中窗族的立面出图设置和构件显隐

​  一、窗族的立面出图设置 Revit中&#xff0c;除了平面的出图设置以外还有立面的出图设置。 例如&#xff1a;如何在立面中&#xff0c;使窗户在精细详细程度显示的是窗的全部主体结构而在粗略/中等详细程度下是显示这样的样式呢? 在窗族样板中&#xff0c;打开立面&…

unity uitoolkit学习

使用UI Toolkit Debugger查看元素 1、打开面板 2、找到元素 在UI Builder窗体&#xff0c;别忘了打开Preview再选择元素 3、可以选择不同类型的窗体 4、查看元素的样式 需要注意的是下面的样式会覆盖上面的 5、调试 修改内置控件样式 1、找到PanelSettings>Theme St…

如果STM32/GD32一类的ARM单片机解除读写保护的方法

文章目录 前言一、打开软件二、连接到目标芯片1.连接2.若目标芯片无写包含3. 若存在读写保护 三、解除读写保护的操作&#xff1a;1. 打开Options Bytes对话框2. 操作步骤 四、工具下载链接 前言 有时候啊&#xff0c;使用ST-Link给STM32一类的ARM单片机下载程序的时候&#x…

Fastjson过滤器用法

fastjson git地址&#xff1a;https://github.com/alibaba/fastjson/wiki SimplePropertyPreFilter 用法 用于过滤某个属性 SimplePropertyPreFilter filter new SimplePropertyPreFilter();filter.getExcludes().add("gender");A a new A(1,"zhangsan"…

CSS样式的三种引入方式及优先级

说明&#xff1a;网页开发有三种技术&#xff0c;分别是html、css和js&#xff0c;分别对应页面的结构、表现和动作。css样式引入&#xff0c;是指把对页面的渲染作用到html上&#xff0c;有以下三种方式&#xff1a;行内式、内嵌式和外联式。 第一种&#xff1a;行内式&#…

噶了呀,现在的00后这么卷的吗?

现在的小年轻真的卷得过分了。前段时间我们公司来了个00年的&#xff0c;工作没两年&#xff0c;跳槽到我们公司起薪20K&#xff0c;都快接近我了。 后来才知道人家是个卷王&#xff0c;从早干到晚就差搬张床到工位睡觉了。 最近和他聊了一次天&#xff0c;原来这位小老弟家里…