Java日期处理易踩的十个坑
前言
整理了Java日期处理的十个坑,希望对大家有帮助。
一、用Calendar设置时间的坑
反例:
Calendar c = Calendar.getInstance();
c.set(Calendar.HOUR, 10);
System.out.println(c.getTime());
运行结果:
Thu Mar 26 22:28:05 GMT+08:00 2020
解析:
我们设置了10小时,但运行结果是22点,而不是10点。因为Calendar.HOUR默认是按12小时制处理的,需要使用Calendar.HOUR_OF_DAY,因为它才是按24小时处理的。
正例:
Calendar c = Calendar.getInstance();
c.set(Calendar.HOUR_OF_DAY, 10);
二、Java日期格式化YYYY的坑
反例:
Calendar calendar = Calendar.getInstance();
calendar.set(2019, Calendar.DECEMBER, 31);
Date testDate = calendar.getTime();
SimpleDateFormat dtf = new SimpleDateFormat("YYYY-MM-dd");
System.out.println("2019-12-31 转 YYYY-MM-dd 格式后 " + dtf.format(testDate));
运行结果:
2019-12-31 转 YYYY-MM-dd 格式后 2020-12-31
解析:
为什么明明是2019年12月31号,就转了一下格式,就变成了2020年12月31号了?因为YYYY是基于周来计算年的,它指向当天所在周属于的年份,一周从周日开始算起,周六结束,只要本周跨年,那么这一周就算下一年的了。正确姿势是使用yyyy格式。
正例:
Calendar calendar = Calendar.getInstance();
calendar.set(2019, Calendar.DECEMBER, 31);
Date testDate = calendar.getTime();
SimpleDateFormat dtf = new SimpleDateFormat("yyyy-MM-dd");
System.out.println("2019-12-31 转 yyyy-MM-dd 格式后 " + dtf.format(testDate));
三、Java日期格式化hh的坑。
反例:
String str = "2020-03-18 12:00";
SimpleDateFormat dtf = new SimpleDateFormat("yyyy-MM-dd hh:mm");
Date newDate = dtf.parse(str);
System.out.println(newDate);
运行结果:
Wed Mar 18 00:00:00 GMT+08:00 2020
解析:
设置的时间是12点,为什么运行结果是0点呢?因为hh是12制的日期格式,当时间为12点,会处理为0点。正确姿势是使用HH,它才是24小时制。
正例:
String str = "2020-03-18 12:00";
SimpleDateFormat dtf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
Date newDate = dtf.parse(str);
System.out.println(newDate);
四、Calendar获取的月份比实际数字少1即(0-11)
反例:
//获取当前月,当前是3月
Calendar calendar = Calendar.getInstance();
System.out.println("当前"+calendar.get(Calendar.MONTH)+"月份");
运行结果:
当前2月份
解析:
The first month of the year in the Gregorian and Julian calendars
is <code>JANUARY</code> which is 0;
也就是1月对应的是下标 0,依次类推。因此获取正确月份需要加 1.
正例:
//获取当前月,当前是3月
Calendar calendar = Calendar.getInstance();
System.out.println("当前"+(calendar.get(Calendar.MONTH)+1)+"月份");
五、Java日期格式化DD的坑
反例:
Calendar calendar = Calendar.getInstance();
calendar.set(2019, Calendar.DECEMBER, 31);
Date testDate = calendar.getTime();
SimpleDateFormat dtf = new SimpleDateFormat("yyyy-MM-DD");
System.out.println("2019-12-31 转 yyyy-MM-DD 格式后 " + dtf.format(testDate));
运行结果:
2019-12-31 转 yyyy-MM-DD 格式后 2019-12-365
解析:
DD和dd表示的不一样,DD表示的是一年中的第几天,而dd表示的是一月中的第几天,所以应该用的是dd。
正例:
Calendar calendar = Calendar.getInstance();
calendar.set(2019, Calendar.DECEMBER, 31);
Date testDate = calendar.getTime();
SimpleDateFormat dtf = new SimpleDateFormat("yyyy-MM-dd");
System.out.println("2019-12-31 转 yyyy-MM-dd 格式后 " + dtf.format(testDate));
六、SimleDateFormat的format初始化问题
反例:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
System.out.println(sdf.format(20200323));
运行结果:
1970-01-01
解析:
用format格式化日期是,要输入的是一个Date类型的日期,而不是一个整型或者字符串。
正例:
Calendar calendar = Calendar.getInstance();
calendar.set(2020, Calendar.MARCH, 23);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
System.out.println(sdf.format(calendar.getTime()));
七、日期本地化问题
反例:
String dateStr = "Wed Mar 18 10:00:00 2020";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss yyyy");
LocalDateTime dateTime = LocalDateTime.parse(dateStr, formatter);
System.out.println(dateTime);
运行结果:
Exception in thread "main" java.time.format.DateTimeParseException: Text 'Wed Mar 18 10:00:00 2020' could not be parsed at index 0
at java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1949)
at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1851)
at java.time.LocalDateTime.parse(LocalDateTime.java:492)
at com.example.demo.SynchronizedTest.main(SynchronizedTest.java:19)
解析:
DateTimeFormatter 这个类默认进行本地化设置,如果默认是中文,解析英文字符串就会报异常。可以传入一个本地化参数(Locale.US)解决这个问题
正例:
String dateStr = "Wed Mar 18 10:00:00 2020";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss yyyy",Locale.US);
LocalDateTime dateTime = LocalDateTime.parse(dateStr, formatter);
System.out.println(dateTime);
八、SimpleDateFormat 解析的时间精度问题
反例:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String time = "2020-03";
System.out.println(sdf.parse(time));
运行结果:
Exception in thread "main" java.text.ParseException: Unparseable date: "2020-03"
at java.text.DateFormat.parse(DateFormat.java:366)
at com.example.demo.SynchronizedTest.main(SynchronizedTest.java:19)
解析:
SimpleDateFormat 可以解析长于/等于它定义的时间精度,但是不能解析小于它定义的时间精度。
正例:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM");
String time = "2020-03";
System.out.println(sdf.parse(time));
九、SimpleDateFormat 的线性安全问题
反例:
public class SimpleDateFormatTest {
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 100, 1, TimeUnit.MINUTES, new LinkedBlockingQueue<>(1000));
while (true) {
threadPoolExecutor.execute(() -> {
String dateString = sdf.format(new Date());
try {
Date parseDate = sdf.parse(dateString);
String dateString2 = sdf.format(parseDate);
System.out.println(dateString.equals(dateString2));
} catch (ParseException e) {
e.printStackTrace();
}
});
}
}
运行结果:
Exception in thread "pool-1-thread-49" java.lang.NumberFormatException: For input string: "5151."
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Long.parseLong(Long.java:589)
at java.lang.Long.parseLong(Long.java:631)
at java.text.DigitList.getLong(DigitList.java:195)
at java.text.DecimalFormat.parse(DecimalFormat.java:2051)
at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:2162)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
at java.text.DateFormat.parse(DateFormat.java:364)
at com.example.demo.SimpleDateFormatTest.lambda$main$0(SimpleDateFormatTest.java:19)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Exception in thread "pool-1-thread-47" java.lang.NumberFormatException: For input string: "5151."
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Long.parseLong(Long.java:589)
at java.lang.Long.parseLong(Long.java:631)
at java.text.DigitList.getLong(DigitList.java:195)
at java.text.DecimalFormat.parse(DecimalFormat.java:2051)
at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:2162)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
at java.text.DateFormat.parse(DateFormat.java:364)
at com.example.demo.SimpleDateFormatTest.lambda$main$0(SimpleDateFormatTest.java:19)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
解析:
全局变量的SimpleDateFormat,在并发情况下,存在安全性问题。
- SimpleDateFormat继承了 DateFormat
- DateFormat类中维护了一个全局的Calendar变量
- sdf.parse(dateStr)和sdf.format(date),都是由Calendar引用来储存的。
- 如果SimpleDateFormat是static全局共享的,Calendar引用也会被共享。
- 又因为Calendar内部并没有线程安全机制,所以全局共享的SimpleDateFormat不是线性安全的。
解决SimpleDateFormat线性不安全问题,有三种方式:
- 将SimpleDateFormat定义为局部变量
- 使用ThreadLocal。
- 方法加同步锁synchronized。
正例:
public class SimpleDateFormatTest {
private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
private static ThreadLocal<DateFormat> threadLocal = new ThreadLocal<DateFormat>();
public static DateFormat getDateFormat() {
DateFormat df = threadLocal.get();
if(df == null){
df = new SimpleDateFormat(DATE_FORMAT);
threadLocal.set(df);
}
return df;
}
public static String formatDate(Date date) throws ParseException {
return getDateFormat().format(date);
}
public static Date parse(String strDate) throws ParseException {
return getDateFormat().parse(strDate);
}
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 100, 1, TimeUnit.MINUTES, new LinkedBlockingQueue<>(1000));
while (true) {
threadPoolExecutor.execute(() -> {
try {
String dateString = formatDate(new Date());
Date parseDate = parse(dateString);
String dateString2 = formatDate(parseDate);
System.out.println(dateString.equals(dateString2));
} catch (ParseException e) {
e.printStackTrace();
}
});
}
}
}
十、Java日期的夏令时问题
反例:
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"));
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(sdf.parse("1986-05-04 00:30:00"));
运行结果:
Sun May 04 01:30:00 CDT 1986
解析:
先了解一下夏令时
- 夏令时,表示为了节约能源,人为规定时间的意思。
- 一般在天亮早的夏季人为将时间调快一小时,可以使人早起早睡,减少照明量,以充分利用光照资源,从而节约照明用电。
- 各个采纳夏时制的国家具体规定不同。目前全世界有近110个国家每年要实行夏令时。
- 1986年4月,中国中央有关部门发出“在全国范围内实行夏时制的通知”,具体作法是:每年从四月中旬第一个星期日的凌晨2时整(北京时间),将时钟拨快一小时。(1992年起,夏令时暂停实行。)
- 夏时令这几个时间可以注意一下哈,1986-05-04, 1987-04-12, 1988-04-10, 1989-04-16, 1990-04-15, 1991-04-14.
结合demo代码,中国在1986-05-04当天还在使用夏令时,时间被拨快了1个小时。所以0点30分打印成了1点30分。如果要打印正确的时间,可以考虑修改时区为东8区。
正例:
TimeZone.setDefault(TimeZone.getTimeZone("GMT+8"));
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(sdf.parse("1986-05-04 00:30:00"));
个人公众号
- 觉得写得好的小伙伴给个点赞+关注啦,谢谢~
- 如果有写得不正确的地方,麻烦指出,感激不尽。
- 同时非常期待小伙伴们能够关注我公众号,后面慢慢推出更好的干货~嘻嘻
Java日期处理易踩的十个坑的更多相关文章
- Java 重要知识点,踩过的坑
(1),关于 LinkedHashMap TreeMap HashMap 之间的区别: HashMap 是无序的,LinkedHashMap 由于内部维护了一个记录的链表,数据操作的前后顺序都会在链 ...
- 踩过无数坑实现的哈夫曼压缩(JAVA)
最近可能又是闲着没事干了,就想做点东西,想着还没用JAVA弄过数据结构,之前搞过算法,就试着写写哈夫曼压缩了. 本以为半天就能写出来,结果,踩了无数坑,花了整整两天时间!!orz...不过这次踩坑,算 ...
- "开发路上踩过的坑要一个个填起来————持续更新······(7月30日)"
欢迎转载,请注明出处! https://gii16.github.io/learnmore/2016/07/29/problem.html 踩过的坑及解决方案记录在此篇博文中! 个人理解,如有偏颇,欢 ...
- 《C++之那些年踩过的坑(二)》
C++之那些年踩过的坑(二) 作者:刘俊延(Alinshans) 本系列文章针对我在写C++代码的过程中,尤其是做自己的项目时,踩过的各种坑.以此作为给自己的警惕. 今天讲一个小点,虽然小,但如果没有 ...
- 初学spring boot踩过的坑
一.搭建spring boot环境 maven工程 pom文件内容 <project xmlns="http://maven.apache.org/POM/4.0.0" xm ...
- java日期详解
[TOC] 一.简介 java中的日期处理一直是个问题,没有很好的方式去处理,所以才有第三方框架的地位比如joda. 文章主要对java日期处理的详解,用1.8可以不用joda. 1. 相关概念 首先 ...
- elasticsearch2.3.3集群搭建踩到的坑
本文来自我的github pages博客http://galengao.github.io/ 即www.gaohuirong.cn 摘要: 作者原来搭建的环境是0.95版本 现在升级到2.3.3版本, ...
- wrk 使用记录及踩过的坑
wrk是什么?https://github.com/wg/wrk wrk 是一个非常小巧高效的开源性能测试工具,支持lua脚本来创建复杂的测试场景.wrk 的一个很好的特性就是能用很少的线程压出很大的 ...
- 在Mac osx使用ADT Bundle踩过的坑
前言 本篇博客整理一下笔者在Mac下使用ADT Bundle踩过的坑,Google现在也不支持Eclipse了,开发者也到了抛弃Eclipse的时候,但考虑到大部分Java的开发者还是比较习惯与Ecl ...
随机推荐
- javascript中this的四种用法
javascript中this的四种用法 投稿:hebedich 字体:[增加 减小] 类型:转载 时间:2015-05-11我要评论 在javascript当中每一个function都是一个对象,所 ...
- RocketMQ介绍与实践
一.RocketMQ介绍 1.相关术语名词 1. NameSrv:是一个几乎无状态节点,可集群部署,节点之间无任何信息同步. 2. Broker:分为Master与Slave,一个 ...
- RxJava学习笔记(操作符)
前言 上一篇文章介绍了RxJava的基础知识和简单实现,篇幅已经比较多了,所以把操作符(Operators)相关的内容放在这一篇.有了上一篇文章的基础,相信会比较容易理解操作符相关的内容了. 操作符( ...
- 使用Lucene.Net做一个简单的搜索引擎-全文索引
Lucene.Net Lucene.net是Lucene的.net移植版本,是一个开源的全文检索引擎开发包,即它不是一个完整的全文检索引擎,而是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎. ...
- 11--PHP中的类和对象
PHP类和对象 类是面向对象程序设计的基本概念,通俗的理解类就是对现实中某一个种类的东西的抽象, 比如汽车可以抽象为一个类,汽车拥有名字.轮胎.速度.重量等属性,可以有换挡.前进.后退等操作方法. 通 ...
- Java 在PDF中添加表格
本文将介绍通过Java编程在PDF文档中添加表格的方法.添加表格时,可设置表格边框.单元格对齐方式.单元格背景色.单元格合并.插入图片.设置行高.列宽.字体.字号等. 使用工具:Free Spire. ...
- 7-5 jmu-python-分段函数1 (10 分)
本题目要求计算下列分段函数f(x)的值(x为从键盘输入的一个任意实数): 输入格式: 直接输入一个实数给 x,没有其他任何附加字符. 输出格式: 在一行中按“f(x)=result”的格式输出,其中x ...
- 《即时消息技术剖析与实战》学习笔记11——IM系统如何保证服务高可用:流量控制和熔断机制
IM 系统的不可用主要有以下两个原因: 一是无法预测突发流量,即使进行了服务拆分.自动扩容,但流量增长过快时,服务已经不可用了: 二是业务中依赖的这些接口.资源不可用或变慢时,比如发消息可能需要依赖& ...
- 什么是phpMyAdmin
phpMyAdmin 是一个以PHP为基础,以Web-Base方式架构在网站主机上的MySQL的数据库管理工具,让管理者可用Web接口管理MySQL数据库.借由此Web接口可以成为一个简易方式输入繁杂 ...
- 置顶,博客中所有源码 github
所有项目源代码,开源地址. 作者 github 主页 https://github.com/nejidev 目前开源项目有: 1, linux tea5767 at24c08 mmap 实现fm 收音 ...