前言

整理了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.HOUROFDAY,因为它才是按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 0at 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();}});}}
}

10、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 日期处理的十个坑相关推荐

  1. el-date-picker设置默认日期_程序员必备:Java 日期处理的十个坑

    前言 整理了Java日期处理的十个坑,希望对大家有帮助. 一.用Calendar设置时间的坑 反例: Calendar c = Calendar.getInstance(); c.set(Calend ...

  2. Java程序员必备 : Java反编译神器——“GUI” 资源分享

    GUI简介 当我们编写完成一个java类后,运行结果会得到一个class文件,这种二进制文件如果用普通记事本打开,就会出现各种乱码现象,令人十分头疼,当我们要查看.class文件的源代码时,可以通过反 ...

  3. 吐血整理-高级程序员必备Java并发编程原理,没时间看建议收藏

    简介: Java线程之间的通信对程序员完全透明,内存可见性问题很容易困扰Java程序员,这一系列几篇文章将揭开Java内存模型的神秘面纱.这一系列的文章大致分4个部分,分别是: Java内存模型基础, ...

  4. java程序_Java程序员必备----Java命令大全

    1.java.exe: ====================== 运行java程序,这个相信每一位用Java的人知道了.2.javac.exe: ====================== 编译 ...

  5. Java 程序员必备的 15 个框架,前 3 个地位无可动摇!

    2019独角兽企业重金招聘Python工程师标准>>> Java 程序员方向太多,且不说移动开发.大数据.区块链.人工智能这些,大部分 Java 程序员都是 Java Web/后端开 ...

  6. Java程序员必备的10个大数据框架

    当今IT开发人员面对的最大挑战就是复杂性,硬件越来越复杂,OS越来越复杂,编程语言和API越来越复杂,我们构建的应用也越来越复杂.根据外媒的一项调查报告,小千列出了Java程序员在过去12个月内一直使 ...

  7. java的六大_java程序员必备的六大工具!

    原标题:java程序员必备的六大工具! Java程序员都会有套工具来应对工作上的挑战.多年来,Java 程序员使用软件来完成他们的工作.有很多工具对他们是有用的,不过对于初入行的人员来说,寻找合适的工 ...

  8. Java程序员必备的几款开发工具,高效才是硬道理!

    作为一名优秀的Java程序员,怎能没有几款得心应手的高效开发工具呢!市面上类库.工具千千万,下面我就给大家推荐几款高效的Java开发工具. 一.Eclipse Eclipse做为一款开发源代码的Jav ...

  9. Java程序员必备的几款开发工具

    工欲善其事,必先利其器.作为一名优秀的Java程序员,怎能没有几款得心应手的高效开发工具呢!市面上类库.工具千千万,下面就给大家推荐几款高效的Java开发工具. 1.UItraEdit UltraEd ...

最新文章

  1. 南邮 AAencode
  2. SQL Server2005 ROW_NUMBER() OVER 使用
  3. java向飞秋发文件_Java 给飞秋发送消息
  4. python是如何实现进程池和线程池的_进程、线程、线程池和协程如何理解?
  5. 修改数据无法提交_数据库系统原理
  6. 桌面圣诞树酷炫特效合集【含动态效果展示及网盘源码分享】
  7. 做一个完整的Java Web项目需要掌握的技能[转]
  8. 自定义表单提交后返回上一页修改
  9. Atitit 集团与个人的完整入口列表 attilax的完整入口 1. 集团与个人的完整入口列表 1 2. 流量入口概念 2 3. 流量入口的历史与发展 2 1.集团与个人的完整入口列表
  10. 设计者模式详解--状态模式
  11. Spring AOP原理分析(一)-- AOP相关概念
  12. 软件开发 | 如何写软件开发文档
  13. 奔图cp2510dn linux,奔图CP2510DN驱动
  14. 学计算机编程要考证吗,程序员可以考的证书有哪些_可以自学吗_上学吧
  15. 典型的递归计算费氏数列
  16. 人工智能、机器学习、深度学习 -- 学习摘记
  17. 一万年太久,只争朝夕_散文网
  18. 深度学习中的感受野计算
  19. 真正手把手教你玩转Git
  20. 老贾笔记--轻轻松松oracle之logmnr篇[吃顿饭的功夫学恢复误删除(delete)的数据]

热门文章

  1. nordic 协议栈区别
  2. 微众WeCross 跨链平台(6)UBI通用区块链接口
  3. Opencv轻松入门:OpenCV入门难?资料少?看完这篇文章就稳了
  4. 有关Non-cacheable,,Cacheable, non-shareable,inner-shareable,outer-shareable的理解
  5. [ARM-assembly]-ARM ASM内联汇编学习
  6. easyre-153 testre寒假逆向生涯(13/100)
  7. 网络安全人才的发展情况是怎么样的呢?快上车,带你了解
  8. 软件调试学习笔记(五)—— 软件断点内存断点
  9. Microsoft RTF栈溢出漏洞(CVE-2010-3333)漏洞分析
  10. MySQL DTAETIME、TIMESTAMP、DATE、TIME、YEAR(日期和时间类型)