您的位置 首页 java

Java的日期和时间API使用备忘

不做软件项目可能永远也不会考虑日期和时间的使用问题。上学时做的最多的是数学模型和算法的仿真验证,根本不用操心软件里如何表示日期和时间,或者如何计算两个时间或日期间的差(小学数学的水平吧),但实际工作中,却会有大量的需要考虑日期和时间的表示和计算问题,稍有不慎就会犯下错误,如果再是个“国际项目”,带上时区再做处理,那可就不是随便找个小学生都能做的问题了。

据说, java 8以前的API对日期和时间的支持就不是很好(java.util.Date以及java.util.Calendar),广遭诟病,其中就有用0 – 11表示1月至12月的奇葩设计,带来了很多后续处理的问题,以致很多码农表示日期时间要么用数组,要么自建,要么转投第三方库。

Java 8 在日期和时间处理上利用“后发优势”,不继续在java.util包内“修修补补”,推出java.time包“另起炉灶”。这里就简单介绍Java 8的日期和时间API,推荐一本Java 8教程,并给出两个特殊的应用示例。

1. Java 8的日期和时间

java.time包

LocalDate、 LocalTime 、Instant、Duration及Period是Java 8提供的日期和时间API。关于它的使用,网上很多博客在讲(应该有讲的好的),比较推荐的是《Java 8 实战》(《Java 8 in Action》),书上讲的比较系统,有了全面掌握后,如果日后工作中再有问题就能针对性地百度下,提高效率。

2. 应用场景

*LocalDate、LocalTime、LocalDateTime

从关键词即可推测出,LocalDate用于日期,如:“2017-07-14”;

*LocalTime用于时间,如:“12:01:56”;

*LocalDateTime用于既有日期又有时间,如:“2017-07-14T12:01:56”,还可以表示到精度为纳秒级,带时区的格式。

字符“T”在后面介绍的几种DateTimeFormatter提供的ISO标准中都用,具体含义没去查证,猜测可能是单词“Time”的缩写。如果不希望在输入 字符串 组中包含字母T,那就按照上述示例,利用ofPattern定制一个日期时间格式器即可。

*Period、Duration及Instant

有了日期和时间,那么比较两个日期或时间之间的差异, 或者计算两者间的时间差就是再自然不过的需求了。

Period用于比较两个具体日期的年、月、日的差值,如:”1997-07-01″至”2017-08-11″的日期差为“P20Y1M10D”(20 years, 1 month and 10days);

Duration用于比较两个时间的秒或纳秒级的差值,如:“12:01:00”与“12:01:30”的时间为“PT30S”(30 seconds);

Instant用于测试时比较程序中两个时间标签之间的差值,比如某个耗时操作前后的时间差值。

注意: Instant.now()获取的时间主要是为了设定时间标签,便于日后测试两个时间标签间的时长,并不能看做准确的当下时间。

*DateTimeFormatter

DateTimeFormatter不仅可以通过ofPattern自定义日期时间格式,还有一些标准化的格式可供使用。可根据需要参照 javadoc 进行使用。

DateTimeFormmatter提供的标准日期时间格式

稍微花点时间熟悉该API的使用方法和异常处理,并根据应用场景选用Java 8日期时间API,可以提高编码效率,减少不必要的错误。

3. 典型应用

*获取两个日期间的天数

假如需要求解两个日期节点间的天数,如果按照javadoc上文档说明,很可能第一反应使用Period去计算两个日期的差,然后再通过调用getDays获取总天数。这种做法在同年同月的处理时没有问题的,比如香港回归纪念日和今天的日期差:

但如果不同月不同年就会出现问题,比如香港回归纪念日和建军90周年的日期差:

原因很简单,因为 静态方法 回值是Period,而刚才也讲到,Period的返回值是“P{年数}Y{月数}M{日数}D”,调用的getDays方法实际返回的就是这个{日数},所以输出的上述结果就不足为奇了。

当然,Period的这种表示在很多情况下还是有用的,但实际中还是有时需要具体知道两个日期间差了多少天,有没有官方支持啊,总不至于对相差的每个月判断大小月、如果里面刚好还有个2月,还要再判断个闰年后再相加吧。

显然不会的,使用LocalDate演示一下:

ChronoUnit还有其它的计算单元,可以逐一试试,但要注意,如果调用until方法的实例不支持该计算单元就会报出UnsupportedTemporalTypeException异常,比如在上面的例子中将“ChronoUnit.DAYS”改为“ChronoUnit.HOURS”。

*解析日期

习惯上对日期的解析都是通过字符串进行的,那么这时候如果选用的parse方法或者DateTimeFormatter的格式设置有误也是会报错的。比如有时我们为了简便,会用“2017-1-1”表示今年元旦,但如果用默认parse方法解析会成功吗?

默认解析“2017-1-1”报出异常

那用DateTimeFormatter的ofPattern方法自定义一个应该就可以了吧

其中,字母“y”表示年,“M”表示月,“d”表示日。

不好意思,还是报出异常:

使用“yyyy-MM-dd”解析“2017-1-1”报出异常

其实上述做法在解析“2017-01-01”或者其它两位表示的“月”和“日”的时候是完全正确的,但一旦图省事或者不巧遇到粗心汉子对单数日就不想在前面补“0”,那么错误就发生了。

为了避免这种情况,正确的做法是:

插句题外话,前一段时间就是这样的错误让我在启动的异步线程中发生了错误,而由于采用了 回调 机制,回调方法认为此时执行失败直接返回failed方法,加之自己也没有在此做异常捕获,没有任何异常提示,不过还好日志做的比较完善,基本能够很快定位,但由于压根没想到这里会出问题,足足花了一下午时间逐行排查才找到问题原因。

目前遇到的就这么多了,以后碰到其它再补充。

END

文章来源:智云一二三科技

文章标题:Java的日期和时间API使用备忘

文章地址:https://www.zhihuclub.com/180270.shtml

关于作者: 智云科技

热门文章

网站地图