Date如果不格式化,打印出的日期可读性差
Date date = new Date();
Wed May 13 16:06:28 CST 2020
LocalDate localDate = LocalDate.now();
2020-05-13
Java8之前所有的日期类都是可变的, 这就导致了线程不安全
Java的日期和时间类的定义不一致,在java.util和java.sql中都包含日期类
java.util.Date同时包含日期和时间,但是java.sql中只包含日期,将其纳入java.sql中是不合适的,而且最糟糕的是:着两个类中的日期类的名字都是一样的
对于时间、时间戳、格式化及解析,没有一些明确定义的类, 而且对于格式化和解析的需求,Java中有java.text.DateFormat抽象类,但是通常我们用的是SimpleDateFormat
日期不支持国际化,没有时区支持,即使Java引入java.util.Calendar和java.uril.TimeZone类,但是问题依然存在
使用SimpleDateFormat对时间进行格式化, 但SimpleDateFormat是线程不安全的:
`Date和Calendar都是可变的.能把2014年3月18日修改成4月18日意味着什么呢?这种设计会被拖入维护的噩梦
明确定义
用以完成相同的行为
,例如,想要拿到当前实例,可以用new()方法,在所有的类方法中都实现了formate()和parse()方法,不再是之前用单独一个类去解决,而且新的API中所有的类都使用了工厂模式和策略模式;在绝大数情况下,我们不应该直接使用它
,因为java.time包中相应的类已经提供了格式化和解析的方法我们使用比较频繁地可能是以下几个类:
LocalDate是不变的日期时间对象代表一个日期,往往被视为年月日。
方法 | 描述 |
---|---|
static LocalDate now() | 从默认时区的系统时钟获取当前日期 |
static LocalDate of(int year, int month, int dayofMonth) | 从一年,一个月和一天获得一个LocalDate的实例 |
int get(TemporalField field) | 从这个日期时间作为一个int获取指定字段的值,一般使用实现类ChronoField |
int getYear() | 获取年份字段 |
int getMonthValue() | 获取月份 |
int getDayofMonth() | 获取当月的第几天,一般用这个方法获取日 |
String format(DateTimeFormatter formatter) | 格式化日期,日期转String |
static LocalDate parse(CharSequence text, DateTimeFormatter formatter) | 字符串转日期 |
static LocalDateTime parse(CharSequence text) | 获得LocalDate实例从一个字符串 |
int compareTo(chronoLocalDate other) | 将此日期与另一个日期比较 |
boolean isAfter(ChronoLocalDate other) | 检查此日期是否在指定日期之后 |
boolean isBefore(ChronoLocalDate other) | 检查此日期是否在指定日期之前 |
boolean isEqual(ChronoLocalDate other) | 检查此日期是否等于指定日期 |
LocalDate plus(long amountToAdd, TemporalUnit unit) | 返回此日期的一个副本, 添加指定的TemporalUnit。 |
LocalDate minusDays(long daysToSubtract) | 返回此LocalDate的副本,并减去指定的天数(年份和月份类似) |
LocalDate plusDays(long daysToAdd) | 返回此LocalDate的副本,并增加指定的天数(年份和月份类似 |
long until(Temporal endExclusive, TemporalUnit unit) | 计算时间的量,直到指定单元的另一个日期为止 |
ZonedDateTime atStartofDay(ZoneId zone) | 根据时区中的规则,在最找的有效时间内从此日期返回划分的日期时间 |
LocalDate with(TemporalAdjuster adjuster) | 返回此日期的调整副本 |
LocalDate with(TemporalField field, long newValue) | 给字段的字段设置新值 |
LocalDate withYear(int year) | 返回此日期的副本与年更改。 |
LocalDate withMonth(int month) | 返回此日期的副本与更改的月份 |
LocalDate withDayOfMonth(int dayOfMoth) | 返回此日期的副本与月更改日期。 |
String toString() | 输出该日期时间为字符串 |
LocalDate date = LocalDate.of(2022, 1, 1);
LocalDate date2 = LocalDate.of(2022, 2, 1);
//获取日期内容信息
System.out.println(date);
System.out.println(date.getYear()); = localDate.get(ChronoField.YEAR)
System.out.println(date.getMonthValue()); = localDate.get(ChronoField.MONTH_OF_YEAR)
System.out.println(date.getDayOfMonth()); = localDate.get(ChronoField.DAY_OF_MONTH)
System.out.println(date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
LocalDate parse = LocalDate
.parse("2023-01-19", DateTimeFormatter.ofPattern("yyyy-MM-dd"));
System.out.println(parse);
//日期比较
System.out.println(date.compareTo(date2));
System.out.println(date.isAfter(date2));
System.out.println(date.isBefore(date2));
System.out.println(date.equals(date2));
//日期加运算
System.out.println(date.plusYears(1)) = date.plus(1, ChronoUnit.YEARS)
System.out.println(date.plusMonths(1)) = date.plus(1, ChronoUnit.MONTHS)
System.out.println(date.plusDays(1)) = date.plus(1, ChronoUnit.DAYS)
//日期减运算
System.out.println(date.minusYears(1));
System.out.println(date.minusMonths(1));
System.out.println(date.minusDays(1));
//日期间隔运算,ChronoUnit枚举是TemporalUnit的实现类, 还可以计算时分秒等
System.out.println(date.until(date2, ChronoUnit.YEARS));
System.out.println(date.until(date2, ChronoUnit.MONTHS));
System.out.println(date.until(date2, ChronoUnit.DAYS));
//调整年份,月,日
LocalDate date = LocalDate.of(2015, 1, 1);
LocalDate newDate = localDate.with(ChronoField.YEAR, 2018) =>2018-01-01 和date.withYear是等价的
System.out.println(date.withYear(2018)); => 2018-01-01
System.out.println(date.withMonth(5)); => 2015-05-01
System.out.println(date.withDayOfMonth(2)); => 2015-01-02
System.out.println(date.withDayOfYear(35)); => 2015-02-04
和Date之间的相互转换
public Date localDate2Date(LocalDate localDate){
ZonedDateTime zdt = localDate.atStartOfDay(ZoneId.systemDefault());
return Date.from(zdt.toInstant());
}
public LocalDate date2LocalDate(Date date) {
ZonedDateTime zdt = date.toInstant().atZone(ZoneId.systemDefault());
return zdt.toLocalDate();
}
调整日期 LocalDate with(TemporalAdjuster adjuster)
, 使用TemporalAdjusters类:
//获取这个月的第一天, 以及localDate获取第一天并转Date(其他类似)
System.out.println(localDate.with(TemporalAdjusters.firstDayOfMonth()));
Date startDate = Date.from(localDate.with(TemporalAdjusters.firstDayOfMonth())
.atStartOfDay(ZoneId.systemDefault()).toInstant());
//获取这个月最后一天
System.out.println(localDate.with(TemporalAdjusters.lastDayOfMonth()));
//获取上个月第一天获取上个月最后一天
LocalDate lastMonathLocalDate = localDate.minusMonths(1);
......
//获取今年第一天
System.out.println(localDate.with(TemporalAdjusters.firstDayOfYear()));
//获取今年最后一天
System.out.println(localDate.with(TemporalAdjusters.lastDayOfYear()));
LocalTime是不变的日期时间对象代表一个时间,往往被视为小时分钟秒,方法使用借鉴LocalDate即可
LocalDate是不变的日期时间对象代表一个日期时间,往往被视为年、月、日、时、分、秒。
其实方法跟LocalDate差不多, 只是多了一些时分秒的操作,这里就列举一些LocalDateTime的API
方法 | 描述 |
---|---|
static LocalDateTime now() | 从默认时区的系统时钟获取当前日期时间 |
of(int year, int month, int dayOfMonth, int hour, int minute, int second) | 获得LocalDateTime从年,月,日,小时,分钟和秒的实例,设置纳秒为零 |
atZone(ZoneId zone) | 结合时间与时区来创建一个ZonedDateTime |
int get(TemporalField field) | 从这个日期时间作为一个int获取指定字段的值, 一般使用实现类ChronoField |
int getYear() | 获取年度字段。 |
int getMonthValue() | 从1到12得到一个月的时间字段。 |
int getDayOfMonth() | 获取月字段的一天。 |
int getHour() | 获取小时字段 |
int getMinute() | 获取分钟字段 |
int getSecond() | 获取秒 |
boolean isAfter(ChronoLocalDateTime> other) | 检查此日期是否在指定日期之后 |
boolean isBefore(ChronoLocalDateTime> other) | 检查此日期是否在指定日期之前 |
boolean isEqual(ChronoLocalDateTime> other) | 检查此日期是否等于指定日期 |
LocalDateTime minus(long amountToSubtract, TemporalUnit unit) | 返回日期时间副本, 减少指定的单位数量 |
LocalDateTime minusHours(long Hours) | 返回此LocalDateTime的副本,以减少的小时数(年月日类似) |
LocalDateTime minusMinutes(long minutes) | 返回此LocalDateTime的副本,以减少的分钟数 |
LocalDateTime minusSeconds(long seconds) | 返回此LocalDateTime的副本,以减少的秒数 |
LocalDateTime plus(long amountToAdd, TemporalUnit unit) | 返回日期时间副本, 增加指定的单位数量 |
LocalDateTime plusHours(long Hours) | 返回此LocalDateTime的副本,以增加的小时(年月日等类似) |
LocalDateTime until(Temporal endExclusive, TemporalUnit unit) | 计算时间的量,直到指定单元的另一个日期时间 |
LocalDateTime with(TemporalField field, long newValue) | 将此日期时间的副本与指定的字段设置为一个新值,一般使用实现类ChronoField |
LocalDateTime with(TemporaAdjuster adjuster) | 返回此日期时间的调整副本 |
LocalDate toLocalDate() | 获取这个日期时间LocalDate的一部分 |
LocalTime toLocalTIme() | 获取这个日期时间LocalTime的一部分 |
//测试
LocalDateTime date = LocalDateTime.of(2010, 5, 5, 5, 5, 5);
LocalDateTime date2 = LocalDateTime.of(2010, 5, 5, 5, 5, 5);
System.out.println(date.getYear()); = date.get(ChronoField.YEAR)
System.out.println(date.getMonthValue());
System.out.println(date.getDayOfMonth());
System.out.println(date.getHour());
System.out.println(date.getMinute());
System.out.println(date.getSecond());
System.out.println(date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
LocalDate parse = LocalDate
.parse("2023-01-19", DateTimeFormatter.ofPattern("yyyy-MM-dd"));
System.out.println(parse);
//日期比较
System.out.println(date.compareTo(date2));
System.out.println(date.isAfter(date2));
System.out.println(date.isBefore(date2));
System.out.println(date.equals(date2));
//加运算
System.out.println(date.plusYears(1));
System.out.println(date.plusMonths(1));
System.out.println(date.plusDays(1));
System.out.println(date.plusHours(1));
System.out.println(date.plusMinutes(1));
System.out.println(date.plusSeconds(date.getSecond()));
//减运算
System.out.println(date.minusYears(1));
System.out.println(date.minusMonths(1));
System.out.println(date.minusDays(1));
System.out.println(date.minusHours(1));
System.out.println(date.minusMinutes(1));
System.out.println(date.minusSeconds(1));
//转换LocalDate,LocalTime
System.out.println(date.toLocalDate());
System.out.println(date.toLocalTime());
//日期间隔运算,ChronoUnit枚举是TemporalUnit的实现类
System.out.println(date.until(date2, ChronoUnit.YEARS));
System.out.println(date.until(date2, ChronoUnit.MONTHS));
System.out.println(date.until(date2, ChronoUnit.DAYS));
System.out.println(date.until(date2, ChronoUnit.HOURS));
System.out.println(date.until(date2, ChronoUnit.MINUTES));
System.out.println(date.until(date2, ChronoUnit.SECONDS));
//日期调整
date.with(ChronoField.YEAR, 2020)
.....
和Date之间相互转换
public Date localDateTime2Date(LocalDateTime localDateTime){
ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault());
return Date.from(zdt.toInstant());
}
public LocalDateTime date2LocalDateTime(Date date) {
ZonedDateTime zdt = date.toInstant().atZone(ZoneId.systemDefault());
return zdt.toLocalDateTime();
}
调整日期,参考LocalDate
方法 | 描述 |
---|---|
static Instant now() | 从系统时钟中获得当前时刻。 |
ZonedDateTime atZone(ZoneId zone) | 结合即时时区创建 ZonedDateTime 。 |
long toEpochMilli() | 把这瞬间的毫秒数从1970-01-01t00:00:00z时代。 |
long getEpochSecond() | 从1970-01-01t00:00:00z java时代得到的秒数。 |
long until(Temporal endExclusive, TemporalUnit unit) | 计算时间的量,直到指定单元的另一个时刻为止。 |
计算机存储的当前时间,本质上是一个不断递增的整数。Java提供的System.currentTimeMillis()
返回的就是以毫秒表示的当前时间戳。这个时间戳在java.time中以Instant类型表示,我们用Instant.now()
获取当前时间戳,效果System.currentTimeMillis()
类似:
Instant now = Instant.now();
System.out.println(System.currentTimeMillis()); => 1617873014216
System.out.println(now.getEpochSecond());//秒 => 1617873014
System.out.println(now.toEpochMilli());//毫秒 => 1617873014216
实际上,Instant内部只有2个核心字段:
public final class Instant implements ... {
private final long seconds;
private final int nanos;
}
一个是以秒为单位的时间戳,一个是更精确的纳秒精度。它和System.currentTimeMillis()
返回的long
相比,只是多了更高精度的纳秒。既然Instant
就是时间戳,那么,给它附加上一个时区,就可以创建出ZonedDateTime
:
ZonedDateTime zonedDateTime = Instant.now().atZone(ZoneId.systemDefault());
System.out.println(zonedDateTime);
2020-04-08T17:34:00.825+08:00[Asia/Shanghai]
可见,对于某一个时间戳或者时间,给它关联上指定的ZoneId,就得到了ZonedDateTime
, 继而可以获得对应的LocalDateTime
。ZonedDateTime
可以作为一个转换的媒介
Period类用来度量两个日期之间年、月、日的间隔值
注意: 如果是指定某年,某月,某日相差总数,请使用LocalDate的until(Temporal endExclusive, TemporalUnit unit)方法
方法 | 描述 |
---|---|
static Period of(int years, int months, int days) | 获得Period代表数年、月、日 |
static Period between(LocalDate start, LocalDate end) | 获得由年数Period月,和两个日期之间的天数 |
int getYears() | 获取此期间的年数 |
int getMonths() | 获取此期间的月份 |
int getDays() | 获取此期间的天数 |
long toTotalMonths() | 获取此期间的总月数 |
boolean isNegative() | 检查这一时期的三个单位是否都是负的 |
Period minus(TemporalAmount temp) | 返回此期间的一个副本,与指定的周期减去 |
Period minusYears(long yearsToSubtract) | 返回此期间的一个副本,用指定的年份减去(月份和日类似) |
Period plusYears(long yearsToAdd) | 返回此期间的一个副本,用指定的年份加上(月份和日类似) |
Period withYears(int years) | 返回此期间的一个副本,指定年数 |
Perod withMonths(int months) | 返回此期间的一个副本,指定月份 |
Period withDays(int days) | 返回此期间的一个副本,指定日数 |
LocalDate startDate = LocalDate.of(2019, 2, 2);
LocalDate endDate = LocalDate.of(2020, 3, 28);
//日期间隔内容获取
Period period = Period.between(startDate, endDate);
System.out.println(period.getYears()); => 1
System.out.println(period.getMonths()); => 1
System.out.println(period.getDays()); => 26
System.out.println(period.toTotalMonths()); =>13
System.out.println(period.isNegative()); => false: endDate>startDate, true: endDate<startDate
//添加减少操作
Period period1 = period.minus(Period.of(1, 2, 5)); => 批量扣减: 0, -1, 21
System.out.println(period.plusYears(1));
System.out.println(period.plusMonths(1));
System.out.println(period.plusDays(1).getDays());
System.out.println(period.minusYears(1));
........
//指定操作
System.out.println(period.withYears(3).getYears()); => 3
........
Duration类用来度量秒或纳秒时间间隔,适合处理较短的时间,需要更高的精确性
注意: 如果是指定某年,某月,某日相差总数,请使用LocalDate的until(Temporal endExclusive, TemporalUnit unit)方法
方法 | 描述 |
---|---|
static Duration of(long amount, TemporalUnit unit) | 获取Duratiion代表指定的单位数量,一般使用实现类ChronoUnit指定单位 |
static Duration between(Temporal start, Temporal end) | 获得Duration表示两个时间对象之间的时间 => 和LocalDate不同,参数是接口,除了LocalDateTime,还可以指定时间戳Instant等… |
int get(TemporalUnit unit) | 获取所请求单元的值,一般使用实现类ChronoUnit指定单位 |
long toDays() | 获取此期间的天数 |
long toHours() | 获取此期间的几个小时数 |
long toMinutes() | 获取此期间的分钟数 |
long getSeconds | 获取此期间的秒数 |
long toMillis() | 将此持续时间转换为毫秒内的总长度 |
long getNano | 得到的纳秒数 |
plus(Duration duration) | 返回此持续时间的副本,添加指定的持续时间。 |
plus(long amountToAdd, TemporalUnit unit) | 返回此持续时间的副本,添加指定的持续时间。 |
minus(Duration duration) | 返回此持续时间的副本,用指定的持续时间减去。 |
minus(long amountToAdd, TemporalUnit unit) | 返回此期间的一个副本,用指定的持续时间减去。 |
LocalDateTime localDateTime = LocalDateTime.of(2019, 2, 2, 10, 10, 10);
LocalDateTime localDateTime2 = LocalDateTime.of(2020, 3, 3, 20, 20, 20);
Duration between = Duration.between(localDateTime, localDateTime2);
//获取间隔时间
System.out.println(between.toDays());
System.out.println(between.toHours());
System.out.println(between.toMinutes());
System.out.println(between.getSeconds());
System.out.println(between.toMillis());
System.out.println(between.getNano());
//增加或者减少等操作
between.plus(1, ChronoUnit.Days)
...........
between.minus(1, ChronoUnit.Days)
.......
//结合Instant
Instant start = Instant.now();
try {
//代码..........
Thread.currentThread().sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Instant end = Instant.now();
Duration between = Duration.between(start, end);
//秒
System.out.println(between.getSeconds());
//毫秒
System.out.println(between.toMillis());
异常处理:
//间隔增加年抛出异常
betwueen.plus(1, ChronoUnit.YEARS)
java.time.temporal.UnsupportedTemporalTypeException: Unit must not have an estimated duration
进去了Duration的plus方法,这行出现了问题, 再进入到该方法中,发现有进行顺序对比
原来是isDurationEstimated方法ordinal< Days的ordinal才会通过,查看Enum类的ordinal属性, 就是枚举类中的排列顺序,从0开始
ChronoUnit中,我们指定在Days之前的枚举就行了
日期时间对象格式化和解析
方法 | 描述 |
---|---|
static DateTimeFormatter ofPattern(String pattern) | 创建一个格式化程序使用指定的模式 |
static DateTimeFormatter ofPattern(String pattern,Locale locale) | 获得由年数Period月,和两个日期之间的天数 |
String format(TemporalAccessor temporal) | 使用格式化程序格式的日期时间对象 |
TemporalAccessor parse(CahrSequence text) | 充分分析文本产生的时空对象 |
TemporalAccessor parse(CahrSequence text, ParsePostion position) | 分析文本使用此格式化程序,提供对文本位置的控制 |
Strinig format(DateTimeFormatter formatter) | 使用指定的格式的日期格式(LocalDate, LocalDateTime) |
static LocalDate parse(CharSequence text, DateTimeFormatter formatter) | 获得LocalDate实例从使用特定格式的字符串 |
static LocalDateTime parse(CharSequence text, DateTimeFormatter formatter) | 获得LocalDateTime实例从使用特定格式的字符串 |
除了方法,DateTimeFormatter还初始化了很多静态字段对象, 比如ISO_LOCAL_DATE(yyyy-mm-dd)、BASIC_ISO_DATE等
//LocalDate
LocalDate localDate = LocalDate.of(2014, 2, 2);
String format = localDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
LocalDate parse = LocalDate.parse("2018-06-06", DateTimeFormatter.ofPattern("yyyy-MM-dd"));
//LocalDateTime
LocalDateTime localDateTime = LocalDateTime.of(localDate, LocalTime.of(10, 10, 10));
String format1 = localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
LocalDateTime parse1 = localDateTime.parse("2018-06-06 10:10:10", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
之前看到的日期和时间的种类都不包含时区信息,时区的处理是新版日期和时间API新增加的重要功能,使用新版日期和时间API被极大的简化了。新的java.time.ZoneId类是老板java.util.TimeZone的替代品。
时区是按照一定的规则将区域划分成的标准时间相同的区间。在ZoneRules这个类中包含了40个这样的实例。你可以简单地通过调用ZoneId的getRules()得到指定时区的规则。每个特定ZoneId对象都由一个地区ID标识,地区ID都为”{区域}/{城市}“的格式,比如:
ZoneId romeZone = ZoneId.of("Europe/Rome"); java
一旦得到一个ZoneId对象, 你就可以把LocalDate, LocalDateTime或者是Instant对象整合起来,构造为一个ZonedDateTime实例,它代表了相对于指定时区的时间点:
LocalDate date = LocalDate.of(2014, Month.MARCH, 18);
ZonedDateTime zdt1 = date.atStartOfDay(romeZone);
LocalDateTime dateTime = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45);
ZonedDateTime zdt2 = dateTime.atZone(romeZone);
Instant instant = Instant.now();
ZonedDateTime zdt3 = instant.atZone(romeZone);
ZonedDateTIme的组成部分
:
通过ZoneId->下面的romeZone,你还可以将LocalDateTime转换为Instant:
LocalDateTime dateTime = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45);
Instant instantFromDateTime = dateTime.toInstant(romeZone);
你也可以通过反向的方式得到LocalDateTime对象:
Instant instant = Instant.now();
LocalDateTime timeFromInstant = LocalDateTime.ofInstant(instant, romeZone);
参考:
https://www.liaoxuefeng.com/wiki/1252599548343744/1298613246361634
https://docs.oracle.com/javase/8/docs/api/index.html