伴随lambda表达式、streams以及一系列小优化,Java 8 推出了全新的日期时间API,在教程中我们将通过一些简单的实例来学习如何使用新API。Java处理日期、日历和时间的方式一直为社区所诟病,将 java.util.Date设定为可变类型,以及SimpleDateFormat的非线程安全使其应用非常受限。Java也意识到需要一个更好的 API来满足社区中已经习惯了使用JodaTime API的人们。全新API的众多好处之一就是,明确了日期时间概念,例如:瞬时(instant)、 长短(duration)、日期、时间、时区和周期。同时继承了Joda库按人类语言和计算机各自解析的时间处理方式。不同于老版本,新API基于ISO标准日历系统,java.time包下的所有类都是不可变类型而且线程安全。下面是新版API中java.time包里的一些关键类:
新API还引入了ZoneOffSet和ZoneId类,使得解决时区问题更为简便。解析、格式化时间的DateTimeFormatter类也全部重新设 计。
java.time包定义的类表示了日期-时间概念的规则,包括instants, durations, dates, times, time-zones and periods。这些都是基于ISO日历系统,它又是遵循 Gregorian规则的。
还有一些方法前缀的含义,统一了api:
来源:
@FunctionalInterface
public interface TemporalQuery<R>
{
R queryFrom(TemporalAccessor temporal)
}
此枚举类是作为get方法的参数获取时间的值
它里面的属性含义有的跟Calendar的成员变量含义差不多,想要了解一下可以看我的关于Calendar类详解的文章 ,如果要看很详细的讲解可以去查看ChronoField类的文档
时间单位
public interface TemporalUnit
{
//返回add amount个 unit之后的值
<R extends Temporal> R addTo(R temporal, long amount)
//计算2个Temporal的时间差。
long between(Temporal temporal1Inclusive, Temporal temporal2Exclusive)
///返回unit的持续时间
Duration getDuration()
//检查此unit是否表示日期的组成部分
boolean isDateBased()
//检查unit的持续时间是否为估计值。
boolean isDurationEstimated()
//检查此unit是否被支持
default boolean isSupportedBy(Temporal temporal)
//检查此unit是否表示时间的组成部分
boolean isTimeBased()
//获取unit的显示名称
String toString()
}
时间数量接口
实现类
Duration由固定秒数定义,精确到纳秒。例如:34.5 seconds ,0.0003 seconds,可由day,hour,minute互相转换,因为是固定的。
Period为一段时间,包括多个时间域,例如:2 years, 3 months and 4 days
public interface TemporalAmount
{
//add to Temporal
Temporal addTo(Temporal temporal)
//get unit value
long get(TemporalUnit unit)
//获取支持的Unit列表
List<TemporalUnit> getUnits()
//sub from Temporal
Temporal subtractFrom(Temporal temporal)
}
public interface TemporalAccessor
{
//获取指定时间域的int值
default int get(TemporalField field);
//获取指定时间域的long值
long getLong(TemporalField field);
Gets the value of the specified field as a long.
//是否支持指定时间域
boolean isSupported(TemporalField field);
//返回一个时间对象,通过query,query是个函数接口。
default <R> R query(TemporalQuery<R> query);
//返回指定时间域的有效范围
default ValueRange range(TemporalField field);
}
public interface Temporal extends TemporalAccessor
{
//是否支持Unit
boolean isSupported(TemporalUnit unit)
//减时间单位
default Temporal minus(long amountToSubtract, TemporalUnit unit)
//减相对时间
default Temporal minus(TemporalAmount amount)
Returns an object of the same type as this object with an amount subtracted.
//加时间单位
Temporal plus(long amountToAdd, TemporalUnit unit)
//加相对时间
default Temporal plus(TemporalAmount amount)
//计算时间差
long until(Temporal endExclusive, TemporalUnit unit)
//时间调整
default Temporal with(TemporalAdjuster adjuster)
//时间调整
Temporal with(TemporalField field, long newValue)
}
时间调整器接口,函数接口。
此类配合java.time基础包中类的with方法:
@FunctionalInterface
public interface TemporalAdjuster
{
//调整时间
Temporal adjustInto(Temporal temporal)
}
// 以下2种方式等价,推荐第2种。 temporal = thisAdjuster.adjustInto(temporal); temporal = temporal.with(thisAdjuster);
Month是个枚举,表示一年的12个月
Month有个int属性,表示每个月份对应的数字。不要使用ordinal()获取月份对应数字,而是使用getValue()方法。
枚举,表示每周的某天。
有个int属性,表示每个星期几对应的数字。不要使用ordinal()获取月份对应数字,而是使用getValue()方法。
数字从1到7分别代表星期一到星期日。固定不变,不支持根据local不同星期日对应不同数字。如有需要,通过WeekFields设置。
表示纪年
实现类:
Anno Hegirae,伊斯兰纪元)转换可通过下面的方法进行。
Date.toInstant()
Date.from(Instant)
Calendar.toInstant()
日期和时间格式由 日期和时间模式字符串 指定。在 日期和时间模式字符串 中,未加引号的字母 'A' 到 'Z' 和 'a' 到 'z' 被解释为模式字母,用来表示日期或时间字符串元素。文本可以使用单引号 (') 引起来,以免进行解释。所有其他字符均不解释,只是在格式化时将它们简单复制到输出字符串。
简单的讲:这些 A ——Z,a —— z 这些字母(不被单引号包围的)会被特殊处理替换为对应的日期时间,其他的字符串还是原样输出。
日期和时间模式(注意大小写,代表的含义是不同的)如下:
示例1:
DateTimeFormatter f = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime t = LocalDateTime.parse("2019-11-12 00:00:00",f);
Instant i = t.toInstant(ZoneOffset.UTC);
Long s = i.getEpochSecond();
输出:
t:2019-11-12T00:00
i:2019-11-12T00:00:00Z
s:1573516800(北京时间:2019-11-12 08:00:00)
示例2:
DateTimeFormatter f = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime t = LocalDateTime.parse("2019-11-12 00:00:00",f);
Instant i = t.toInstant(ZoneOffset.ofHours(8));
Long s = i.getEpochSecond();
输出:
t:2019-11-12T00:00
i:2019-11-11T16:00:00Z
s:1573488000(北京时间:2019-11-12 00:00:00)