背景

今天在跑定时任务的过程中,发现有一个任务在设置数据的查询时间范围异常,出现了开始时间戳比结束时间戳大的奇怪现象,计算时间戳的代码大致如下。

package com.lingyejun.authenticator;

public class IntegerTest {

    public static void main(String[] args) {
long endTime = System.currentTimeMillis();
long startTime = endTime - 30 * 24 * 60 * 60 * 1000; System.out.println("end : " + endTime);
System.out.println("start : " + startTime);
}
}

先放出结论:因为java中整数默认是int类型,在计算的过程中30 * 24 * 60 * 60 * 1000计算结果大于Integer.MAX_VALUE,所以出现了数据溢出,从而导致了计算结果不准确的问题。

验证

我们将上面的代码稍稍改造一下,方便我们确认定位问题,调整后的代码如下:

package com.lingyejun.authenticator;

public class IntegerTest {

    public static long calcStartTime(long endTime, long minusMills) {
System.out.println("end : " + endTime + " minus mills : " + minusMills);
long startTime = endTime - minusMills;
System.out.println("start: " + startTime);
return startTime;
} public static void main(String[] args) {
long nowTime = System.currentTimeMillis();
long a = 30 * 24 * 60 * 60 * 1000;
calcStartTime(nowTime, a);
}
}  

结果如下:

end  : 1560869539864 minus mills : -1702967296
start: 1562572507160

这和我们的预期不一样,因为30 * 86400000 = 2592000000,但是计算出来却是:-1702967296。

到这里想必大家都知道原因了,这是因为java中整数的默认类型是整型int,而int的最大值是2147483647,

在代码中java是先计算右值,再赋值给long变量的。在计算右值的过程中(int型相乘)发生溢出,然后将溢出后截断的值赋给变量,导致了结果不准确。

将代码做一下小小的改动,再看一下。

package com.lingyejun.authenticator;

public class IntegerTest {

    public static long calcStartTime(long endTime, long minusMills) {
System.out.println("end : " + endTime + " minus mills : " + minusMills);
long startTime = endTime - minusMills;
System.out.println("start: " + startTime);
return startTime;
} public static void main(String[] args) {
long nowTime = System.currentTimeMillis();
long a = 30 * 24 * 60 * 60 * 1000L;
calcStartTime(nowTime, a);
}
}

结果为

end  : 1560869539864 minus mills : 2592000000
start: 1558277539864

似乎这样应该就没有什么问题了,但是这样就真的保险了吗,如果我要把30调整为24856(Integer.MAX_VALUE / 86400 = 24855),即改为:long a = 24856 * 24 * 60 * 60 * 1000L 那么同样会出现溢出。

因为java的运算规则从左到右,再与最后一个long型的1000相乘之前就已经溢出,所以结果也不对,正确的方式应该如下:long a = 24856L * 24 * 60 * 60 * 1000。

package com.lingyejun.authenticator;

public class IntegerTest {

    public static long calcStartTime(long endTime, long minusMills) {
System.out.println("end : " + endTime + " minus mills : " + minusMills);
long startTime = endTime - minusMills;
System.out.println("start: " + startTime);
return startTime;
} public static void main(String[] args) {
long a = 30L * 24 * 60 * 60 * 1000;
calcStartTime(nowTime, a);
}
}

参考文章:

https://njucz.github.io/2017/08/16/java-int%E6%BA%A2%E5%87%BA%E6%80%BB%E7%BB%93/ 

