Java 8通过发布新的Date-Time API (JSR 310)来进一步加强对日期与时间的处理。
在旧版的 Java 中,日期时间 API 存在诸多问题,其中有:
非线程安全 − java.util.Date 是非线程安全的,所有的日期类都是可变的,这是Java日期类最大的问题之一。
设计很差 − Java的日期/时间类的定义并不一致,在java.util和java.sql的包中都有日期类,此外用于格式化和解析的类在java.text包中定义。java.util.Date同时包含日期和时间,而java.sql.Date仅包含日期,将其纳入java.sql包并不合理。另外这两个类都有相同的名字,这本身就是一个非常糟糕的设计。
时区处理麻烦 − 日期类并不提供国际化,没有时区支持,因此Java引入了java.util.Calendar和java.util.TimeZone类,但他们同样存在上述所有的问题。
Java8引入了一套全新的日期时间处理API,新的API基于ISO标准日历系统。
Java8 在 java.time 包下提供了很多新的 API。以下为两个比较重要的 API:
Local(本地) − 简化了日期时间的处理,没有时区的问题。
Zoned(时区) − 通过制定的时区处理日期时间。
新的java.time包涵盖了所有处理日期,时间,日期/时间,时区,时刻(instants),过程(during)与时钟(clock)的操作。
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
public class DateTimeAPI {
public static void main(String[] args) {
LocalDate.now(); // 获取当前日期 format: yyyy-MM-dd
LocalTime.now(); // 获取当前时间 format: HH:mm:ss
LocalDateTime.now(); // 获取当前日期时间 format: yyyy-MM-dd HH:mm:ss
}
}
以上是对当前日期的获取,比较三者的输出(print console:)有什么区别:
2020-04-20
11:27:01.570
2020-04-20T11:27:01.570
调用工厂方法LocalDate.of()创建任意日期, 该方法需要传入年、月、日做参数,返回对应的LocalDate实例。这个方法的好处是没再犯老API的设计错误,比如年度起始于1900,月份是从0开始等等.
public static void main(String[] args) {
LocalDate date = LocalDate.of(2000, 1, 1);
LocalTime time = LocalTime.of(0,0,0);
LocalDateTime dateTime = LocalDateTime.of(date, time);
System.out.println("千禧年=" + dateTime);
LocalDateTime dateTime1 = LocalDateTime.of(2000,1,1,0,0,0);
System.out.println("千禧年=" + dateTime1);
}
输出结果如下:
千禧年=2000-01-01T00:00
千禧年=2000-01-01T00:00
public static void main(String[] args) {
LocalDate date = LocalDate.now();
System.out.printf("年=%d, 月=%d, 日=%d", date.getYear(), date.getMonthValue(), date.getDayOfMonth());
System.out.println();
LocalTime time = LocalTime.now();
System.out.printf("时=%d, 分=%d, 秒=%d, 毫秒=%d", time.getHour(), time.getMinute(), time.getSecond(), time.getNano());
System.out.println();
LocalDateTime dateTime = LocalDateTime.now();
System.out.printf("年=%d, 月=%d, 日=%d, 时=%d, 分=%d, 秒=%d, 毫秒=%d", dateTime.getYear(), dateTime.getMonthValue(),
dateTime.getDayOfMonth(), dateTime.getHour(), dateTime.getMinute(), dateTime.getSecond(), dateTime.getNano());
}
输出结果如下:
年=2020, 月=4, 日=20
时=11, 分=58, 秒=1, 毫秒=800000000
年=2020, 月=4, 日=20, 时=11, 分=58, 秒=1, 毫秒=801000000
public static void main(String[] args) {
// 获取秒数
Long second = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"));
System.out.println("时间戳 秒数: " + second);
// 获取毫秒
Long millisecond = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();
System.out.println("时间戳 毫秒: " + millisecond);
}
输出结果如下:
时间戳 秒数: 1587367802
时间戳 毫秒: 1587367802085
Java8提供了新的plusXxx() 、minsXxx()方法用于计算日期时间增量值 / 减量值,替代了原来的add()方法。新的API将返回一个全新的日期时间示例,需要使用新的对象进行接收。
public static void main(String[] args) {
// 时间增量
LocalTime time = LocalTime.now();
System.out.println("当前时间 time: " + time);
System.out.println("增加 2小时: " + time.plusHours(2));
System.out.println("增加 2分钟: " + time.plusMinutes(2));
System.out.println("增加 2秒钟: " + time.plusSeconds(2));
System.out.println("增加 2毫秒: " + time.plusNanos(2));
// 日期增量
LocalDate date = LocalDate.now();
System.out.println("当前日期 date: " + date);
System.out.println("增加 2年: " + date.plusYears(2));
System.out.println("增加 2月: " + date.plusMonths(2));
System.out.println("增加 2周: " + date.plusWeeks(2));
System.out.println("增加 2天: " + date.plusDays(2));
}
输出结果如下:
当前时间 time: 14:31:54.785
增加 2小时: 16:31:54.785
增加 2分钟: 14:33:54.785
增加 2秒钟: 14:31:56.785
增加 2毫秒: 14:31:54.785000002
当前日期 date: 2020-04-20
增加 2年: 2022-04-20
增加 2月: 2020-06-20
增加 2周: 2020-05-04
增加 2天: 2020-04-22
Java8提供了isAfter()、isBefore()用于判断当前日期时间和指定日期时间的比较。
public static void main(String[] args) {
LocalDateTime dateTime = LocalDateTime.now();
LocalDateTime dateTime1 = LocalDateTime.of(2000,1,1,0,0,0,0);
if (dateTime.isAfter(dateTime1)) {
System.out.println("千禧年已经过去了。。。。");
}
LocalDateTime dateTime2 = LocalDateTime.of(2050, 1,1,0,0,0,0);
if (dateTime.isBefore(dateTime2)) {
System.out.println("2050年还未到来!!!!!");
}
}
输出结果如下:
千禧年已经过去了。。。。
2050年还未到来!!!!!
public static void main(String[] args) {
LocalDate now = LocalDate.now();
LocalDate date = LocalDate.of(2018, 9, 24);
System.out.println("日期是否相等=" + now.equals(date));
}
输出结果如下:
日期是否相等=false
public static void main(String[] args) {
// 计算时间点之间的间隔
LocalDateTime startTime = LocalDateTime.now();
LocalDateTime endTime = startTime.plusHours(1).plusMinutes(30);
Duration duration = Duration.between(startTime, endTime);
Long day = duration.toDays();
System.out.println("间隔天数: " + day);
Long hour = duration.toHours();
System.out.println("间隔小时: " + hour);
Long minute = duration.toMinutes();
System.out.println("间隔分钟: " + minute);
Long millis = duration.toMillis();
System.out.println("间隔毫秒: " + millis);
}
输出结果如下:
间隔天数: 0
间隔小时: 1
间隔分钟: 90
间隔毫秒: 5400000
public static void main(String[] args) {
// 计算日期之间的间隔
LocalDate startDate = LocalDate.now();
LocalDate endDate = startDate.plusMonths(1).plusDays(5);
Period period = Period.between(startDate, endDate);
int year = period.getYears();
System.out.println("间隔年数: " + year);
int month = period.getMonths();
System.out.println("间隔月数: " + month);
int day = period.getDays();
System.out.println("间隔天数: " + day);
}
输出结果如下:
间隔年数: 0
间隔月数: 1
间隔天数: 5
Java 8不仅分离了日期和时间,也把时区分离出来了。现在有一系列单独的类如ZoneId来处理特定时区,ZoneDateTime类来表示某时区下的时间。
public static void main(String[] args) {
// 上海时间
ZoneId shanghaiZoneId = ZoneId.of("Asia/Shanghai");
ZonedDateTime shanghaiZonedDateTime = ZonedDateTime.now(shanghaiZoneId);
// 东京时间
ZoneId tokyoZoneId = ZoneId.of("Asia/Tokyo");
ZonedDateTime tokyoZonedDateTime = ZonedDateTime.now(tokyoZoneId);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
System.out.println("上海时间: " + shanghaiZonedDateTime.format(formatter));
System.out.println("东京时间: " + tokyoZonedDateTime.format(formatter));
}
输出结果如下:
上海时间: 2020-04-20 15:03:18
东京时间: 2020-04-20 16:03:18
public static void main(String[] args) {
// 北京时间
ZonedDateTime zonedDateTime = LocalDateTime.now().atZone(ZoneId.of("Asia/Shanghai"));
ZonedDateTime zonedDateTime1 = LocalDateTime.now().atZone(ZoneId.of("UTC+8"));
System.out.println("上海时间 : " + zonedDateTime);
System.out.println("上海时间 UTC: " + zonedDateTime1);
// 东京时间
ZonedDateTime zonedDateTime2 = LocalDateTime.now().atZone(ZoneId.of("Asia/Tokyo"));
ZonedDateTime zonedDateTime3 = LocalDateTime.now().atZone(ZoneId.of("UTC+9"));
System.out.println("东京时间 : " + zonedDateTime2);
System.out.println("东京时间 UTC: " + zonedDateTime3);
}
输出结果如下:
上海时间 : 2020-04-20T15:15:58.378+08:00[Asia/Shanghai]
上海时间 UTC: 2020-04-20T15:15:58.380+08:00[UTC+08:00]
东京时间 : 2020-04-20T15:15:58.380+09:00[Asia/Tokyo]
东京时间 UTC: 2020-04-20T15:15:58.381+09:00[UTC+09:00]
public static void main(String[] args) {
// 解析日期
String dateText = "20200420";
LocalDate date = LocalDate.parse(dateText, DateTimeFormatter.BASIC_ISO_DATE);
System.out.println("格式化后的日期: " + date);
// 格式化日期
dateText = date.format(DateTimeFormatter.BASIC_ISO_DATE);
System.out.println("dateText: " + dateText);
}
输出结果如下:
格式化后的日期: 2020-04-20
dateText: 20200420
public static void main(String[] args) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// 日期时间转字符串
LocalDateTime now = LocalDateTime.now();
String dateToStr = now.format(formatter);
System.out.println("日期转换成字符串: " + dateToStr);
// 字符串转日期时间
String datetimeText = "1999-12-31 23:59:59";
LocalDateTime strToDate = LocalDateTime.parse(datetimeText, formatter);
System.out.println("字符串转换成日期: " + strToDate);
}
输出结果如下:
日期转换成字符串: 2020-04-20 15:50:45
字符串转换成日期: 1999-12-31T23:59:59
public static void main(String[] args) {
// 日期转时间戳
System.out.println(LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli());
System.out.println(LocalDateTime.now().toInstant(ZoneOffset.ofHours(8)).toEpochMilli());
System.out.println(LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
// 时间戳转成日期
Long instantL = 1587367830000L;
Instant instant = Instant.ofEpochMilli(instantL);
LocalDateTime instantToDate = LocalDateTime.ofInstant(instant, ZoneId.of("+8"));
System.out.println("时间戳转换成日期: " + instantToDate);
}
输出结果如下:
1587371801887
1587371801888
1587371801888
时间戳转换成日期: 2020-04-20T15:30:30
public static void main(String[] args) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// 字符串转时间戳
String datetimeText = "2020-04-20 15:30:30";
LocalDateTime strToDate = LocalDateTime.parse(datetimeText, formatter);
System.out.println("字符串转换成日期: " + strToDate);
Long dateToInstant = strToDate.toInstant(ZoneOffset.of("+8")).toEpochMilli();
System.out.println("日期转换成毫秒时间戳: " + dateToInstant);
// 时间戳转换成字符串
Long instant = 1587367830000L;
LocalDateTime instantToDate = LocalDateTime.ofInstant(Instant.ofEpochMilli(instant), ZoneId.of("+8"));
System.out.println("时间戳转换成日期: " + instantToDate);
String dateToStr = formatter.format(instantToDate);
System.out.println("日期转换成字符串: " + dateToStr);
}
输出结果如下:
字符串转换成日期: 2020-04-20T15:30:30
日期转换成毫秒时间戳: 1587367830000
时间戳转换成日期: 2020-04-20T15:30:30
日期转换成字符串: 2020-04-20 15:30:30
TemporalAdjuster : 时间校正器。它是一个函数式接口,只有一个方法 adjustInto。有时我们可能需要获取例如:将日期调整到“下个周日”等操作。
TemporalAdjusters : 该类通过静态方法提供了大量的常用TemporalAdjuster 的实现。
Temporal 是一个接口,LocalDateTime 、LocalDate 、 LocalTime 都是它的实现类,有实现方法 with(TemporalAdjuster adjuster)。
@FunctionalInterface
public interface TemporalAdjuster {
Temporal adjustInto(Temporal temporal);
}
public static void main(String[] args) {
// 接下来第一个周五
LocalDateTime nextFriday = LocalDateTime.now().with(TemporalAdjusters.next(DayOfWeek.FRIDAY));
System.out.println("接下来第一个周五: " + nextFriday);
// 明年的感恩节(明年11月第四个星期四)
LocalDate date = LocalDate.now().plusYears(1).withMonth(11)
.with(TemporalAdjusters.dayOfWeekInMonth(4, DayOfWeek.THURSDAY));
System.out.println("明年感恩节: " + date);
}
输出结果如下:
接下来第一个周五: 2020-04-24T18:55:50.841
明年感恩节: 2021-11-25
注意: TemporalAdjusters.next(DayOfWeek day) 方法返回的是 接下来第一个周五,并不是我们一般理解的 下周五,比如说:今天 2020-01-13(周一),那么返回的就是 2020-01-17 四天后的周五。
Instant 时间戳
Duration 持续时间、时间差
LocalDate 只包含日期,比如:2018-09-24
LocalTime 只包含时间,比如:10:32:10
LocalDateTime 包含日期和时间,比如:2018-09-24 10:32:10
Peroid 时间段
ZoneOffset 时区偏移量,比如:+8:00
ZonedDateTime 带时区的日期时间
Clock 时钟,可用于获取当前时间戳
java.time.format.DateTimeFormatter 时间格式化类