1. JDK8 之前的日期时间API
1.1 java.lang.System类
System
类提供的public static long currentTimeMillis()
用于返回当前时间与1971年1月1日0时0分0秒之间以毫秒为单位的时间差,此方法适用于计算时间差。
计算世界时间的主要标准有:
- UTC(Coordinated Universal Time)
- GMT(Greenwich Mean Time)
- CST(Central Standard Time)
@Test
public void test() {
long time = System.currentTimeMillis();
System.out.println(time);
}
返回的是一个时间差,往往我们将其称为时间戳
1.2 java.util.Date类
表示特定的瞬间,精确到毫秒
构造器:
Date()
:使用无参构造器创建的对象可以获取本地当前时间Date(long date)
常用方法:
getTime()
:返回自1970年1月1日00:00:00 GMT以来此Date
对象表示的毫秒数toString()
:把此Date
对象转换为一下形式的String
:dow mon dd hh:mm:ss zzz yyyy
其中,dow
是一周中的某一天(Sun, Mon, Tue, Wed, Thu, Fri, Sat
),zzz
是时间标准- 其他方法大多数都过时了
@Test
public void test2() {
// 两个构造器的使用
Date date = new Date();
System.out.println("date: " + date);
System.out.println("date.getTime(): " + date.getTime());
Date date1 = new Date(0);
System.out.println("date1: " + date1);
}
运行结果:
date: Wed Mar 17 21:52:49 CST 2021 date.getTime(): 1615989169194 date1: Thu Jan 01 08:00:00 CST 1970
1.3 java.sql.Date类
java.sql.Date
对应着数据库中日期类型的变量,是java.util.Date
类的子类
@Test
public void test3() {
java.sql.Date date = new java.sql.Date(3547857487584L);
System.out.println(date);
}
运行结果:
2082-06-05
如何将java.util.Date
对象转换为java.sql.Date
对象:
@Test
public void test4() {
// 情况一:
Date date1 = new java.sql.Date(2735648723645L);
java.sql.Date date2 = (java.sql.Date) date1;
System.out.println("date1: " + date1);
System.out.println("date2: " + date2);
// 情况二:
Date date3 = new Date();
java.sql.Date date4 = new java.sql.Date(date3.getTime());
System.out.println("date3: " + date3);
System.out.println("date4: " + date4);
}
运行结果:
date1: 2056-09-08 date2: 2056-09-08 date3: Wed Mar 17 22:04:50 CST 2021 date4: 2021-03-17
由于
java.sql.Date
是java.util.Date
的子类,所以情况一使用了面向对象的多态,但是对于情况二,只能使用构造器的方式转换
1.4 SimpleDateFormat类
Date
类的API不易于国际化,因此大部分都被废弃了,java.text.SimpleDateFormat
类是一个不与语言环境有关的方式来格式化和解析日期的具体类。- 它允许进行格式化:日期->文本、解析:文本->日期
格式化:
SimpleDateFormat()
:默认的模式和语言环境创建对象public SimpleDateFormat(String pattern)
:该构造方法可以用参数pattern
指定的格式创建一个对象,该对象调用public String format(Date date)
方法格式化时间对象date
解析:
public Date parse(String source)
:从给定字符串的开始解析文本,以生成一个日期。
@Test
public void testSimpleDateFormat() throws ParseException {
// 实例化-默认构造器
SimpleDateFormat simpleDateFormat = new SimpleDateFormat();
// 格式化:日期 ---> 字符串
Date date = new Date();
System.out.println(simpleDateFormat.format(date));
// 解析:格式化的逆过程,字符串 ---> 日期
String str = "21-3-18 下午4:08";
System.out.println(simpleDateFormat.parse(str));
// 实例化-指定格式构造器
SimpleDateFormat simpleDateFormat1 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
// 格式化
System.out.println(simpleDateFormat1.format(date));
// 解析
String str2 = "2021-03-18 04:17:09";
System.out.println(simpleDateFormat1.parse(str2));
}
运行结果:
21-3-18 下午4:17 Thu Mar 18 16:08:00 CST 2021 2021-03-18 04:17:17 Thu Mar 18 04:17:09 CST 2021
1.4 Calendar 日历类
Calendar
是一个抽象基类,主要用于完成日期字段之间互相操作的功能获取
Calendar
实例的方法- 使用
Calendar.getInstance()
方法 - 调用它的子类
GregorianCalendar
的构造器
- 使用
一个
Calendar
的实例是系统时间的抽象表示,通过get(int field)
方法来取得想要的时间信息。如YEAR
、MONTH
、DAT_OF_WEEK
、HOUR_OF_DAY
、MINUTE
、SECOND
、public void set(int field, int value)
public void add(int field, int amount)
public final Date getTime()
public final void setTime(Date date)
注意:
- 获取月份时:一月份是
0
,二月是1
,······,十二月是11
- 获取星期时:周日是
1
,周一是2
,······,周六是7
- 获取月份时:一月份是
@Test
public void test3() {
// 实例化
Calendar calendar = Calendar.getInstance();
// get()
System.out.println("calendar.get(Calendar.DAY_OF_MONTH): " + calendar.get(Calendar.DAY_OF_MONTH));
System.out.println("calendar.get(Calendar.DAY_OF_YEAR): " + calendar.get(Calendar.DAY_OF_YEAR));
// set()
calendar.set(Calendar.DAY_OF_YEAR, 20);
System.out.println("calendar.get(Calendar.DAY_OF_YEAR): " + calendar.get(Calendar.DAY_OF_YEAR));
// add()
calendar.add(Calendar.DAY_OF_YEAR, 3);
System.out.println("calendar.get(Calendar.DAY_OF_YEAR): " + calendar.get(Calendar.DAY_OF_YEAR));
calendar.add(Calendar.DAY_OF_YEAR, -3);
System.out.println("calendar.get(Calendar.DAY_OF_YEAR): " + calendar.get(Calendar.DAY_OF_YEAR));
// getTime()
Date date = calendar.getTime();
System.out.println("date: " + date);
// setTime()
calendar.setTime(new Date());
System.out.println("calendar: " + calendar.get(Calendar.DAY_OF_YEAR));
}
运行结果:
calendar.get(Calendar.DAY_OF_MONTH): 18 calendar.get(Calendar.DAY_OF_YEAR): 77 calendar.get(Calendar.DAY_OF_YEAR): 20 calendar.get(Calendar.DAY_OF_YEAR): 23 calendar.get(Calendar.DAY_OF_YEAR): 20 date: Wed Jan 20 17:56:24 CST 2021 calendar: 77
2. JDK8中日期时间API介绍
2.1 出现的背景
JDK1.0中引入的java.util.Date
类,在JDK1.1中引入Calendar
类后大多数方法已经被弃用,而Calendar
类仍然面临很多问题:
- 可变性:像日期和时间这样的类应该是不可变的
- 偏移性:
Date
中的年份从1990年开始,而月份都从0开始 - 格式化:格式化只对
Date
有用,Calendar
则不行 - 此外,它们也不是线程安全的,且不能处理闰秒
日期和时间的操作一直是Java程序员最痛苦的地方之一
- 第三次引入时间API是成功的,并且Java8中引入的
java.time
API纠正了过去的缺陷,将来很长一段时间内我们都将使用该API - Java8吸收了 Joda-Time 的精华,以一个新的开始为 Java 创建优秀的 API。新的
java.time
API 中包含了所有关于本地日期、本地时间、本地日期时间、时区和持续时间的类。历史悠久的Date
类新增了toInstant()
方法,用于把Date转换成新的表示形式。这些新增的本地户时间日期API大大简化了日期时间和本地化的管理
2.2 新时间日期API
java.time
:包含值对象的基础包java.time.chrono
:提供不同的日历系统的访问java.time.format
:格式化和解析时间和日期java.time.temporal
:包括底层框架和扩展特性java.time.zone
:包含时区支持的类
大多数开发者只会用到基础包和format
包,也可能会用到temporal
包。因此,尽管有68个新的公开类型,对大多数开发者而言,一般只用到其中的三分之一
2.3 LocalDate、LocalTime、LocalDateTime的使用
方法 | 描述 |
---|---|
now() 、now(Zoned zone) | 静态方法,根据当前时间创建对象/指定时区的对象 |
of() | 静态方法,根据指定日期时间创建对象 |
getDayOfMonth 、getDayOfYear() | 获得月份天数(1~31),获得年份天数(1~366) |
getDayOfWeek() | 获得星期几(返回一个DayOfWeek 枚举值) |
getMonth() | 获得月份,返回一个Month 枚举值 |
getMonthValue() 、getDayOfYear() 、getYear() | 获得月份(1~12),获得年份 |
getHour() 、getDayOfYear() 、getMinute() 、getDayOfYear() 、getSecond() | 获得当前对象对应的小时、分钟、秒 |
withDayOfMonth() 、withDayOfYear() 、withMonthOfYear() 、withYear() | 将月份天数、年份天数、月份、年份修改为指定的值并返回新对象 |
plusDay() 、plusWeeks() 、plusMonths() 、plusYears() 、plusHours() | 向当前对象添加几天、几周、几个月、几年、几小时 |
minusMonths() ,minusWeeks() 、minusDays() 、minusYears() 、minusHours() | 从当前对象减去几月、几周、几天、几年、几小时 |
@Test
public void test() {
LocalDate localDate = LocalDate.now();
LocalTime localTime = LocalTime.now();
LocalDateTime localDateTime = LocalDateTime.now();
System.out.println("localDate: " + localDate);
System.out.println("localTime: " + localTime);
System.out.println("localDateTime: " + localDateTime);
LocalDate localDate1 = LocalDate.of(2020, 2, 18);
LocalTime localTime1 = LocalTime.of(18, 30);
LocalDateTime localDateTime1 = LocalDateTime.of(2020, 3, 18, 16, 6, 3);
System.out.println("localDate1: " + localDate1);
System.out.println("localTime1: " + localTime1);
System.out.println("localDateTime1: " + localDateTime1);
}
运行结果:
localDate: 2021-03-18 localTime: 18:50:05.300 localDateTime: 2021-03-18T18:50:05.300 localDate1: 2020-02-18 localTime1: 18:30 localDateTime1: 2020-03-18T16:06:03
2.4 Instant类的使用
Instant
:时间线上的一个瞬时点。这可能被用来记录应用程序中的时间时间戳- 在处理时间和日期的时候,我们通常会想到年、月、日、时、分、秒。然而,这只是时间的一个模型,是面向人类的。第二种通用模型是面向机器的,或者说是连续的。在此模型中,时间线中的一个点表示为一个很大的数,这有利于计算机处理。在UNIX中,这个数从1970年开始,以秒为单位;同样的,在Java中,也是从1970年开始,但以毫秒为单位。
java.time
包通过值类型Instant
提供机器视图,不提供处理人类意义上的时间单位。Instant
表示时间线上的一点,而不需要任何上下文信息,例如:时区。从概念上讲,它只是简单地表示自1970年1月1日0时0分0秒(UTC)开始的秒数。因为java.time
包是基于纳秒计算的,所以Instant
的精度可以达到纳秒级- (1ns = 10^-9s) 1秒 = 1000毫秒 = 10^6 微秒 = 10^9纳秒
方法 | 描述 |
---|---|
now() | 静态方法,返回默认UTC时区的Instant 类的对象 |
ofEpochMilli(long epochMilli) | 静态方法,返回在1970-01-01 00:00:0基础上加上指定毫秒数之后的Instant 类对象 |
atOffset(ZoneOffset offset) | 结合即时的偏移创建一个OffsetDateTime |
toEpochMilli() | 返回1970-01-01 00:00:00到当前时间的毫秒数,即为时间戳 |
@Test
public void test1() {
// 获取实例
Instant instant = Instant.now(); // 默认UTC时间
System.out.println("instant: " + instant);
// 添加时间偏移量
OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8));// 东八区(+8)时间
System.out.println("offsetDateTime: " + offsetDateTime);
// 获取毫秒数
long milli = instant.toEpochMilli();
System.out.println("milli: " + milli);
// 通过给定毫秒数获取一个Instant实例
Instant instant1 = Instant.ofEpochMilli(1616067858186L);
System.out.println("instant1: " + instant1);
}
运行结果
instant: 2021-03-18T11:46:03.575Z offsetDateTime: 2021-03-18T19:46:03.575+08:00 milli: 1616067963575 instant1: 2021-03-18T11:44:18.186Z
2.5 DateTimeFormatter类的使用
java.time.format.DateTimeFormatter
类提供了三种格式化方法:
- 预定义的标准格式。如:
ISO_LOCAL_DATE_TIME
、ISO_LOCAL_DATE
、ISO_LOCAL_TIME
- 本地化相关的格式。如:
ofLocalizedDateTime(FormatStyle.LONG)
- 自定义格式。如:
ofPattern("yyyy-MM-dd hh:mm:ss E")
方法 | 描述 |
---|---|
ofPattern(String pattern) | 静态方法,返回一个指定字符串格式的DateTimeFormatter |
format(TemporalAccessor t) | 格式化一个日期、时间。返回字符串 |
parse(CharSequence text) | 将指定格式的字符序列解析为一个日期、时间 |
@Test
public void test2() {
// 实例化方式一:使用标准格式
DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
System.out.println(formatter.format(LocalDateTime.now()));
TemporalAccessor parse = formatter.parse("2021-03-18T20:00:04.94");
System.out.println(parse);
// 实例化方式二:本地化相关格式
DateTimeFormatter formatter1 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT);
System.out.println(formatter1.format(LocalDateTime.now()));
// 实例化方式三:自定义格式
DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
System.out.println(formatter2.format(LocalDateTime.now()));
TemporalAccessor parse1 = formatter2.parse("2021-03-18 08:06:36");
System.out.println(parse1);
}
运行结果:
2021-03-18T20:07:23.296 {},ISO resolved to 2021-03-18T20:00:04.940 21-3-18 下午8:07 2021-03-18 08:07:23 {HourOfAmPm=8, MilliOfSecond=0, MinuteOfHour=6, MicroOfSecond=0, NanoOfSecond=0, SecondOfMinute=36},ISO resolved to 2021-03-18
2.6 其它日期时间相关API
Zoneld
:该类中包含了所有的时区信息,一个时区的ID,如Europe/Paris
ZonedDateTime
:一个在ISO-8601日历系统时区的日期时间,如2007-12-03T10:15:30+1:00 Europe/Paris
,其中每个时区都有对应的ID,地区ID都为{Area}/{City}
格式Clock
:使用时区提供对当前即时、日期和时间的访问的时钟Duration
:持续时间,用于计算两个”时间“间隔Period
:日期间隔,用于计算两个”日期“间隔TemporalAdjuster
:时间矫正器。有时我们可能需要获取例如:将日期调整到”下一个工作日“等操作TemporalAdjusters
:该类通过静态方法firstDayOfXxx()
、lastDayOfXxx()
、nextXxx()
提供了大量常用TemporalAdjuster
的实现