Java中在时间戳计算的过程中遇到的数据溢出问题的更多相关文章

  1. Java:验证在类继承过程中equals()、 hashcode()、toString()方法的使用

    以下通过实际例子对类创建过程汇中常用的equals().hashcode().toString()方法进行展示,三个方法的创建过程具有通用性,在项目中可直接改写. //通过超类Employee和其子类 ...

  2. 编写Java程序,模拟五子棋博弈过程中的异常声明和异常抛出

    返回本章节 返回作业目录 需求说明: 模拟五子棋博弈过程中的异常声明和异常抛出,判断用户所下棋子的位置,是否超越了棋盘的边界. 棋盘的横坐标的范围为0-9,纵坐标范围为0-14,如果用户所放棋子的坐标 ...

  3. WPF中使用WindowChrome美化窗口过程中的一个小问题

    WPF中使用WindowChrome美化窗口,在园子里有几篇不错的文章,我也是参考练习过程中发现的问题,并记录下来. 在看过几篇教程后,给出的窗口很多出现这样一个问题,如果设置了窗口标题栏的高度大于默 ...

  4. Android中的ListView的绘制过程中执行的方法

    首先,系统在绘制ListView之前, 将会先调用getCount方法来获取Item的个数.(如果getCount方法返回0的话,列表时不显示任何内容的) 之后每绘制一个 Item就会调用一次getV ...

  5. Java 利用Map集合计算一个字符串中每个字符出现的次数

    步骤分析 1.给出一串字符串,字符串中可以包含字母.数字.符号等等. 2.创建一个Map集合,key是字符串中的字符,value是字符的个数. 3.遍历字符串,获取每一个字符. 5.使用获取到的字符, ...

  6. Python ctypes 在 Python 2 和 Python 3 中的不同 // 使用ctypes过程中问题汇总

    In Python 2.7, strings are byte-strings by default. In Python 3.x, they are unicode by default. Try ...

  7. C++开发--在Visual Studio2013中使用boost::split()编译过程中出现error C4996

    Visual Studio is being overly cautious.  In debug mode, visual studio uses something called "Ch ...

  8. 在mybatis中,在列表分页查询过程中造成集合属性数据丢失的问题

    由于在进行多表关联分页查询时,某一个集合属性的多条数据正好位于2页的分割处,那么就会造成在前一页获取到的该集合属性的集合内部数据不全,因为其余数据被分到了第二页, 因此建议在进行集合属性的封装时,最好 ...

  9. .NET、PHP、MySql、JS中的时间戳你每次是手写还是复制?这篇文章让你一次性搞懂

    什么是时间戳(chuō)? 答:时间戳是指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总秒数. 为什么时间戳要从1970年01月0 ...

随机推荐

  1. ipc$ 空连接 net use

    常用命令 [xxx]表示的内容,需要根据自己的需求更改 //建立空连接 > net use \\127.0.0.1\ipc$ //删除连接 > net use \\127.0.0.1\ip ...

  2. 推荐一个去除图片人物背景的工具Removebg

    可以在线使用,url:https://www.remove.bg/users/sign_in 用邮箱免注册一个免费账号: 注册的邮箱会收到一封激活账号的邮件: 点击Activate account后激 ...

  3. E2E测试工具之--01 Cypress 上手使用

    The web has evolved. Finally, testing has too. 1. 简介 cypress 最近很火的e2e(即end to end(端到端))测试框架,它基于node ...

  4. JWT生成token及过期处理方案

    业务场景 在前后分离场景下,越来越多的项目使用token作为接口的安全机制,APP端或者WEB端(使用VUE.REACTJS等构建)使用token与后端接口交互,以达到安全的目的.本文结合stacko ...

  5. mysqlbinlog 恢复数据到任意时间点

    创建表,插入数据. ``` mysql> create database binlog; mysql> create table bt(id int); mysql> insert ...

  6. 编译heartbeat出现的问题

    如报 cc1: warnings being treated as errors pils.c:245: error: initialization fromincompatible pointer ...

  7. fastjson将json格式字符串转成list集合

    1.gameListStr = "[{"gameId":"1","gameName":"哈哈"},{" ...

  8. IIS 使用 web.config 实现从 http 自动301跳转到 https 的方法

    现在很多网站为了安全,开启了 SSL 连接,那么开启 SSL 连接之后,如何将对应的 http 访问自动跳转到 https 上呢?之前介绍了 IIS 用 web.config 做域名的301跳转的方法 ...

  9. 批量插入实体类转化DataTable

    /// <summary> /// 根据实体类得到表结构 /// </summary> /// <param name="model">实体类& ...

  10. MySQL 日期时间相关函数

    第一部分:时间差函数  timestampdiff.datediff.timediff 一.时间差函数:timestampdiff 语法:timestampdiff(interval, datetim ...