若想了解java8其它新特性,请到 00-java8常用新特性文章索引 阅读。
Java8 在 java.time 包中增加了时间日期相关的API,弥补了 Java8 以前对日期、时间处理的不足。
在介绍Java8新的时间日期API前,先看看 java8 以前我们操作日期时间常用到的 java.util.Date
、java.util.Calendar
、java.text.SimpleDateFormat
。
1 java.util.Date
1.1 java.util.Date常用API介绍
java.util.Date从JDK1.0开始就已经存在了,在开发中经常会接触到,进入 java.util.Date 的源码,我们可以看到好多方法都已经过时了。
下面我们介绍一下Date中未过时的方法。
- Date() // Date 空参构造器,调用的构造器 Date(long date),入参是 System.currentTimeMillis()
- Date(long date) // 带时间戳入参的构造器,初始化一个 Date对象,入参 date 表示自1970年1月1日00:00:00 GMT(格林尼治标准时间)以来的毫秒数
- long getTime() // 返回 1970年1月1日00:00:00 GMT 到当前 Date 的毫秒数
- void setTime(long time) //设置Date的时间,入参 time 表示自1970年1月1日00:00:00 GMT(格林尼治标准时间)以来的毫秒数
- boolean before(Date when) // 测试此日期是否早于入参指定的日期。
- boolean after(Date when) // 测试此日期是否晚于入参指定的日期。
- int compareTo(Date anotherDate) // 当前日期与入参指定日期进行比较,当前日期小于入参日期,返回 -1;当前日期等于入参日期,返回0;当前日期大于入参日期,返回 1;
- static Date from(Instant instant) // 将一个 Instant 实例转为 Date 实例
- Instant toInstant() // 将一个 Date 实例转为 Instant 实例
Date源码
public class Date implements Serializable, Cloneable, Comparable<Date> {
// Date 空参构造器,调用的构造器 Date(long date),入参是 System.currentTimeMillis()
public Date() {
this(System.currentTimeMillis());
}
// 通过时间戳,初始化一个 Date对象,入参 date 表示自1970年1月1日00:00:00 GMT(格林尼治标准时间)以来的毫秒数
public Date(long date) {
fastTime = date;
}
// 返回 1970年1月1日00:00:00 GMT 到当前 Date 的毫秒数
public long getTime() {
return getTimeImpl();
}
//设置Date的时间,入参 time 表示自1970年1月1日00:00:00 GMT(格林尼治标准时间)以来的毫秒数
public void setTime(long time) {
fastTime = time;
cdate = null;
}
// 测试此日期是否早于入参指定的日期。
public boolean before(Date when) {
return getMillisOf(this) < getMillisOf(when);
}
// 测试此日期是否晚于入参指定的日期。
public boolean after(Date when) {
return getMillisOf(this) > getMillisOf(when);
}
// 当前日期与入参指定日期进行比较,
// 当前日期小于入参日期,返回 -1;当前日期等于入参日期,返回0;当前日期大于入参日期,返回 1;
public int compareTo(Date anotherDate) {
long thisTime = getMillisOf(this);
long anotherTime = getMillisOf(anotherDate);
return (thisTime<anotherTime ? -1 : (thisTime==anotherTime ? 0 : 1));
}
// 将一个 Instant 实例转为 Date 实例
public static Date from(Instant instant) {
try {
return new Date(instant.toEpochMilli());
} catch (ArithmeticException ex) {
throw new IllegalArgumentException(ex);
}
}
// 将一个 Date 实例转为 Instant 实例
public Instant toInstant() {
return Instant.ofEpochMilli(getTime());
}
}
1.2 java.util.Date简单使用
public class DateCalendarTest {
/**
* 测试 java.util.Date
*/
@Test
public void testDateAPI() {
// 1、Date() Date 空参构造器,调用的构造器 Date(long date),入参是 System.currentTimeMillis()
Date date1 = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
// 用时间戳入参的构造器
// 2、Date(long date) 入参 date 表示自1970年1月1日00:00:00 GMT(格林尼治标准时间)以来的毫秒数
Date date2 = new Date(0);
System.out.println("空参构造器:" + dateFormat.format(date1));
System.out.println("时间戳入参构造器:" + dateFormat.format(date2));
// 3、getTime() // 返回 1970年1月1日00:00:00 GMT 到当前 Date 的毫秒数
long time = date1.getTime();
System.out.println("date1的毫秒数:" + time);
// setTime(long time) 入参 time 表示自1970年1月1日00:00:00 GMT(格林尼治标准时间)以来的毫秒数
date2.setTime(System.currentTimeMillis());
System.out.println("date2设置时间后的毫秒数:" + date2.getTime());
// before(Date when) 测试此日期是否早于入参指定的日期。
boolean before = date1.before(date2);
System.out.println("data1 before date2 ? " + before);
// after(Date when) 测试此日期是否晚于入参指定的日期。
boolean after = date1.after(date2);
System.out.println("data1 after date2 ? " + after);
Instant instant = date1.toInstant();
System.out.println("date1.toInstant(): " + instant);
Date from = Date.from(instant);
System.out.println("Date.from(instant): " + from.getTime());
}
}
运行结果:
空参构造器:2020-09-03
时间戳入参构造器:1970-01-01
date1的毫秒数:1599102000943
date2设置时间后的毫秒数:1599102000949
data1 before date2 ? true
data1 after date2 ? false
date1.toInstant(): 2020-09-03T03:00:00.943Z
Date.from(instant): 1599102000943
2 java.util.Calendar
Calendar提供了操作日历相关的API。month是从0开始的,1-12月份的值是 0-11。
2.1 java.util.Calendar 常用API介绍
- Calendar getInstance() // 使用默认时区和区域设置获取日历
- void add(int field,int amount) //根据日历的规则,为给定的日历字段添加或减去指定的时间量。
- Date getTime() // 返回表示当前日历时间的 Date
- void setTime(Date date) // 通过给定的 date 设置 Calendar 的时间
- long getTimeInMillis() // 返回 Calendar 时间对应的毫秒数
- void setTimeInMillis(long millis) // 用给定的毫秒数设置日历的当前时间
- int get(int field) // 返回指定日历字段的值。
- void set(int field,int value) // 将给定的日历字段设置为给定值。
- void set(int year,int month,int date) // 设置Calendar对象的年、月、日3个字段的值。
- void set(int year,int month,int date,int hour,int minute,int second) // 设置Calendar对象的年、月、日、时、分、秒6个字段的值。
2.2 java.util.Calendar简单使用
/**
* 测试 java.util.Calendar
*/
@Test
public void testCalendarAPI() {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
Calendar cal = Calendar.getInstance();
System.out.println("当前日历时间:" + simpleDateFormat.format(cal.getTime()));
//年
System.out.println("年:" + cal.get(Calendar.YEAR));
//月:month从0开始
System.out.println("月(month从0开始):" + cal.get(Calendar.MONTH));
//日
System.out.println("日:" + cal.get(Calendar.DATE));
//设置年、月、日、时、分、秒
cal.set(2020, 10, 1, 8, 0, 0);
System.out.println("设置后的时间:" + simpleDateFormat.format(cal.getTime()));
//年份+1
cal.add(Calendar.YEAR, 1);
System.out.println("将年份+1:" + simpleDateFormat.format(cal.getTime()));
}
运行结果:
当前日历时间:2020-09-03
当前日历时间的毫秒数:1599103912759
年:2020
月(month从0开始):8
日:3
设置后的时间:2020-11-01
将年份+1:2021-11-01
3 SimpleDateFormat
SimpleDateFormat是常用的日期格式化类,将日期字符串解析成Date实例,或者Date格式化为某种模式的字符串。
3.1 SimpleDateFormat 常用API
- SimpleDateFormat(String pattern) // 构造器,入参是时间字符串对应的模式 pattern
- String format(Date date) // 把给定的 date 按照 pattern 指定的模式,格式化为字符串
- Date parse(String source) // 把给定的日期字符串 source 解析成Date实例
SimpleDateFormat定义 pattern 时,各个字母分别对应的含义
Date and Time Pattern:pattern定义的样例
Result:pattern 对应时间字符串的样式
3.2 SimpleDateFormat 简单使用
/**
* 测试 SimpleDateFormat
*/
@Test
public void testSimpleDateFormatAPI() throws ParseException {
//定义时间字符串的pattern
String pattern = "yyyy-MM-dd HH:mm:ss";
SimpleDateFormat format = new SimpleDateFormat(pattern);
//把字符串解析为Date
Date date = format.parse("2020-09-03 12:30:29");
System.out.println("String to Date:"+date);
//打印 Date 的时间字符串
Date now = new Date();
System.out.println("Date to String:"+format.format(now));
}
运行结果:
String to Date:Thu Sep 03 12:30:29 CST 2020
Date to String:2020-09-03 12:57:16
4 Date、Calendar、SimpleDateFormat不足
- Calendar的month从0开始计算,开发者很容易忽略;
- Date 和 Calendar 所提供用于操作时间和日期的API太少了,运算比较麻烦,比如求当前日期的下周六;
- SimpleDateFormate 也是线程不安全的。
4.1 演示 SimpleDateFormate 线程不安全
/**
* 演示SimpleDateFormat类线程不安全
*/
@Test
public void testSimpleDateFormatUnSafe() throws InterruptedException {
// 闭锁:主线程等待所有子线程运行完后,再继续往下运行
// 不了解 CountDownLatch 可以忽略 CountDownLatch 的相关代码,后面会有系列文章介绍
CountDownLatch latch = new CountDownLatch(5);
//定义时间字符串的pattern
String pattern = "yyyy-MM-dd HH:mm:ss";
SimpleDateFormat format = new SimpleDateFormat(pattern);
for (int i = 1; i <= 5; i++) {
int millis = i * 1000;
new Thread(() -> {
Date date = new Date(millis);
String strDateTime = format.format(date);
System.out.println("当前线程" + Thread.currentThread().getName() + " 对应的时间字符串:" + strDateTime);
// 不了解 CountDownLatch 可以忽略 CountDownLatch 的相关代码,后面会有系列文章介绍
latch.countDown();//闭锁数值减一
}).start();
}
// 主线程阻塞,等待所有子线程运行完后,再继续往下运行
// 不了解 CountDownLatch 可以忽略 CountDownLatch 的相关代码,后面会有系列文章介绍
latch.await();
}
因为 SimpleDateFormat 线程不安全,运行结果:
当前线程Thread-0 对应的时间字符串:1970-01-01 08:00:03
当前线程Thread-1 对应的时间字符串:1970-01-01 08:00:03
当前线程Thread-3 对应的时间字符串:1970-01-01 08:00:03
当前线程Thread-4 对应的时间字符串:1970-01-01 08:00:03
当前线程Thread-2 对应的时间字符串:1970-01-01 08:00:03
线程安全的话,正确的结果应该是:
当前线程Thread-2 对应的时间字符串:1970-01-01 08:00:03
当前线程Thread-4 对应的时间字符串:1970-01-01 08:00:05
当前线程Thread-0 对应的时间字符串:1970-01-01 08:00:01
当前线程Thread-1 对应的时间字符串:1970-01-01 08:00:02
当前线程Thread-3 对应的时间字符串:1970-01-01 08:00:04
5 Java8 提供新时间日期API
5.1 时间日期相关API:
LocalDate、LocalTime、LocalDateTime、Instant 的实例,都是不可变的对象,就算进行一些时间调整操作,如 plus/minus操作,返回的实例都是一个新的对象。
所以这些类的操作都是线程安全的
LocalDate、LocalDateTime 获取月份返回的是 Month 的枚举,通过
Month
枚举的getValue
返回1-12
的数值表示 1-12 月份。
- LocalDate //日期类,不包含时间
- LocalTime //时间类,不包含日期
- LocalDateTime // 日期时间类,包含日期和时间
- Instant // 某个时间点,可以获取到毫秒数,能够与 LocalDateTime 相互转换
5.2 两个时间或日期之差
- Duration // 两个时间 Instance 之间的间隔
- Period // 两个日期之间的间隔
5.3 日期操作类
- TemporalAdjuster // 调整时间或日期,如:调整到这个月的第一个星期一,调整后生成一个新的时间或日期实例
- TemporalAdjusters //好多静态方法,用于提供常用的 TemporalAdjuster 实例
5.4 日期或时间解析和格式化
- DateTimeFormatter // 线程安全的日期或时间解析和格式化
5.5 时区
- ZonedDateTime // 带时区的日期时间
- ZoneId // 时区对应的ID,格式为:{区域}/{城市},如:Asia/Shanghai
下面,我们对上门的类详细介绍。
6 LocalDate 日期类
LocalDate 方法 | 用途 |
---|---|
static LocalDate now() static LocalDate of(int year, int month, int dayOfMonth) |
从默认时区的系统时钟获取当前日期。 从给定的年、月和日获取LocalDate的实例。 |
static LocalDate parse(CharSequence text, DateTimeFormatter formatter) | 通过给定的字符串和 DateTimeFormatter 解析成 LocalDate |
LocalDateTime atTime(int hour, int minute, int second) | 这将返回一个 LocalDateTime ,日期为当前日期,时间为指定的小时、分钟和秒 |
long getLong(TemporalField field) | 返回指定字段的值, TemporalField接口的实现类 ChronoField 是个枚举类,定义了很多常用的时间字段TemporalField,LocalDate 中只能使用:, DAY\_OF\_WEEK , ALIGNED\_DAY\_OF\_WEEK\_IN\_MONTH, ALIGNED\_DAY\_OF\_WEEK\_IN\_YEAR, DAY\_OF\_MONTH, DAY\_OF\_YEAR, EPOCH\_DAY, ALIGNED\_WEEK\_OF\_MONTH, ALIGNED\_WEEK\_OF\_YEAR, MONTH\_OF\_YEAR, PROLEPTIC\_MONTH, YEAR\_OF\_ERA, YEAR, ERA |
int getDayOfMonth() DayOfWeek getDayOfWeek() int getDayOfYear() |
返回月份中第几天 返回 DayOfWeek 枚举,表示周几 返回年份中第几天 |
Month getMonth() int getMonthValue() |
返回 Month 枚举,表示月份 返回月份对应的数值 1-12 |
int getYear() | 返回年份 |
LocalDate minusDays(long daysToSubtract) LocalDate minusMonths(long monthsToSubtract) LocalDate minusWeeks(long weeksToSubtract) LocalDate minusYears(long yearsToSubtract) |
当前日期减去多少天,返回新的LocalDate实例 当前日期减去多少月,返回新的LocalDate实例 当前日期减去多少周,返回新的LocalDate实例 当前日期减去多少年,返回新的LocalDate实例 |
LocalDate plusDays(long daysToAdd) LocalDate plusMonths(long monthsToAdd) LocalDate plusWeeks(long weeksToAdd) LocalDate plusYears(long yearsToAdd) |
当前日期加上多少天,返回新的LocalDate实例 当前日期加上多少月,返回新的LocalDate实例 当前日期加上多少周,返回新的LocalDate实例 当前日期加上多少年,返回新的LocalDate实例 |
LocalDate withDayOfMonth(int dayOfMonth) LocalDate withDayOfYear(int dayOfYear) LocalDate withMonth(int month) LocalDate withYear(int year) |
调整月份中的天,返回新的LocalDate实例 调整年份中的年,返回新的LocalDate实例 跳整日期中的月份,返回新的LocalDate实例 调整日期中的年份,返回新的LocalDate实例 |
boolean isLeapYear() | 如果年份是闰年,则为true,否则为false |
boolean isAfter(ChronoLocalDate other) | 如果晚于给定的日期,则为true,否则为false |
boolean isBefore(ChronoLocalDate other) | 如果早于给定的日期,则为true,否则为false |
6.1 LocalDate 简单使用
/**
* 测试 LocalDate
*/
@Test
public void testLocalDate() {
// 从给定的年、月和日获取LocalDate的实例。
LocalDate ldOf = LocalDate.of(2020,9,3);
System.out.println("从给定的年、月和日获取LocalDate的实例: "+ldOf);
// 从默认时区的系统时钟获取当前日期
LocalDate ldNow = LocalDate.now();
System.out.println("从默认时区的系统时钟获取当前日期: "+ldNow);
// 返回一个 LocalDateTime ,日期为当前日期,时间为指定的小时、分钟和秒
LocalDateTime localDateTime = ldNow.atTime(1, 2, 30);
System.out.println("返回一个 LocalDateTime日期为当前日期,时间为指定的小时、分钟和秒:" + localDateTime);
// 返回月份中第几天
System.out.println("返回月份中第几天: "+ldNow.getDayOfMonth());
// 返回 DayOfWeek 枚举,表示周几
System.out.println("返回 DayOfWeek 枚举,表示周几: "+ldNow.getDayOfWeek());
// 返回年份中第几天
System.out.println("返回年份中第几天: "+ldNow.getDayOfYear());
// 返回 Month 枚举,表示月份
System.out.println("返回 Month 枚举,表示月份: " + ldNow.getMonth());
// 返回月份对应的数值 1-12
System.out.println("返回月份对应的数值 1-12 : " + ldNow.getMonthValue());
// 当前日期减去多少天,返回新的LocalDate实例
System.out.println("当前日期减去5天,返回新的LocalDate实例: " + ldNow.minusDays(5));
// 当前日期减去多少月,返回新的LocalDate实例
System.out.println("当前日期减去1月,返回新的LocalDate实例: " + ldNow.minusMonths(1));
// 当前日期加上多少周,返回新的LocalDate实例
System.out.println("当前日期加上1周,返回新的LocalDate实例: " + ldNow.plusWeeks(1));
// 当前日期加上多少年,返回新的LocalDate实例
System.out.println("当前日期加上2年,返回新的LocalDate实例: " + ldNow.plusYears(2));
// 如果年份是闰年,则为true,否则为false
System.out.println("今年是否是闰年?" + ldNow.isLeapYear());
// 如果早于给定的日期,则为true,否则为false
System.out.println("当前日期是否早于2020-9-3? " + ldNow.isBefore(ldOf));
// 如果晚于给定的日期,则为true,否则为false
System.out.println("当前日期是否晚于2020-9-3? " + ldNow.isAfter(ldOf));
}
运行结果:
从给定的年、月和日获取LocalDate的实例: 2020-09-03
从默认时区的系统时钟获取当前日期: 2020-09-04
返回一个 LocalDateTime日期为当前日期,时间为指定的小时、分钟和秒:2020-09-04T01:02:30
返回月份中第几天: 4
返回 DayOfWeek 枚举,表示周几: FRIDAY
返回年份中第几天: 248
返回 Month 枚举,表示月份: SEPTEMBER
返回月份对应的数值 1-12 : 9
当前日期减去5天,返回新的LocalDate实例: 2020-08-30
当前日期减去1月,返回新的LocalDate实例: 2020-08-04
当前日期加上1周,返回新的LocalDate实例: 2020-09-11
当前日期加上2年,返回新的LocalDate实例: 2022-09-04
今年是否是闰年?true
当前日期是否早于2020-9-3? false
当前日期是否晚于2020-9-3? true
7 LocalTime 时间类
因为篇幅问题,只列出常用的API。
LocalTime 方法 | 用途 |
---|---|
static LocalTime now() static LocalTime of(int hour, int minute, int second) static LocalTime of(int hour, int minute, int second, int nanoOfSecond) |
从默认时区的系统时钟获取当前时间。 从给定的hour, minute 和 second获取LocalTime的实例。 从给定的hour, minute, second 和 nanosecond(纳秒)获取LocalTime的实例。 |
static LocalTime parse(CharSequence text, DateTimeFormatter formatter) | 通过给定的字符串和 DateTimeFormatter 解析成 LocalTime |
LocalDateTime atDate(LocalDate date) | 这将返回一个 LocalDateTime ,日期为给定的LocalDate,时间为指定的当前时间 |
long getLong(TemporalField field) | 返回指定字段的值, TemporalField接口的实现类 ChronoField 是个枚举类,定义了很多常用的时间字段TemporalField,LocalTime 中只能使用: NANO\_OF\_SECOND, NANO\_OF\_DAY, MICRO\_OF\_SECOND, MICRO\_OF\_DAY, MILLI\_OF\_SECOND, MILLI\_OF\_DAY, SECOND\_OF\_MINUTE, SECOND\_OF\_DAY, MINUTE\_OF\_HOUR, MINUTE\_OF\_DAY, HOUR\_OF\_AMPM, CLOCK\_HOUR\_OF\_AMPM, HOUR\_OF\_DAY, hour, CLOCK\_HOUR\_OF\_DAY, AMPM\_OF\_DAY |
int getHour() int getMinute() int getSecond() getNano() |
返回小时 返回分钟 返回秒 返回纳秒 |
LocalTime minusHours(long hoursToSubtract) LocalTime minusMinutes(long minutesToSubtract) LocalTime minusSeconds(longsecondsToSubtract) LocalTime minusNanos(long nanosToSubtract) |
当前时间减去多少小时,返回新的LocalTime实例 当前时间减去多少分钟,返回新的LocalTime实例 当前时间减去多少秒,返回新的LocalTime实例 当前时间减去多少纳秒,返回新的LocalTime实例 |
LocalTime plusHours(long hoursToAdd) LocalTime plusMinutes(long minutesToAdd) LocalTime plusSeconds(long secondstoAdd) LocalTime plusNanos(long nanosToAdd) |
当前时间加上多少小时,返回新的LocalTime实例 当前时间加上多少分钟,返回新的LocalTime实例 当前时间加上多少秒,返回新的LocalTime实例 当前时间加上多少纳秒,返回新的LocalTime实例 |
LocalTime withHour(int hour) LocalTime withMinute(int minute) LocalTime withSecond(int second) LocalTime withNano(int nanoOfSecond) |
调整小时,返回新的LocalTime实例 调整分钟,返回新的LocalTime实例 跳整秒数,返回新的LocalTime实例 调整纳秒,返回新的LocalTime实例 |
boolean isAfter(LocalTime other) | 如果晚于给定的时间,则为true,否则为false |
boolean isBefore(LocalTime other) | 如果早于给定的时间,则为true,否则为false |
7.1 LocalTime的简单使用
/**
* 测试 LocalTime
*/
@Test
public void testLocalTime() {
// 从给定的hour, minute 和 second获取LocalTime的实例。
LocalTime ltOf = LocalTime.of(1,5,23);
System.out.println("从给定的hour, minute 和 second获取LocalTime的实例: "+ltOf);
// 从默认时区的系统时钟获取当前时间
LocalTime ltNow = LocalTime.now();
System.out.println("从默认时区的系统时钟获取当前时间: "+ltNow);
// 这将返回一个 LocalDateTime ,日期为给定的LocalDate,时间为指定的当前时间
LocalDateTime localDateTime = ltNow.atDate(LocalDate.now());
System.out.println("这将返回一个 LocalDateTime ,日期为今天(2020-09-04),时间为指定的当前时间:" + localDateTime);
// 返回小时
System.out.println("返回小时: "+ltNow.getHour());
// 返回分钟
System.out.println("返回分钟: "+ltNow.getMinute());
// 返回秒
System.out.println("返回秒: "+ltNow.getSecond());
// 返回纳秒
System.out.println("返回纳秒: " + ltNow.getNano());
// 当前时间减去多少小时,返回新的LocalTime实例
System.out.println("当前时间减去1小时,返回新的LocalTime实例: " + ltNow.minusHours(1));
// 当前时间减去多少分钟,返回新的LocalTime实例
System.out.println("当前时间减去20分钟,返回新的LocalTime实例: " + ltNow.minusMinutes(20));
// 当前时间减去多少秒,返回新的LocalTime实例
System.out.println("当前时间加上10秒,返回新的LocalTime实例: " + ltNow.plusSeconds(10));
// 当前时间减去多少纳秒,返回新的LocalTime实例
System.out.println("当前时间加上999纳秒,返回新的LocalTime实例: " + ltNow.plusNanos(999));
// 调整小时,返回新的LocalTime实例
System.out.println("调整小时到3时,返回新的LocalTime实例:" + ltNow.withHour(3));
}
运行结果:
从给定的hour, minute 和 second获取LocalTime的实例: 01:05:23
从默认时区的系统时钟获取当前时间: 01:24:16.666
这将返回一个 LocalDateTime ,日期为今天(2020-09-04),时间为指定的当前时间:2020-09-04T01:24:16.666
返回小时: 1
返回分钟: 24
返回秒: 16
返回纳秒: 666000000
当前时间减去1小时,返回新的LocalTime实例: 00:24:16.666
当前时间减去20分钟,返回新的LocalTime实例: 01:04:16.666
当前时间加上10秒,返回新的LocalTime实例: 01:24:26.666
当前时间加上999纳秒,返回新的LocalTime实例: 01:24:16.666000999
调整小时到3时,返回新的LocalTime实例:03:24:16.666
8 LocalDateTime 时间日期类
LocalDateTime 表示日期时间,可以理解为包含了 LocalDate 和 LocalTime。
LocalDateTime中的API 与 LocalDate 和 LocalTime的相似,同样包含 :
- now(),
- ofXX(xx),
- minusXX(xx),
- plusXX(xx),
- withXX(xx)方法,
- isAfter(xx),
- isBefore(xx)
- static LocalDateTime parse(CharSequence text, DateTimeFormatter formatter) // 解析时间字符串成LocalDateTime
- long getLong(TemporalField field) // 返回指定字段的值
8.1 LocalDateTime简单使用
/**
* 测试 LocalDateTime
*/
@Test
public void testLocalDateTime() {
// 从给定的year, month, day, hour, minute 和 second 获取LocalDateTime的实例。
LocalDateTime ldtOf = LocalDateTime.of(2020, 9, 3, 1, 5, 23);
System.out.println("从给定的year, month, day, hour, minute 和 second 获取LocalDateTime的实例: " + ldtOf);
// 从默认时区的系统时钟获取当前日期时间 LocalDateTime
LocalDateTime ldtNow = LocalDateTime.now();
System.out.println("从默认时区的系统时钟获取当前日期时间 LocalDateTime: " + ldtNow);
// 这将返回一个带时区的日期时间实例 ZonedDateTime,入参是系统默认时区
ZonedDateTime zonedDateTime = ldtNow.atZone(ZoneId.systemDefault());
System.out.println("返回一个带时区的日期时间实例 ZonedDateTime,入参是系统默认时区:" + zonedDateTime);
// 通过给定 Asia/Shanghai 时区,返回 ZonedDateTime 实例
ZonedDateTime shZonedDateTime = ldtNow.atZone(ZoneId.of("Asia/Shanghai"));
System.out.println("通过给定 Asia/Shanghai 时区,返回 ZonedDateTime 实例:" + shZonedDateTime);
//返回周几
long dayOfWeek = ldtNow.getLong(ChronoField.DAY\\_OF\\_WEEK);
System.out.println("返回周几:" + dayOfWeek);
//日期在天中的毫秒数
long milliOfDay = ldtNow.getLong(ChronoField.MILLI\\_OF\\_DAY);
System.out.println("日期在天中的毫秒数:" + milliOfDay);
LocalDateTime endExclusive = ldtNow.withHour(0).withMinute(0).withSecond(0).withNano(0);
System.out.println("日期在天中的毫秒数 Duration:" + Duration.between(endExclusive, ldtNow).toMillis());
//当前日期是年份中的第几天
long dayOfYear = ldtNow.getLong(ChronoField.DAY\\_OF\\_YEAR);
System.out.println("getLong(TemporalField field)方式获取当前日期是年份中的第几天:" + dayOfYear);
// 返回月份中第几天
System.out.println("返回月份中第几天: " + ldtNow.getDayOfMonth());
// 返回 DayOfWeek 枚举,表示周几
System.out.println("返回 DayOfWeek 枚举,表示周几: " + ldtNow.getDayOfWeek());
// 返回年份中第几天
System.out.println("返回年份中第几天: " + ldtNow.getDayOfYear());
// 返回 Month 枚举,表示月份
System.out.println("返回 Month 枚举,表示月份: " + ldtNow.getMonth());
// 返回月份对应的数值 1-12
System.out.println("返回月份对应的数值 1-12 : " + ldtNow.getMonthValue());
// 返回小时
System.out.println("返回小时: " + ldtNow.getHour());
// 返回分钟
System.out.println("返回分钟: " + ldtNow.getMinute());
// 返回秒
System.out.println("返回秒: " + ldtNow.getSecond());
// 返回纳秒
System.out.println("返回纳秒: " + ldtNow.getNano());
// 当前日期减去多少天,返回新的LocalDateTime实例
System.out.println("当前日期减去5天,返回新的LocalDateTime实例: " + ldtNow.minusDays(5));
// 当前日期减去多少月,返回新的LocalDateTime实例
System.out.println("当前日期减去1月,返回新的LocalDateTime实例: " + ldtNow.minusMonths(1));
// 当前日期加上多少周,返回新的LocalDateTime实例
System.out.println("当前日期加上1周,返回新的LocalDateTime实例: " + ldtNow.plusWeeks(1));
// 当前日期加上多少年,返回新的LocalDateTime实例
System.out.println("当前日期加上2年,返回新的LocalDateTime实例: " + ldtNow.plusYears(2));
// 当前日期时间减去多少小时,返回新的LocalDateTime实例
System.out.println("当前日期时间减去1小时,返回新的LocalDateTime实例: " + ldtNow.minusHours(1));
// 当前日期时间减去多少分钟,返回新的LocalDateTime实例
System.out.println("当前日期时间减去20分钟,返回新的LocalDateTime实例: " + ldtNow.minusMinutes(20));
// 当前日期时间减去多少秒,返回新的LocalDateTime实例
System.out.println("当前日期时间加上10秒,返回新的LocalDateTime实例: " + ldtNow.plusSeconds(10));
// 当前日期时间减去多少纳秒,返回新的LocalDateTime实例
System.out.println("当前日期时间加上999纳秒,返回新的LocalDateTime实例: " + ldtNow.plusNanos(999));
// 如果早于给定的日期,则为true,否则为false
System.out.println("当前日期是否早于2020-9-3? " + ldtNow.isBefore(ldtOf));
// 如果晚于给定的日期,则为true,否则为false
System.out.println("当前日期是否晚于2020-9-3? " + ldtNow.isAfter(ldtOf));
// 调整小时,返回新的LocalDateTime实例
System.out.println("调整月份到8月,返回新的LocalDateTime实例:" + ldtNow.withMonth(8));
// with(TemporalAdjuster adjuster) 通过TemporalAdjuster去调整到对应的时间点,返回新的Instant实例
// 调整当前日期到下个月第一天
LocalDateTime firstDayOfNextMonth = ldtNow.with(TemporalAdjusters.firstDayOfNextMonth());
System.out.println("通过TemporalAdjuster去调整到到下个月第一天:" + firstDayOfNextMonth);
// LocalDateTime 转为 LocalDate
LocalDate localDate = ldtNow.toLocalDate();
System.out.println("LocalDateTime 转为 LocalDate: " + localDate);
// LocalDateTime 转为 LocalTime
LocalTime localTime = ldtNow.toLocalTime();
System.out.println("LocalDateTime 转为 LocalTime: " + localTime);
// LocalDateTime 转为 Instant
Instant instant = ldtNow.atZone(ZoneId.systemDefault()).toInstant();
// instant打印的是格林威治时间,会小8小时,但是毫秒数(时间戳)和当前时区的毫秒数是一样的
System.out.println("LocalDateTime 转为 Instant: " + instant);
}
运行结果:
从给定的year, month, day, hour, minute 和 second 获取LocalDateTime的实例: 2020-09-03T01:05:23
从默认时区的系统时钟获取当前日期时间 LocalDateTime: 2020-09-04T14:10:08.476
返回一个带时区的日期时间实例 ZonedDateTime,入参是系统默认时区:2020-09-04T14:10:08.476+08:00[Asia/Shanghai]
通过给定 Asia/Shanghai 时区,返回 ZonedDateTime 实例:2020-09-04T14:10:08.476+08:00[Asia/Shanghai]
返回周几:5
日期在天中的毫秒数:51008476
日期在天中的毫秒数 Duration:51008476
getLong(TemporalField field)方式获取当前日期是年份中的第几天:248
返回月份中第几天: 4
返回 DayOfWeek 枚举,表示周几: FRIDAY
返回年份中第几天: 248
返回 Month 枚举,表示月份: SEPTEMBER
返回月份对应的数值 1-12 : 9
返回小时: 14
返回分钟: 10
返回秒: 8
返回纳秒: 476000000
当前日期减去5天,返回新的LocalDateTime实例: 2020-08-30T14:10:08.476
当前日期减去1月,返回新的LocalDateTime实例: 2020-08-04T14:10:08.476
当前日期加上1周,返回新的LocalDateTime实例: 2020-09-11T14:10:08.476
当前日期加上2年,返回新的LocalDateTime实例: 2022-09-04T14:10:08.476
当前日期时间减去1小时,返回新的LocalDateTime实例: 2020-09-04T13:10:08.476
当前日期时间减去20分钟,返回新的LocalDateTime实例: 2020-09-04T13:50:08.476
当前日期时间加上10秒,返回新的LocalDateTime实例: 2020-09-04T14:10:18.476
当前日期时间加上999纳秒,返回新的LocalDateTime实例: 2020-09-04T14:10:08.476000999
当前日期是否早于2020-9-3? false
当前日期是否晚于2020-9-3? true
调整月份到8月,返回新的LocalDateTime实例:2020-08-04T14:10:08.476
通过TemporalAdjuster去调整到到下个月第一天:2020-10-01T14:10:08.476
LocalDateTime 转为 LocalDate: 2020-09-04
LocalDateTime 转为 LocalTime: 14:10:08.476
LocalDateTime 转为 Instant: 2020-09-04T06:10:08.476Z
9 Instant 某个时间点
Instant 某个时间点,可以获取到毫秒数,能够与 LocalDateTime 相互转换
Instant 方法 | 用途 |
---|---|
static Instant now() | 返回当前时间对应的 Instant 实例 |
static Instant ofEpochMilli(long epochMilli) | 通过给定的毫秒数(milliseconds from 1970-01-01T00:00:00Z),返回 Instant 实例 |
ZonedDateTime atZone(ZoneId zone) | 将当前 Instant 和给定的时区ID ZoneId 结合返回 ZonedDateTime |
int get(TemporalField field) | 返回指定字段的值, TemporalField接口的实现类 ChronoField 是个枚举类,定义了很多常用的时间字段TemporalField,Instant中只能使用: NANO\_OF\_SECOND, MICRO\_OF\_SECOND, MILLI\_OF\_SECOND, INSTANT\_SECOND, 举例解释常用字段的作用: MILLI\_OF\_SECOND: 秒内的毫秒数,从0到999 INSTANT\_SECONDS: 1970-01-01T00:00Z(ISO)开始到当前Instant实例的秒数 |
boolean isAfter(Instant otherInstant) | 如果当前 Instant 晚于给定的Instant,则为true,否则为false |
boolean isBefore(Instant otherInstant) | 如果当前 Instant 早于给定的Instant,则为true,否则为false |
Instant minus(long amountToSubtract, TemporalUnit unit) | 减去给定的时间量,返回新的Instant实例 |
Instant plus(long amountToAdd, TemporalUnit unit) | 增加给定的时间量,返回新的Instant实例 |
Instant with(TemporalField field, long newValue) | 调整给定时间属性的值 ,返回新的Instant实例 |
Instant with(TemporalAdjuster adjuster) | 通过TemporalAdjuster去调整到对应的时间点,返回新的Instant实例 |
long toEpochMilli() | 返回当前时间点对应的毫秒数,也就是时间戳 |
9.1 Instant 简单使用
/**
* 测试 Instant
* 注意:Instant.toString() 调用的是 DateTimeFormatter.ISO\\_INSTANT.format(this)
* instant打印的是格林威治时间,会小8小时,但是毫秒数(时间戳)和当前时区的毫秒数是一样的
*/
@Test
public void testInstant() {
// 返回当前时间对应的 Instant 实例
Instant instantNow = Instant.now();
System.out.println("返回当前时间对应的 Instant 实例: " + instantNow);
// 这将返回一个带时区的日期时间实例 ZonedDateTime,入参是系统默认时区
ZonedDateTime zonedDateTime = instantNow.atZone(ZoneId.systemDefault());
System.out.println("返回一个带时区的日期时间实例 ZonedDateTime,入参是系统默认时区:" + zonedDateTime);
// 通过给定 Asia/Shanghai 时区,返回 ZonedDateTime 实例
ZonedDateTime shZonedDateTime = instantNow.atZone(ZoneId.of("Asia/Shanghai"));
System.out.println("通过给定 Asia/Shanghai 时区,返回 ZonedDateTime 实例:" + shZonedDateTime);
//秒内的毫秒数,从0到999
long milliOfSecond = instantNow.getLong(ChronoField.MILLI\\_OF\\_SECOND);
System.out.println("秒内的毫秒数,从0到999:" + milliOfSecond);
//1970-01-01T00:00Z(ISO)开始到当前Instant实例的秒数
long instantSeconds = instantNow.getLong(ChronoField.INSTANT\\_SECONDS);
System.out.println("1970-01-01T00:00Z(ISO)开始到当前Instant实例的秒数:" + instantSeconds);
//1970-01-01T00:00Z(ISO)开始到当前Instant实例的毫秒数
long milli = instantNow.toEpochMilli();
System.out.println("开始到当前Instant实例的毫秒数:" + milli);
// 当前日期减去多少天,返回新的Instant实例
System.out.println("当前日期减去5天,返回新的Instant实例: " + instantNow.minus(5, ChronoUnit.DAYS));
// 当前日期减去1小时
System.out.println("当前日期减去1小时,返回新的Instant实例: " + instantNow.minus(1, ChronoUnit.HOURS));
// 当前日期时间减去多少秒,返回新的Instant实例
System.out.println("当前日期时间加上10秒,返回新的Instant实例: " + instantNow.plusSeconds(10));
// 当前日期时间减去多少纳秒,返回新的Instant实例
System.out.println("当前日期时间加上999纳秒,返回新的Instant实例: " + instantNow.plusNanos(999));
// 2020-9-3 对应的 Instant
Instant otherInstant = LocalDateTime.of(2020, 9, 3, 0, 0, 0).atZone(ZoneId.systemDefault()).toInstant();
// 如果早于给定的日期,则为true,否则为false
System.out.println("当前日期是否早于2020-9-3? " + instantNow.isBefore(otherInstant));
// 如果晚于给定的日期,则为true,否则为false
System.out.println("当前日期是否晚于2020-9-3? " + instantNow.isAfter(otherInstant));
// with(ChronoField.INSTANT\\_SECONDS, 0)):设置1970-01-01T00:00Z(ISO)开始到当前Instant实例的秒数
// 也就是调整到 1970-01-01T00:00Z(ISO)
System.out.println("调整到 1970-01-01T00:00Z(ISO):" + instantNow.with(ChronoField.INSTANT\\_SECONDS, 0).with(ChronoField.NANO\\_OF\\_SECOND,0));
// Instant 转为 LocalDateTime
LocalDateTime localDateTime = LocalDateTime.ofInstant(instantNow, ZoneId.systemDefault());
System.out.println("Instant 转为 LocalDateTime: "+localDateTime);
}
运行结果:
返回当前时间对应的 Instant 实例: 2020-09-04T06:06:10.851Z
返回一个带时区的日期时间实例 ZonedDateTime,入参是系统默认时区:2020-09-04T14:06:10.851+08:00[Asia/Shanghai]
通过给定 Asia/Shanghai 时区,返回 ZonedDateTime 实例:2020-09-04T14:06:10.851+08:00[Asia/Shanghai]
秒内的毫秒数,从0到999:851
1970-01-01T00:00Z(ISO)开始到当前Instant实例的秒数:1599199570
开始到当前Instant实例的毫秒数:1599199570851
当前日期减去5天,返回新的Instant实例: 2020-08-30T06:06:10.851Z
当前日期减去1小时,返回新的Instant实例: 2020-09-04T05:06:10.851Z
当前日期时间加上10秒,返回新的Instant实例: 2020-09-04T06:06:20.851Z
当前日期时间加上999纳秒,返回新的Instant实例: 2020-09-04T06:06:10.851000999Z
当前日期是否早于2020-9-3? false
当前日期是否晚于2020-9-3? true
调整到 1970-01-01T00:00Z(ISO):1970-01-01T00:00:00Z
Instant 转为 LocalDateTime: 2020-09-04T14:06:10.851
10 Duration 时间间隔
Duration:时间间隔,表示两个时间点之间的时间间隔长度。
Duration 方法 | 用途 |
---|---|
static Duration between(Temporal startInclusive, Temporal endExclusive) | 返回两个时间点之间的间隔Duration。 参数可以是:LocalTime、LocalDateTime、Instant 注意:LocalDate不可以作为入参 |
static Instant ofEpochMilli(long epochMilli) | 通过给定的毫秒数(milliseconds from 1970-01-01T00:00:00Z),返回 Instant 实例 |
of(long amount, TemporalUnit unit) static Duration ofDays(long days) static Duration ofHours(long hours) static Duration ofMinutes(long minutes) static Duration ofSeconds(long seconds) static Duration ofSeconds(long seconds) |
通过给定时间单位 TemporalUnit 的时间长度 amount,返回Duration实例 |
Duration minus(long amountToSubtract, TemporalUnit unit) | 减去给定的时间量,返回新的Duration实例 |
Duration plus(long amountToAdd, TemporalUnit unit) | 增加给定的时间量,返回新的Duration实例 |
long toDays() long toHours() long toMinutes() long toMillis() |
Duration 时间长度转为 天数/小时数/分钟数/毫秒数 |
10.1 Duration 简单使用
/**
* 测试 Duration
*/
@Test
public void testDuration() {
// 从给定的year, month, day, hour, minute 和 second 获取LocalDateTime的实例。
LocalDateTime ldtOf = LocalDateTime.of(2020, 9, 4, 10, 0, 0);
System.out.println("从给定的year, month, day, hour, minute 和 second 获取LocalDateTime的实例: " + ldtOf);
// 从默认时区的系统时钟获取当前日期时间 LocalDateTime
LocalDateTime ldtNow = LocalDateTime.now();
System.out.println("从默认时区的系统时钟获取当前日期时间 LocalDateTime: " + ldtNow);
// 返回两个时间点之间的间隔Duration
// 参数可以是:LocalTime、LocalDateTime、Instant
// 注意:LocalDate不可以作为入参
Duration duration = Duration.between(ldtOf, ldtNow);
// 打印 Duration 时间长度转为 天数/小时数/分钟数/毫秒数
System.out.println("Duration 时间间隔天数:" + duration.toDays());
System.out.println("Duration 时间间隔小时数:" + duration.toHours());
System.out.println("Duration 时间间隔分钟数:" + duration.toMinutes());
System.out.println("Duration 时间间隔毫秒数:" + duration.toMillis());
}
运行结果:
从给定的year, month, day, hour, minute 和 second 获取LocalDateTime的实例: 2020-09-04T10:00
从默认时区的系统时钟获取当前日期时间 LocalDateTime: 2020-09-04T14:24:22.692
Duration 时间间隔天数:0
Duration 时间间隔小时数:4
Duration 时间间隔分钟数:264
Duration 时间间隔毫秒数:15862692
11 Period 日期间隔
Period 方法 | 用途 |
---|---|
static Period between(LocalDate startDateInclusive, LocalDate endDateExclusive) | 返回两个日期之间的间隔 Period,入参是:LocalDate 类型 |
static Period of(int years, int months, int days) | 通过给定的 years, months 和 days 生成Period |
Period minusDays(long daysToSubtract) Period minusMonths(long monthsToSubtract) Period minusYears(long yearsToSubtract) |
period 实例中 days字段- daysToSubtract\ months字段-daysToSubtract\ years字段-yearsToSubtract |
Period plusDays(long daysToAdd) Period plusMonths(long monthsToAdd) Period plusYears(long yearsToAdd) |
period 实例中 days字段+ daysToSubtract\ months字段+daysToSubtract\ years字段+yearsToSubtract |
long getDays() int getMonths() int getYears() |
获 period 实例的 days/months/years 字段值,这里不是间隔的长度,只是字段值,看测试用例会更容易理解 |
Period withDays(int days) Period withMonths(int months) Period withYears(int years) |
调整 Period中的 days,months,years 字段为给定值,返回新的 Period 实例 |
long toTotalMonths() | 返回 Period 转换过来总的月数。 如: Period period = Period.of(1, 1, 1); period.toTotalMonths() //13 |
11.1 Period 简单使用
/**
* 测试 Period
*/
@Test
public void testPeriod() {
// 从给定的年、月和日获取LocalDate的实例。
LocalDate ldOf = LocalDate.of(2018, 9, 3);
System.out.println("从给定的年、月和日获取LocalDate的实例: " + ldOf);
// 从默认时区的系统时钟获取当前日期
LocalDate ldNow = LocalDate.now();
System.out.println("从默认时区的系统时钟获取当前日期: " + ldNow);
// 返回两个时间点之间的间隔Period
// 参数类型是:LocalDate
Period period = Period.between(ldOf, ldNow);
// 打印 Period 时间长度转为 天数/月数/年数
System.out.println("period 实例 days 字段值:" + period.getDays());
System.out.println("period 实例 months 字段值:" + period.getMonths());
System.out.println("period 实例 years 字段值:" + period.getYears());
// Period 日期间隔长度转换为总的月数
System.out.println("Period 日期间隔长度转换为总的月数:" + period.toTotalMonths());
}
运行结果:
从给定的年、月和日获取LocalDate的实例: 2018-09-03
从默认时区的系统时钟获取当前日期: 2020-09-04
period 实例 days 字段值:1
period 实例 months 字段值:0
period 实例 years 字段值:2
Period 日期间隔长度转换为总的月数:24
12 日期操作类 TemporalAdjuster 和 TemporalAdjusters
前面介绍过:
- TemporalAdjuster // 调整时间或日期,如:调整到这个月的第一个星期一,调整后生成一个新的时间或日期实例
- TemporalAdjusters //好多静态方法,用于提供常用的 TemporalAdjuster
Instant、LocalDate、LocalTime、LocalDateTime 或者其他实现了 Temporal 接口的类中都有一个 with(TemporalAdjuster adjuster)
方法,入参为 TemporalAdjuster,也就是要进行的时间调整操作,返回新的时间日期实例。
TemporalAdjusters 提供了好多静态方法用于生成常用的 TemporalAdjuster 实例
TemporalAdjusters 静态方法 | 用途 |
---|---|
static TemporalAdjuster dayOfWeekInMonth(int ordinal, DayOfWeek dayOfWeek) | 调整返回新日期实例,调整到当前日期月份的第几个周几,ordinal表示第几个,DayOfWeek是枚举类,表示周几 |
static TemporalAdjuster firstDayOfMonth() | 调整返回新日期实例,调整到当前日期当前月份第一天 |
static TemporalAdjuster firstDayOfNextMonth() | 调整返回新日期实例,调整到当前日期下一月份的第一天 |
static TemporalAdjuster firstDayOfNextYear() | 调整返回新日期实例,调整到当前日期下一年份的第一天 |
static TemporalAdjuster firstDayOfYear() | 调整返回新日期实例,调整到当前日期当前年份的第一天 |
static TemporalAdjuster firstInMonth(DayOfWeek dayOfWeek) | 调整返回新日期实例,调整到当前日期当前月份的第一个周几 |
static TemporalAdjuster lastDayOfMonth() | 调整返回新日期实例,调整到当前日期当前月份的最后一天 |
static TemporalAdjuster lastDayOfYear() | 调整返回新日期实例,调整到当前日期当前年份的最后一天 |
static TemporalAdjuster lastInMonth(DayOfWeek dayOfWeek) | 调整返回新日期实例,调整到当前日期当前月份的最后一个周几 |
static TemporalAdjuster next(DayOfWeek dayOfWeek) | 调整返回新日期实例,调整到下一个周几 |
static TemporalAdjuster nextOrSame(DayOfWeek dayOfWeek) | 如果当前日期刚好符合是入参指定周几(DayOfWeek),返回当前日期实例;否则调整到下一个周几,返回新日期实例 |
static TemporalAdjuster ofDateAdjuster(UnaryOperator dateBasedAdjuster) | 通过函数式接口 UnaryOperator ,把当前日期实例作为抽象方法入参,进行日期操作后返回新的 LocalDate |
static TemporalAdjuster previous(DayOfWeek dayOfWeek) | 调整返回新日期实例,调整到前一个周几 |
static TemporalAdjuster previousOrSame(DayOfWeek dayOfWeek) | 如果当前日期刚好符合是入参指定周几(DayOfWeek),返回当前日期实例;否则调整到前一个周几,返回新日期实例 |
12.1 TemporalAdjusters简单使用
/**
* 测试 TemporalAdjuster
*/
@Test
public void testTemporalAdjuster() {
// 从默认时区的系统时钟获取当前日期
LocalDate ldNow = LocalDate.now();
System.out.println("当前日期: " + ldNow);
// 调整到当前日期当前月份第一天
LocalDate firstDayOfMonth = ldNow.with(TemporalAdjusters.firstDayOfMonth());
System.out.println("当前月份第一天: " + firstDayOfMonth);
// 当前日期刚好符合是入参指定周几(DayOfWeek),返回当前日期实例;否则调整到下一个周几,返回新日期实例
LocalDate nextOrSameFriday = ldNow.with(TemporalAdjusters.nextOrSame(DayOfWeek.FRIDAY));
System.out.println("当前日期刚好符合是入参指定周五(DayOfWeek),返回当前日期实例: " + nextOrSameFriday);
LocalDate nextOrSameMonday = ldNow.with(TemporalAdjusters.nextOrSame(DayOfWeek.MONDAY));
System.out.println("当前日期不符合是入参指定周一(DayOfWeek),调整到下一个周一,返回新日期实例: " + nextOrSameMonday);
// 下一个周五
LocalDate nextFriday = ldNow.with(TemporalAdjusters.next(DayOfWeek.FRIDAY));
System.out.println("下一个周五: " + nextFriday);
// 当前月份的最后一天
LocalDate lastDayOfMonth = ldNow.with(TemporalAdjusters.lastDayOfMonth());
System.out.println("当前月份的最后一天: " + lastDayOfMonth);
// 下一月份的第一天
LocalDate firstDayOfNextMonth = ldNow.with(TemporalAdjusters.firstDayOfNextMonth());
System.out.println("下一月份的第一天: " + firstDayOfNextMonth);
}
运行结果:
当前日期: 2020-09-04
当前月份第一天: 2020-09-01
当前日期刚好符合是入参指定周五(DayOfWeek),返回当前日期实例: 2020-09-04
当前日期不符合是入参指定周一(DayOfWeek),调整到下一个周一,返回新日期实例: 2020-09-07
下一个周五: 2020-09-11
当前月份的最后一天: 2020-09-30
下一月份的第一天: 2020-10-01
13 DateTimeFormatter 日期或时间解析和格式化
DateTimeFormatter | 用途 |
---|---|
static DateTimeFormatter ofPattern(String pattern) | 通过日期时间格式 pattern 构建一个 DateTimeFormatter |
String format(TemporalAccessor temporal) | 将 LocalTime、LocalDate、LocalDateTime 作为入参,格式化为日期时间字符串 |
LocalDate、LocalTime、LcoalDateTime中都有 static LocalDate/LocalTime/LcoalDateTime parse(CharSequence text, DateTimeFormatter formatter)
方法,可以将时间字符串,解析为 LocalDate/LocalTime/LcoalDateTime 实例。
定义 Pattern 相关字母和符号的含义
13.1 DateTimeFormatter 简单使用
/**
* 测试 DateTimeFormatter
*/
@Test
public void testDateTimeFormatter() {
// 通过日期时间格式 pattern 构建一个 DateTimeFormatter
DateTimeFormatter ldtFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss SSS");
LocalDateTime ldt = LocalDateTime.now();
String strLdt = ldtFormat.format(ldt);// 格式化 LocalDateTime
System.out.println("format LocalDateTime : " + strLdt);
System.out.println("parse to LocalDateTime : " + LocalDateTime.parse("2020-10-01 08:00:06 666", ldtFormat));//解析为LocalDateTime
DateTimeFormatter ldFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd");
LocalDate ld = LocalDate.now();
String strLd = ldFormat.format(ld);// 格式化 LocalDateTime
System.out.println("format LocalDate : " + strLd);
System.out.println("parse to LocalDate : " + LocalDate.parse("2020-10-01", ldFormat));//解析为LocalDate
DateTimeFormatter ltFormat = DateTimeFormatter.ofPattern("HH:mm:ss SSS");
LocalTime lt = LocalTime.now();
String strLt = ltFormat.format(lt);// 格式化 LocalDateTime
System.out.println("format LocalDate : " + strLt);
System.out.println("parse to LocalTime : " + LocalTime.parse("08:00:06 666", ltFormat));//解析为LocalTime
}
运行结果:
format LocalDateTime : 2020-09-04 14:57:57 294
parse to LocalDateTime : 2020-10-01T08:00:06.666
format LocalDate : 2020-09-04
parse to LocalDate : 2020-10-01
format LocalDate : 14:57:57 294
parse to LocalTime : 08:00:06.666
13.2 DateTimeFormatter 线程安全,解决 SimpleDateFormate 线程不安全问题
4.1小节已经演示了 SimpleDateFormate 线程不安全问题,但是 DateTimeFormatter 时线程安全的,看下面代码:
/**
* 演示 DateTimeFormatter 类线程安全
*/
@Test
public void testDateTimeFormatterSafe() throws InterruptedException {
// 闭锁:主线程等待所有子线程运行完后,再继续往下运行
// 不了解 CountDownLatch 可以忽略 CountDownLatch 的相关代码,后面会有系列文章介绍
CountDownLatch latch = new CountDownLatch(5);
//定义时间字符串的pattern
String pattern = "yyyy-MM-dd HH:mm:ss";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
for (int i = 1; i <= 5; i++) {
int seconds = i;
new Thread(() -> {
LocalDateTime of = LocalDateTime.of(2020, 9, 3, 12, 14, 30);
of = of.plusSeconds(seconds);//每个线程分别增加传进来的秒数
String strDateTime = formatter.format(of);
System.out.println("当前线程" + Thread.currentThread().getName() + " 对应的时间字符串:" + strDateTime);
// 不了解 CountDownLatch 可以忽略 CountDownLatch 的相关代码,后面会有系列文章介绍
latch.countDown();//闭锁数值减一
}).start();
}
// 主线程阻塞,等待所有子线程运行完后,再继续往下运行
// 不了解 CountDownLatch 可以忽略 CountDownLatch 的相关代码,后面会有系列文章介绍
latch.await();
}
运行结果:
当前线程Thread-0 对应的时间字符串:2020-09-03 12:14:31
当前线程Thread-3 对应的时间字符串:2020-09-03 12:14:34
当前线程Thread-1 对应的时间字符串:2020-09-03 12:14:32
当前线程Thread-4 对应的时间字符串:2020-09-03 12:14:35
当前线程Thread-2 对应的时间字符串:2020-09-03 12:14:33
14 时区 ZonedDateTime 和 ZoneId
- ZonedDateTime // 带时区的日期时间
- ZoneId // 时区对应的ID,格式为:{区域}/{城市},如:Asia/Shanghai
在 Instant 和 LocalDateTime 中有实例方法 ZonedDateTime atZone(ZoneId zone)
,返回一个带时区的日期时间实例 ZonedDateTime
ZoneId 中的两个常用方法:
- getAvailableZoneIds() : 可以获取所有时区信息
- of(id) : 用指定的时区信息获取ZoneId 对象
14.1 ZonedDateTime 和 ZoneId 简单使用
/**
* 测试 时区
*/
@Test
public void testZone() {
ZoneId systemDefaultZoneId = ZoneId.systemDefault();// 系统默认时区
ZoneId shZoneId = ZoneId.of("Asia/Shanghai");// 上海时区
System.out.println("系统默认时区:" + systemDefaultZoneId);
System.out.println("上海时区:" + shZoneId);
LocalDateTime ldt = LocalDateTime.now();
// 这将返回一个带时区的日期时间实例 ZonedDateTime,入参是系统默认时区
ZonedDateTime zdtFromLdt = ldt.atZone(systemDefaultZoneId);
System.out.println(" ZonedDateTime from LocalDateTime:" + zdtFromLdt);
Instant instant = Instant.now();
// 这将返回一个带时区的日期时间实例 ZonedDateTime,入参是上海时区
ZonedDateTime zdtFromInstant = instant.atZone(shZoneId);
System.out.println(" ZonedDateTime from Instant In Asia/Shanghai :" + zdtFromInstant);
// 获取所有时时区信息
Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
// 打印所有时区信息
System.out.println("所有时区信息: ");
availableZoneIds.forEach(System.out::println);
}
运行结果:
系统默认时区:Asia/Shanghai
上海时区:Asia/Shanghai
ZonedDateTime from LocalDateTime:2020-09-04T15:11:04.685+08:00[Asia/Shanghai]
ZonedDateTime from Instant In Asia/Shanghai :2020-09-04T15:11:04.685+08:00[Asia/Shanghai]
所有时区信息:
Asia/Aden
America/Cuiaba
Etc/GMT+9
Etc/GMT+8
Africa/Nairobi
America/Marigot
Asia/Aqtau
Pacific/Kwajalein
America/El\\_Salvador
Asia/Pontianak
...
15 结语
这篇文章写之前,Java8相关的时间日期API 在项目中经常使用到,以为写起来会很简单,但是真正整理思路时,才发现好多细节繁琐的事项。整理编辑思路,各个章节需要循序渐进,收集资料,看 Oracle 官网的 JAVA API docs,看源码进一步理解各个API 的使用,写DEMO,测试,开始编辑码字。
硬肝: 3个深夜搞到1点半,3个早晨7点到8点半,3个中午 12点半到13点半。
致敬自己!
这篇文章已经把实战中经常用到的大部分API进行介绍,还有一些特殊的API,有兴趣的朋友,自行研究,也欢迎留言讨论。
共同学习,写下你的评论
评论加载中...
作者其他优质文章