为什么现在连Date类都不建议使用了?
一、有什么问题吗java.util.Date
?
java.util.Date
(Date
从现在开始)是一个糟糕的类型,这解释了为什么它的大部分内容在 Java 1.1 中被弃用(但不幸的是仍在使用)。
设计缺陷包括:
- 它的名称具有误导性:它并不代表一个日期,而是代表时间的一个瞬间。所以它应该被称为
Instant
——正如它的java.time
等价物一样。 - 它是非最终的:这鼓励了对继承的不良使用,例如
java.sql.Date
(这意味着代表一个日期,并且由于具有相同的短名称而也令人困惑) - 它是可变的:日期/时间类型是自然值,可以通过不可变类型有效地建模。可变的事实
Date
(例如通过setTime
方法)意味着勤奋的开发人员最终会在各处创建防御性副本。 - 它在许多地方(包括)隐式使用系统本地时区,
toString()
这让许多开发人员感到困惑。有关此内容的更多信息,请参阅“什么是即时”部分 - 它的月份编号是从 0 开始的,是从 C 语言复制的。这导致了很多很多相差一的错误。
- 它的年份编号是基于 1900 年的,也是从 C 语言复制的。当然,当 Java 出现时,我们已经意识到这不利于可读性?
- 它的方法命名不明确:
getDate()
返回月份中的某一天,并getDay()
返回星期几。给这些更具描述性的名字有多难? - 对于是否支持闰秒含糊其辞:“秒由 0 到 61 之间的整数表示;值 60 和 61 仅在闰秒时出现,即使如此,也仅在实际正确跟踪闰秒的 Java 实现中出现。” 我强烈怀疑大多数开发人员(包括我自己)都做了很多假设,认为 for 的范围
getSeconds()
实际上在 0-59 范围内(含)。 - 它的宽容没有明显的理由:“在所有情况下,为这些目的而对方法给出的论据不必落在指定的范围内; 例如,日期可以指定为 1 月 32 日,并被解释为 2 月 1 日。” 多久有用一次?
关键原因如下:
原文如下:为什么要避免使用Date类?
二、为啥要改?
我们要改的原因很简单,我们的代码缺陷扫描规则认为这是一个必须修改的缺陷,否则不给发布,不改不行,服了。
解决思路:避免使用
java.util.Date
与java.sql.Date
类和其提供的API,考虑使用java.time.Instant
类或java.time.LocalDateTime
类及其提供的API替代。
三、怎么改?
只能说这种基础的类改起来牵一发动全身,需要从DO实体类看起,然后就是各种Converter,最后是DTO。由于我们还是微服务架构,业务服务依赖于基础服务的API,所以必须要一起改否则就会报错。这里就不细说修改流程了,主要说一下我们在改造的时候遇到的一些问题。
1. 耐心比对数据库日期字段和DO的映射
(1)确定字段类型
首先你需要确定数据对象中的 Date
字段代表的是日期、时间还是时间戳。
- 如果字段代表日期和时间,则可能需要使用
LocalDateTime
。 - 如果字段仅代表日期,则可能需要使用
LocalDate
。 - 如果字段仅代表时间,则可能需要使用
LocalTime
。 - 如果字段需要保存时间戳(带时区的),则可能需要使用
Instant
或ZonedDateTime
。
(2)更新数据对象类
更新数据对象类中的字段,把 Date
类型改为适当的 java.time
类型。
2. 将DateUtil中的方法改造
(1)替换原来的new Date()和Calendar.getInstance().getTime()
原来的方式:
Date nowDate = new Date();
Date nowCalendarDate = Calendar.getInstance().getTime();
使用 java.time
改造后:
// 使用Instant代表一个时间点,这与Date类似
Instant nowInstant = Instant.now();
// 如果需要用到具体的日期和时间(例如年、月、日、时、分、秒)
LocalDateTime nowLocalDateTime = LocalDateTime.now();
// 如果你需要和特定的时区交互,可以使用ZonedDateTime
ZonedDateTime nowZonedDateTime = ZonedDateTime.now();
// 如果你需要转换回java.util.Date,你可以这样做(假设你的代码其他部分还需要使用Date)
Date nowFromDateInstant = Date.from(nowInstant);
// 如果需要与java.sql.Timestamp交互
java.sql.Timestamp nowFromInstant = java.sql.Timestamp.from(nowInstant);
一些注意点:
Instant
表示的是一个时间点,它是时区无关的,相当于旧的Date
类。它通常用于表示时间戳。LocalDateTime
表示没有时区信息的日期和时间,它不能直接转换为时间戳,除非你将其与时区结合使用(例如通过ZonedDateTime
)。ZonedDateTime
包含时区信息的日期和时间,它更类似于Calendar
,因为Calendar
也包含时区信息。- 当你需要将
java.time
对象转换回java.util.Date
对象时,可以使用Date.from(Instant)
方法。这在你的代码需要与旧的API或库交互时非常有用。
(2)一些基础的方法改造
a. dateFormat
原来的方式
public static String dateFormat(Date date, String dateFormat) {
SimpleDateFormat formatter = new SimpleDateFormat(dateFormat);
return formatter.format(date);
}
使用java.time
改造后
public static String dateFormat(LocalDateTime date, String dateFormat) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(dateFormat);
return date.format(formatter);
}
b. addSecond、addMinute、addHour、addDay、addMonth、addYear
原来的方式
public static Date addSecond(Date date, int second) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(13, second);
return calendar.getTime();
}
public static Date addMinute(Date date, int minute) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(12, minute);
return calendar.getTime();
}
public static Date addHour(Date date, int hour) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(10, hour);
return calendar.getTime();
}
public static Date addDay(Date date, int day) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(5, day);
return calendar.getTime();
}
public static Date addMonth(Date date, int month) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(2, month);
return calendar.getTime();
}
public static Date addYear(Date date, int year) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(1, year);
return calendar.getTime();
}
使用java.time
改造后
public static LocalDateTime addSecond(LocalDateTime date, int second) {
return date.plusSeconds(second);
}
public static LocalDateTime addMinute(LocalDateTime date, int minute) {
return date.plusMinutes(minute);
}
public static LocalDateTime addHour(LocalDateTime date, int hour) {
return date.plusHours(hour);
}
public static LocalDateTime addDay(LocalDateTime date, int day) {
return date.plusDays(day);
}
public static LocalDateTime addMonth(LocalDateTime date, int month) {
return date.plusMonths(month);
}
public static LocalDateTime addYear(LocalDateTime date, int year) {
return date.plusYears(year);
}
c. dateToWeek
原来的方式
public static final String[] WEEK_DAY_OF_CHINESE = new String[]{"周日", "周一", "周二", "周三", "周四", "周五", "周六"};
public static String dateToWeek(Date date) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
return WEEK_DAY_OF_CHINESE[cal.get(7) - 1];
}
使用java.time
改造后
public static final String[] WEEK_DAY_OF_CHINESE = new String[]{"周日", "周一", "周二", "周三", "周四", "周五", "周六"};
public static String dateToWeek(LocalDate date) {
DayOfWeek dayOfWeek = date.getDayOfWeek();
return WEEK_DAY_OF_CHINESE[dayOfWeek.getValue() % 7];
}
d. getStartOfDay和getEndOfDay
原来的方式
public static Date getStartTimeOfDay(Date date) {
if (date == null) {
return null;
} else {
LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(date.getTime()), ZoneId.systemDefault());
LocalDateTime startOfDay = localDateTime.with(LocalTime.MIN);
return Date.from(startOfDay.atZone(ZoneId.systemDefault()).toInstant());
}
}
public static Date getEndTimeOfDay(Date date) {
if (date == null) {
return null;
} else {
LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(date.getTime()), ZoneId.systemDefault());
LocalDateTime endOfDay = localDateTime.with(LocalTime.MAX);
return Date.from(endOfDay.atZone(ZoneId.systemDefault()).toInstant());
}
}
使用java.time
改造后
public static LocalDateTime getStartTimeOfDay(LocalDateTime date) {
if (date == null) {
return null;
} else {
// 获取一天的开始时间,即00:00
return date.toLocalDate().atStartOfDay();
}
}
public static LocalDateTime getEndTimeOfDay(LocalDateTime date) {
if (date == null) {
return null;
} else {
// 获取一天的结束时间,即23:59:59.999999999
return date.toLocalDate().atTime(LocalTime.MAX);
}
}
e. betweenStartAndEnd
原来的方式
public static Boolean betweenStartAndEnd(Date nowTime, Date beginTime, Date endTime) {
Calendar date = Calendar.getInstance();
date.setTime(nowTime);
Calendar begin = Calendar.getInstance();
begin.setTime(beginTime);
Calendar end = Calendar.getInstance();
end.setTime(endTime);
return date.after(begin) && date.before(end);
}
使用java.time
改造后
public static Boolean betweenStartAndEnd(Instant nowTime, Instant beginTime, Instant endTime) {
return nowTime.isAfter(beginTime) && nowTime.isBefore(endTime);
}
我这里就只列了一些,如果有缺失的可以自己补充,不会写的话直接问问ChatGPT,它最会干这事了。最后把这些修改后的方法替换一下就行了。
四、小结一下
这个改造难度不高,但是复杂度非常高,一个地方没改好,轻则接口报错,重则启动失败,非常耗费精力,真不想改。
为什么现在连Date类都不建议使用了?的更多相关文章
- Java基础教程——Date类和Calendar类
Date类和Calendar类都是关于日期的类,都在java.util包中,使用时需要import. Date java.util.Date类的对象用来表示时间和日期,用得最多的是获取系统当前日期和时 ...
- 疯狂java笔记(五) - 系统交互、System、Runtime、Date类
一.程序与用户交互(Java的入口方法-main方法): 运行Java程序时,都必须提供一个main方法入口:public static void main(String[] args){} publ ...
- Java基础(38):Calendar类的应用(优于Date类)
Calendar 类的应用 Date 类最主要的作用就是获得当前时间,同时这个类里面也具有设置时间以及一些其他的功能,但是由于本身设计的问题,这些方法却遭到众多批评,不建议使用,更推荐使用 Calen ...
- Java的Date类与Calendar类
一:Date类 在JDK1.0中,Date类是唯一的一个代表时间的类,但是由于Date类不便于实现国际化,所以从JDK1.1版本开始,推荐使用Calendar类进行时间和日期处理.这里简单介绍一下Da ...
- java基础系列--Date类
原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/7126930.html 1.Date类概述 Date类是从JDK1.1就开始存在的老类,其提 ...
- Java学习笔记22(Date类、DateFormat类)
Date,时间和日期类,这里讲util包中的而不是sql包中的 Date:表示特定的时间瞬间,精确到毫秒(1000毫秒=1秒) 时间和日期的操作都基于毫秒值 时间原点:1970年1月1日,0时0分0秒 ...
- [Day16]常用API(正则表达式、Date类、DateFormat类、Calendar类)
1.正则表达式(Regular Expression,regex)-是一个字符串,使用单个字符串来描述.用来定义匹配规则,匹配一系列符合某个句法规则的字符串 1.1匹配规则: (1)字符:x -代表的 ...
- day319 1、正则表达式的定义及使用 2、Date类的用法 3、Calendar类的用法
1.正则表达式的定义及使用2.Date类的用法3.Calendar类的用法 一.正则表达式 ###01正则表达式的概念和作用* A: 正则表达式的概念和作用* a: 正则表达式的概述* 正则表达式也是 ...
- JavaSE核心之一:Date类、Calendar类、Math类、枚举;
1.Date类 1) java.util.Date类用于封装日期及时间信息,一般仅用它显示某个日期,不对他作任何操作处理,作处理用Calendar类,计算方便. 2) Date 类本质上拥有一个lon ...
- JavaScript的Date类的函数特殊处理导致的问题
记得以前参加校招的时候,总是有日期相关的面试题,比如计算两个日期之间的间隔天数.以前还觉得这种题就是为了纯粹为了面试的,但工作了之后,还就碰到了跟日期相关的bug.下面是一段js代码,是要把字符串描述 ...
随机推荐
- FFmpeg的录制命令
FFmpeg的录制命令 命令的作用 它可以捕捉桌面窗口,摄像头的视频流和麦克风的音频流. 命令的格式 ffmpeg [输入格式] [输入选项] -i [输入设备索引] [输出选项] 输出文件 其中输入 ...
- FileReader读取文件(文本和图片)
1.读取文本 let file = input_file.files[0] let fr = new FileReader(); fr.readAsText(file,'gb2312'); //设置文 ...
- Netty-介绍-1
Netty介绍和应用场景 要求 已经掌握了 主要技术构成: Java OOP 编程. Java 多线程编程. Java IO 编程 . Java 网络编程. 常用的Java 设计模式(比如 观察者模式 ...
- Git企业开发控制理论和实操-从入门到深入(一)|为什么需要Git|Git的安装
前言 那么这里博主先安利一些干货满满的专栏了! 首先是博主的高质量博客的汇总,这个专栏里面的博客,都是博主最最用心写的一部分,干货满满,希望对大家有帮助. 高质量博客汇总https://blog.cs ...
- 【OpenCV】基于cv2的图像阈值化处理【超详细的注释和解释】掌握基本操作
说在前面的话 博主今天给大家带来人工智能的一个重要领域的入门操作,opencv包的使用和基本操作,希望大家可以从中学到一些东西! 前言 那么这里博主先安利一下一些干货满满的专栏啦! 手撕数据结构htt ...
- laravel生成二维码,并添加背景图片,图标logo
1.安装组件 composer require simplesoftwareio/simple-qrcode 1.3.* 在 config/app.php 注册服务提供者: SimpleSoftwar ...
- TActionManager Delphi 超级方便的快捷键 草草
delphi 中用快捷键 草 实在是 太简单了 . 自己摸索出来的 方法 --------------------------------------------------------------- ...
- 【LGR-148-Div.3】洛谷基础赛 #1 & MGOI Round I
[LGR-148-Div.3]洛谷基础赛 #1 & MGOI Round I T1 luoguP9502 『MGOI』Simple Round I | A. 魔法数字 \(100pts\) 水 ...
- ES6学习 第六章 数值的扩展
前言 本章介绍数值的扩展.新增了很多方法,有些不常用的方法了解即可. 本章原文链接:数值的扩展 进制表示法 ES6 提供了二进制和八进制数值的新的写法,分别用前缀0b(或0B)和0o(或0O)表示. ...
- 每月免费调用1000次API调用:实现PDF转档、页面编辑、OCR
每月1000次免费PDF API调用: 使用ComPDFKit API充分发挥您PDF转换的全部潜力 您是否在寻找无需前期投资即可提升软件集成能力的途径?再也不用找了!我们先进的API为您的项目提供所 ...