一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第13天,点击查看活动详情。
零、故事
小白经过多次面试终于被一家互联网公司录用,但是在上班的第一天便遇到了一个棘手的问题。 部门测试:小白啊,你这个程序显示的不对啊,你看部分订单应该显示的时 2020年12月21日,但是程序显示的时间时 2021年0月5日。 小白:不会吧,我看看。 小白运行起来程序发现确实有部分订单显示不正确,而且还都是12月的订单有问题。小白很是不解,预释在网上查了起来。查着查着无意中发现上家公司的技术总监在某S某N上发布了一篇关于 Java 时间的文章,看完之后小白豁然开朗,迅速的改好了代码,提交到测试手里进行测试。
一、解析
在解析前,我们先看一下小白出错的代码是怎么写的,这里的代码是简化版代码。
Date date = new Date(2020,12,21,0,0,0);
System.out.print(date.getYear()+"年");
System.out.print(date.getMonth()+"月");
System.out.print(date.getDay()+"日");
运行代码,输出结果如下:
我们看到输出的结果竟然是 0 月,难道是我们的机器的问题吗?换了台机器依然还是这个结果,那么要知道是什么原因造成的我们就需要来查看一下 Date 的源代码。
从源代码中我们可以看出来年份设置是从 1900 年开始的,月份是从 0 开始的 0到11分别代表1到12月,前面的代码中我们将月份设置为了12,那么就是代表下一年的第一个月 0 月,getDay 方法其实是返回的是周几,而不是几号。
那么我们该怎么修改呢?我们只需要将 Date 构造函数的 month 参数传递为11,并且在获取日的时候调用
getDate方法即可。修正后的代码如下:
Date date = new Date(2020,11,21,0,0,0);
System.out.print(date.getYear()+"年");
System.out.print(date.getMonth()+"月");
System.out.print(date.getDate()+"日");
到这里为止,经过修改的代码可以正常显示了,但是容易将参数值 11 当作 11 月,把 getDate 方法当作获取 Date 的方法。 在这里我插入一个知识点,在 Java 1.1 中引入了 Calendar 类来修正 Date 类的问题,但是 Calendar 类并没有完全修正好 Date 类的问题,反而出现了和 Date 类同样的问题。我们通过一段代码来讲解为什么 Calendar 类并没有完全修正 Date 类的问题:
Calendar cal = Calendar.getInstance();
cal.set(2020, 12, 21);
System.out.print(cal.get(Calendar.YEAR) + "年");
System.out.print(cal.get(Calendar.MONTH)+"月");
System.out.println(cal.get(Calendar.
DAY_OF_MONTH)+"日");
上面的代码输出结果为:
从输出我们可以看出年份变成了2021年,月份变成了0月,这不就是和 Date 类的问题一样嘛。来看一下源代码:
源码显示月份依然是从 0开始11结束的,也就是说传入的12原本我们想作为12月来显示的,但是经过程序处理却显示成了 0月并且年份也进了一位变成了2021年。这里只是掀开了 Calendar类 和 Date类问题的冰山一角,它们还有严重的问题,例如弱类型、过于复杂的状态空间、低劣的结构和随意的命名等,因此在必须使用 Calendar类 和 Date类的时候千万要小心,一旦遇到不明确的地方尽快查阅 API 文档。 前面两个类都无法在保证代码正确的情况下同时保证良好的可读性,因此出现了第三方时间处理包 Joda Time,Joda Time 不仅方便而且可读性也好,并且可以在 JDK 任何版本上使用。它提供了一组类包用于出来标准的 date 和 time 。它主要包含如下的特点:
- 易用:月份的起始是从 1开始;
- 易扩展:Joda Time 支持的多日历系统是通过基于 Chronology 类的插件体系来实现;
- 提供完整功能:提供所有和 date 、time 相关的计算功能。 下面我们利用 Joda Time 来修改前面的代码:
DateTime dateTime=new DateTime(2020, 12, 21, 0, 0,0);
System.out.println(dateTime.toString("yyyy-MM-dd"));
运行代码得出的结果就是我们想要的结果了。
二、总结
如果你所使用的是 JDK 8及其以上版本的话,可以很方便的使用时间日期类,并且可读性也很好,但是如果你所使用的是 JDK7 及其以下版本的话我建议还是使用 Joda Time 为好。