Java8 时间处理
Table of Contents
前言
时间处理是一个经常会用到但又不那么好用的功能,其中的主要问题在于对人友好的时间格式的处理上。
在这一点上,Java8 提供了方便我们的使用的新的日期和时间接口。
……
当然了,Java8 出来都这么久了,这接口也不算新了 @_@
时间单位和时区
时间是我们每天都在接触的东西,但我并不是很清楚和它的相关的一些概念,借着这次机会,去了解了时间处理中常用的两个概念:时间单位和时区。
首先是时间单位,通过 维基百科 可以发现时间单位有很多,在 Java 中是通过枚举 ChronoUnit 来表示时间单位的,这里列出其中常用的一部分:
时间单位 | 大小 | java.time.temporal.ChronoUnit |
---|---|---|
纳秒 | 10-9 s | ChronoUnit.NANOS |
毫秒 | 10-3 s | ChronoUnit.MILLIS |
秒 | 1 s | ChronoUnit.SECONDS |
分 | 60 s | ChronoUnit.MINUTES |
时 | 3600 s | ChronoUnit.HOURS |
日 | 24 时 | ChronoUnit.DAYS |
周 | 7 日 | ChronoUnit.WEEKS |
月 | 28-31 日 | ChronoUnit.MONTHS |
年 | 12 月 | ChronoUnit.YEARS |
然后是时区这个有些复杂的概念,时区的理解需要区分 本地时间 和 UTC 偏移量.
比如说时间 2019-04-19T14:31:09.764+08:00[Asia/Shanghai]
, 这个时间表示 Asia/Shanghai
时区的 本地时间 为 2019-04-19T14:31:09.764
, 而它的 UTC 偏移量 为 +08:00
, 即:时区 Asia/Shanghai 的的本地时间比世界标准时间(本初子午线处的本地时间)快 8 个小时。
在计算两个时区之间的时间差时,不能直接用本地时间进行计算,而应该综合本地时间和 UTC 偏移量。
最直接的方式便是将两个时区的时间都转换为 UTC 世界标准时间然后在进行计算。
时间点
对时间的处理可以粗略的划分为三个部分:时间点、时间段和时间的解析与格式化。
在 Java8 中表示时间点的对象都实现了接口 java.time.temporal,其中,常用的时间点对象有:Instant、LocalDate、LocalTime 和 LocalDateTime。
这几个时间点对象中比较特殊的是 Instant
对象,这个对象表示的时间是世界标准时间,而不是本地时间。
尝试执行下面这一行代码你就会发现,它的输出结果和你电脑上显示的时间是不一样的,如果是在国内,那么它的结果会比你电脑上的时间慢 8 个小时:
System.out.println(Instant.now());
这些时间点对象都提供了可以根据当前时间创建相应时间点对象的静态工厂方法 now
, 除了 Instant
以外也提供了静态工厂方法 of
允许你指定参数创建时间点对象:
LocalDate date = LocalDate.of(2019, 4, 19); // 2019-04-15
LocalTime time = LocalTime.of(15, 0, 0); // 15:00:00
LocalDateTime dt = LocalDateTime.of(2019, 4, 19, 15, 0, 0); // 2019-04-15 15:00:00
和旧的接口不一样的一点是,这些对象都是 不可变 对象,这意味着修改这些对象的属性时会返回创建的新对象。
修改这些对象的方法有很多,通用的便是在 Temporal
接口中定义的一些方法:
minus(long amountToSubtract, TemporalUnit unit); // - amountToSubtract * unit
minus(TemporalAmount amount); // - amount
plus(long amountToAdd, TemporalUnit unit); // + amountToSubtract * unit
plus(TemporalAmount amount); // + amount
with(TemporalAdjuster adjuster);
with(TemporalField field, long newValue);
当然了,这些对象也提供了更多的方便使用的接口,可以很方便的在相应的时间单位上进行修改。
这里比较特殊的是通过 TemporalAdjuster
对时间进行调整,它的伴随类 TemporalAdjusters
提供了一些现成的工厂方法,方便我们使用:
// 将日期调整到该月的最后一天
LocalDate.now().with(TemporalAdjusters.lastDayOfMonth());
时间段
时间段对应的接口是 java.time.temporal,这个接口的实现只有两个:Duration 和 Period。
Duration 可以通过两个 LocalDateTime、LocalTime 和 Instant 对象创建:
Duration.between(time1, time2);
而 Period 对象可以通过两个 LocalDate 对象进行创建:
Period.between(date1, date2);
除了通过两个时间点对象以外,Duration 和 Period 对象也提供了一些静态工厂方法创建实例:
Duration.of(3, ChronoUnit.MILLIS); // 3 Millis
Period.of(0, 0, 1); // 1 day
Duration 和 Period 对象都提供了相应的 get 方法用于获取它们支持的时间单位的长度(好像是病句 QAQ):
Duration.ofDays(1).getSeconds(); // 86400
Period.of(1, 2, 3).getDays(); // 3
时间的解析和格式化
时间的解析和格式化相关的对象是 DateTimeFormatter
, 这个类提供了很多默认的格式器,也可以自己创建格式器。
时间点对象都可以通过静态方法 parse
解析字符串创建实例,除了 Instant 对象以外还支持提供一个 DateTimeFormatter 参数解析特定格式的时间字符串:
Instant.parse('2007-12-03T10:15:30.00Z')
LocalDateTime.parse('2017-12-3T09:32:00', DateTimeFormatter.ISO_LOCAL_DATE_TIME)
虽然 DateTimeFormatter
提供了很多的默认格式器,但是这些格式器有时并不能满足我们的需求,这时可以通过自定义格式器完成相关的时间解析工作:
LocalDateTime.parse('17-12-03 09:32:00', DateTimeFormatter.ofPattern("yy-MM-dd HH:mm:ss"))
下面是常用的日期/时间格式的格式化符号:
时间与或目的 | 示例 |
---|---|
YEAR | yy: 69, yyyy: 1969 |
MONTH | M: 7, MM: 07, MMM: Jul, MMMM: July, MMMMM: J |
DAY | d: 6, dd: 06 |
WEEK | e: 3, E: Wed, EEE: Wednesday, EEEE: W |
HOUR | H: 9, HH: 09 |
MINUTE | mm: 02 |
SECOND | ss: 00 |
时间字符串反过来便是时间的格式化了,我们可以通过格式器来格式化时间:
DateTimeFormatter.ofPattern("yy-MM-dd HH:mm:ss").format(LocalDateTime.now()); // 19-04-19 20:53:49
DateTimeFormatter.BASIC_ISO_DATE.format(LocalDateTime.now()); // 20190419
时区时间
时区时间同样也是时间点对象,实现了 Temporal
接口,相较于 LocalDateTime
, 时区时间多了 时区信息:
ZonedDateTime: 2019-04-20T10:14:44.396+08:00[Asia/Shanghai]
LocalDateTime: 2019-04-20T10:14:44.396
LocalDate: 2019-04-20
LocalTime: 14:44.396
时区信息可以通过 ZoneId
标识,LocalDateTime 对象可以通过 ZoneId
对象添加时区信息(不会修改本地时间的值):
LocalDateTime.now().atZone(ZoneId.of('Europe/London')); // 2019-04-20T10:19:30.461+01:00[Europe/London]
如果不想直接用时区 ID,那么可以选择使用 UTC 时间偏差:
LocalDateTime.now().atOffset(ZoneOffset.of("+01:00")); // 2019-04-20T10:25:14.496+01:00
而且使用 ZoneOffset
可以转换 LocalDateTime 和 Instant:
LocalDateTime.now().toInstant(ZoneOffset.of("+01:00")); // 2019-04-20T09:34:59.110Z
LocalDateTime.ofInstant(Instant.now(), ZoneOffset.of("+01:00")); // 2019-04-20T03:35:51.980
兼容旧接口
Java8 提供了一些方法允许新的日期时间后旧的日期时间进行转换,下面列举出了一部分:
类 | 转换到旧的对象 | 转换到新的对象 |
---|---|---|
Instant - java.util.Date | Date.from(instant) | date.toInstant() |
LocalDate - java.sql.Date | Date.valueOf(localDate) | date.toLocalDate() |
LocalTime - java.sql.Time | Time.valueOf(localTime) | date.toLocalTime() |
ZoneId - java.util.TimeZone | TimeZone.getTimeZone(id) | timeZone.toZoneId() |
可以说,Java 8 不仅提供了新的接口,还提供了能够很方便的兼容旧的接口的方式,真的很棒!
结语
这篇博客的内容不怎么详细,基本上只是简单的提了一下各个接口可以做什么,更多的使用还是需要翻文档。
但是不得不说,Java8 的日期和时间接口提供了很多方便我们使用的功能,而且接口的设计也很 Beautiful!
Nice!!!
……
这篇博客的编写主要参考了两本书:《Java8 实战》和《Java 核心技术卷卷二》。
然后就不得不吐槽《Java8 实战》这本书了,关于时间处理的这一章节勘误一大堆,内容也不是很清楚。
而《Java 核心技术卷卷二》就写的很清楚,不知道是不是翻译的锅……
Java8 时间处理的更多相关文章
- java8 时间使用
为什么需要新的时间API 文章来源:https://www.cnblogs.com/guozp/p/10342775.html 在Java 8之前的日期/时间API之前,现有的与日期和时间相关的类存在 ...
- Java8获取当前时间、新的时间日期类如Java8的LocalDate与Date相互转换、ZonedDateTime等常用操作包含多个使用示例、Java8时区ZoneId的使用方法、Java8时间字符串解析成类
下面将依次介绍 Date转Java8时间类操作 ,Java8时间类LocalDate常用操作(如获得当前日期,两个日期相差多少天,下个星期的日期,下个月第一天等) 解析不同时间字符串成对应的Java ...
- Java8 时间日期类操作
Java8 时间日期类操作 Java8的时间类有两个重要的特性 线程安全 不可变类,返回的都是新的对象 显然,该特性解决了原来java.util.Date类与SimpleDateFormat线程不安全 ...
- 迄今为止最硬核的「Java8时间系统」设计原理与使用方法
为了使本篇文章更容易让读者读懂,我特意写了上一篇<任何人都需要知道的「世界时间系统」构成原理,尤其开发人员>的科普文章.本文才是重点,绝对要读,走起! Java平台时间系统的设计方案 几乎 ...
- java8时间类API安全问题(赠送新的时间工具类哟)
LocalDateTime等新出的日期类全是final修饰的类,不能被继承,且对应的日期变量都是final修饰的,也就是不可变类.赋值一次后就不可变,不存在多线程数据问题. simpleDateFor ...
- Java8 时间 API
前言 Java8 中最为人津津乐道的新改变恐怕当属函数式 API 的加入.但实际上,Java8 所加入的新功能远不止这个. 本文将基于<Java SE8 for the Really Impat ...
- Java8时间的简单时间
package com.java8.date; import org.junit.Test; import java.text.SimpleDateFormat; import java.time.* ...
- java8时间类
java8引入了一套全新的时间日期API 新的时间及日期API位于java.time中java.time包中的是类是不可变且线程安全的. 下面是一些关键类 LocalDateTime // ...
- java8时间使用小结
//LocalDate代表一个IOS格式(yyyy-MM-dd)的日期 获取当前的日期: LocalDate localDate = LocalDate.now();//LocalDate: 表示没有 ...
随机推荐
- maven项目右键快捷方式,然后点击Run As
在某一个maven项目右键快捷方式,然后点击Run As就可以发现几个Maven的命令: 1.Maven Build: 这个命令用于编译Maven工程,执行命令后会在target文件夹中的classe ...
- 2018.7.22 Jdom与dom的区别
SAX 优点:①无需将整个文档加载到内存,因而内存消耗少 ②推模型允许注册多个ContentHandler 缺点:①没有内置的文档导航支持 ②不能够随机访问XML文档 ③不支持在原地修改XML ④不支 ...
- 2017.11.9 如何利用JS做登陆验证界面
()案例----JavaScript实现输入验证 需要验证的表单输入域和要求 用户名不能为空,是否符合规定的格式 密码长度是否超过6,两次密码输入一致 邮箱地址:邮箱地址必须符合邮箱形式 ~~~注意提 ...
- java基础必备单词讲解 day three
if 如果 else 否则 switch 切换判断 case 实例 break 退出 return 返回 default 默认 variable array 数组 null 空的 无效的 pointe ...
- Vue 前端md5加密
用户注册时将加密后的密码发送给后端存储 当登陆的时候,再将加密后的密码和数据库中加密的密码相匹配. npm: https://www.npmjs.com/package/crypto-browseri ...
- Servlet学习笔记01——什么是servlet?
1.什么是Servlet? sun公司制订的一种用来扩展web服务器功能的组件规范. (1)扩展web服务器功能 早期的web服务器(apache web server,iis) 只能处理静态资源的请 ...
- filebeat的安装及配置
概述:Filebeat是一个日志文件托运工具,在你的服务器上安装客户端后,filebeat会监控日志目录或者指定的日志文件,追踪读取这些文件(追踪文件的变化,不停的读),并且转发这些信息到elasti ...
- FreeBSD--常用命令
FreeBSD常用命令 查看网络流量 a.systat -if 1 (1表示1s刷新屏幕一次) b.netstat 1 # Traffic 流量 peak 峰值 average 平均值 查看进程p ...
- 爬虫之urllib模块
1. urllib模块介绍 python自带的一个基于爬虫的模块. 作用:可以使用代码模拟浏览器发起请求. 经常使用到的子模块:request,parse. 使用流程: 指定URL. 针对指定的URL ...
- C语言字符篇(五)内存函数
memcpy不可以把目的地址写成本身 但是memmove可以,因为它是先保存到临时空间 #include <string.h> void *memcpy(void *dest, con ...