写在前面
如果觉得有所收获,写的还不错,记得点个赞或者点个关注哦,不胜感激。我们使用Java的时候,经常会用到的一个类就是时间类,但是很多时候我们只是匆匆而过,并没有深入去了解时间类。在我开发历程中,碰到了许多有关时间类的问题,所以今天抽空,把Java的时间类阐述总结一下,希望看完这篇的小伙伴能有所收获。本篇文章分四个部分讲,分别是 Java 基础时间类
、Java sql 时间类
、Joda Time 时间类
、 java.time 时间类
。
文章目录
Java 基础时间类
在Java8之前对日期、日历及时间的处理一直饱受诟病,尤其是它决定将java.util.Date
定义为可修改的以及将SimpleDateFormat
实现成非线程安全的。直到Java8 大版本的发布,才完善了相关类。这里简单说一下这两个包,java.util
包是 Java 的实用工具类库包,包含 Java 的集合类、时间类、事件模型类等;java.text
包是跟文本、格式化打交道的包,比如处理时间、数字等。这两个包都是 Java 很老的基础包。
Date 类
Date 类是 Java 里时间类中老资历了,所以在讲其他的API之前呢,这里先要来讲讲这个类。Date类在保留着日期和时间最基本用法的同时,总能用其偏执的使用思路让人觉得拧巴。但是在程序员懒得找其他替代品时,它又几乎是第一选择,因为它的确是足够基础,当然了,使用时还要容忍它的反人类。如下是Date的源码,我们可以看到Date 类有两种构造函数,一种是获取当前时间,一种是根据你的输入时间来实例化时间,注意了输入的时间是时间戳,类型用的是long
。
Date 类有很多方法,大部分均已标记 @Deprecated
(已废弃),剩下的几个基本只有一个方法能用:getTime()
,而这个方法非常硬核,它 get 到的 Time 不是年月日,而是一个 long
类型的毫秒数,表示在格林尼治时间 1900 年开始之后,经过了多少毫秒。
Date date = new Date();
System.out.println(date.getTime());
//控制台输出:1585825773101
这里要吐槽一下Date类的一些设计,让人抓狂的一些地方
- Date 类计算年份时,是跟 1900 年进行比较的,比如今年(2020 年)对于 Date 类而言,它是 119 年,当调用
getYear()
方法时(该方法已被官方建议不要使用),它会说这是 119 年。 - Date 类计算月份是,是从 0 开始计算的,因此这个月(4 月)对于 Date 类而言,它是 3 。
getDay()
方法是获取当周的第几天, getDate() 方法是获取当月的第几天(这两个方法也已被遗弃)。- Date 类自己的一切
getXXX()
方法,实际上只剩下了getTime()
方法,表示获取当前时间(精确到毫秒)与 1900 年 1 月 1 日 0 时 0 分 0 秒这一瞬间(包含时差),中间间隔的毫秒数,其他获取年月日等时间的方法均被遗弃,官方建议使用 Calendar 类。 - Date 类的
getTime()
方法是指获取毫秒数,比如现在时间的 getTime() 的值为 1585825773101 ,太硬核了。更硬核的是,Date 类的构造函数只剩下两种能用,一种是无参构造(得到当下的时间),另一种是以刚才的那个毫秒数为参数。 - Date 类的值不是 final 的,是可以在实例化之后通过
setTime()
改变值的,非线程安全。 - 当调用 Date 类的
toString()
方法时,它会打印出例如Thu Apr 02 19:14:14 CST 2020
(中国标准时间 2020 年 4 月 02 日 19 时 14 分 14 秒)的值,你发现这里包含时区信息 CST,但令人无语的是,这个时区信息是 Date 类在调用toString()
方法时,根据系统时区动态打印的。换句话说,刚才那个时间的程序在中国执行,时区是 CST,在美国执行,那时区就是 PDT。
Calendar
calendar 是日历的意思,因此见名知意,Calendar 类是用来跟年月日等时间打交道的类。Calendar 类本身是一个抽象类,它代表着日历类的标准与规范,有 GregorianCalendar 类(格林尼治日历时间)等实现类。实例化一个 Calendar 类,如果不使用子类,那就要通过工厂方法获得了。
// Calendar类的实例化方法
Calendar calendar = Calendar.getInstance();
// 下面这种是错误的
Calendar calendar = new Calendar();
这里通过 Calendar.getInstance() 获得的,是一个 GregorianCalendar 对象。不过也会根据其他的一些信息返回其他对象,具体可以查看源码,这里贴一部分出来
Calendar 类最实用的方法是它的 get() 方法,用这个方法获取年、月、日、周、小时等等 17 类不同的信息。
// 获取calendar的年份信息 如2020
int year = calendar.get(Calendar.YEAR);
// 获取calendar的月份信息 如9
int month = calendar.get(Calendar.MONTH);
// 获取calendar的当月天数信息,如10
int date = calendar.get(Calendar.DATE);
上面诸如 Calendar.YEAR
之类的值,其实是 Calendar 类定义的常量值,Calendar.YEAR
其实就是 1,换句话说,下面两行代码是一样的:
int year1 = calendar.get(Calendar.YEAR);
int year2 = calendar.get(1);
通过 Calendar 类的 get()
方法能得到 17 类不同的时间信息,这 17 个常量值列在下面:
Calendar 的“年、月、日、星期、时、分、秒”这些信息,一共是17个字段。
我们使用Calendar,无非是就是使用这17个字段。它们的定义如下:
(字段0) public final static int ERA = 0;
说明:纪元。
取值:只能为0 或 1。0表示BC(“before Christ”,即公元前),1表示AD(拉丁语“Anno Domini”,即公元)。
(字段1) public final static int YEAR = 1;
说明:年。
(字段2) public final static int MONTH = 2;
说明:月
取值:可以为,JANUARY, FEBRUARY, MARCH, APRIL, MAY, JUNE, JULY, AUGUST, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER, UNDECIMBER。
其中第一个月是 JANUARY,它为 0。
(字段3) public final static int WEEK_OF_YEAR = 3;
(字段4) public final static int WEEK_OF_MONTH = 4;
说明:当前日期在本月中对应第几个星期。一个月中第一个星期的值为 1。
(字段5) public final static int DATE = 5;
说明:日。一个月中第一天的值为 1。
(字段5) public final static int DAY_OF_MONTH = 5;
说明:同“DATE”,表示“日”。
(字段6) public final static int DAY_OF_YEAR = 6;
说明:当前日期在本年中对应第几天。一年中第一天的值为 1。
(字段7) public final static int DAY_OF_WEEK = 7;
说明:星期几。
取值:可以为,SUNDAY、MONDAY、TUESDAY、WEDNESDAY、THURSDAY、FRIDAY 和 SATURDAY。
其中,SUNDAY为1,MONDAY为2,依次类推。
(字段8) public final static int DAY_OF_WEEK_IN_MONTH = 8;
说明:当前月中的第几个星期。
(字段9) public final static int AM_PM = 9;
说明:上午 还是 下午
取值:可以是AM 或 PM。AM为0,表示上午;PM为1,表示下午。
(字段10) public final static int HOUR = 10;
说明:指示一天中的第几小时。
HOUR 用于 12 小时制时钟 (0 - 11)。中午和午夜用 0 表示,不用 12 表示。
(字段11) public final static int HOUR_OF_DAY = 11;
说明:指示一天中的第几小时。
HOUR_OF_DAY 用于 24 小时制时钟。例如,在 10:04:15.250 PM 这一时刻,HOUR_OF_DAY 为 22。
(字段12) public final static int MINUTE = 12;
说明:一小时中的第几分钟。
例如,在 10:04:15.250 PM这一时刻,MINUTE 为 4。
(字段13) public final static int SECOND = 13;
说明:一分钟中的第几秒。
例如,在 10:04:15.250 PM 这一时刻,SECOND 为 15。
(字段14) public final static int MILLISECOND = 14;
说明:一秒中的第几毫秒。
例如,在 10:04:15.250 PM 这一时刻,MILLISECOND 为 250。
(字段15) public final static int ZONE_OFFSET = 15;
说明:毫秒为单位指示距 GMT 的大致偏移量。
(字段16) public final static int DST_OFFSET = 16;
说明:毫秒为单位指示夏令时的偏移量。
public final static int FIELD_COUNT = 17;
这17个字段是保存在int数组中。定义如下:
// 保存这17个字段的数组
protected int fields[];
// 数组的定义函数
protected Calendar(TimeZone zone, Locale aLocale)
{
// 初始化“fields数组”
fields = new int[FIELD_COUNT];
isSet = new boolean[FIELD_COUNT];
stamp = new int[FIELD_COUNT];
this.zone = zone;
setWeekCountData(aLocale);
}
protected Calendar(TimeZone zone, Locale aLocale) 这是Calendar的构造函数。
它会被它的子类的构造函数调用到,从而新建“保存Calendar的17个字段数据”的数组。
但让人依旧无语的是,Calendar 类的月份也是从 0 开始的。此外 Calendar 类不支持格式化。
SimpleDateFormat
SimpleDateFormat 类是一个【格式化】和【解析日期】的工具类,即 Date 转 Text 或者 Text 转 Date,而且能够按照要求格式转换,如输出 2019-09-10 12:00:00
这种时间文本。下面就是最常见的用法,声明好格式之后,使用 format()
方法把时间转换成字符串。
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss zzz");
String dateStr = simpleDateFormat.format(date);
// dateStr : "2020-04-02 22:55:42 CST"
另一种常见的用法是把字符串转换成时间,但是要异常处理,毕竟是处理字符串。
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss zzz");
try {
Date date = simpleDateFormat.parse("2020-04-02 22:55:42 CST");
} catch (ParseException e) {
e.printStackTrace();
}
// date.toString() : Fou Apr 26 22:55:42 CST 2020
字母 | 日期或时间元素 | 表示 | 示例 |
---|---|---|---|
G | Era 标志符 | Text | AD |
y | 年 | Year | 1996; 96 |
M | 年中的月份 | Month | July; Jul; 07 |
w | 年中的周数 | Number | 27 |
W | 月份中的周数 | Number | 2 |
D | 年中的天数 | Number | 189 |
d | 月份中的天数 | Number | 10 |
F | 月份中的星期 | Number | 2 |
E | 星期中的天数 | Text | Tuesday; Tue |
a | Am/pm 标记 | Text | PM |
H | 一天中的小时数 | (0-23) | Number |
k | 一天中的小时数(1-24) | Number | 24 |
K | am/pm 中的小时数(0-11) | Number | 0 |
h | am/pm 中的小时数(1-12) | Number | 12 |
m | 小时中的分钟数 | Number | 30 |
s | 分钟中的秒数 | Number | 55 |
S | 毫秒数 | Number | 978 |
z | 时区 | General time zone | Pacific Standard Time; PST; GMT-08:00 |
Z | 时区 | RFC 822 time zone | -0800 |
虽然使用 SimpleDateFormat 类能进行文本处理了,但是使用起来还是挺不方便的,而且还要考虑异常处理,此外它还线程不安全。
java.sql 包中的时间类
在 java 中有一个与数据库相对应的类包,是 java.sql
包,该包下有三个对应数据库时间类型的类,分别是 Date 类、 Time 类和 TimeStamp 类
,可以这么说,个人感觉这是三个废物类,一无是处。这三个类是与数据库中的时间数据类型完全对应的:
数据库的时间类型 | java.sql 时间类型 | 示例 |
---|---|---|
DATE | Date | 2019-09-01 |
TIME | Time | 12:00:00 |
TIMESTAMP | Timestamp | 2019-09-01 12:00:00.000 |
这三个类废物,不是没有根据的:
- 这三个时间类的构造方法都只有一种(另一种官方废弃不建议使用),那就是【当下时间距离 1970 年 1 月 1 日 0 时 0 分 0 秒 0 毫秒】的毫秒数,反人类。
- 这三个时间类没有诸如 getDate() 之类获取时间的方法,它们有什么方法呢,只有转换成别的时间类的方法 :)
- 除了这三个类之外,别的时间类也可以对接数据库。
Joda Time 时间类
从上面两部分的时间类看得出,用 JDK 自带的时间类编程,还是比较痛苦的:其一,想完成某个需求,可能需要好几个时间类同时使用;其二,上述时间类还存在着许多例如月份从 0 开始计数、时区信息伪造等暗坑。这种痛苦的局面在 JDK 8 得到了解决,因为 JDK 8 设计了全新的时间类,但是低版本的 JDK 依旧痛苦。想解决这种痛苦,就要使用第三方类库,Joda Time 就是一个优秀的第三方时间类库。
org.joda.time
包下的类,大致可以分为三种:
- 时间类(类似于上文的 Date 类)
- 格式化与解析类(类似于上文的 SimpleDateFormat 类)
- 时间跨度类
时间类就是真正用来记录如 2019-10-07 22:48:03
这类时间的类,格式化与解析类是把时间类型和字符串类型进行相互转换的类,时间跨度类是记录如 2年零3个月 这类间隔时间的类。接下来逐个类型讲解。
Joda Time 的时间类
首先要注意:Joda Time 所设计的时间类,统统都不可改变(immutable),跟 String 是一样的,一经实例化,不得改变其值,从源头上实现了线程安全。当需要改变时间时,Joda Time 会返回一个全新的 Joda 实例,也跟 String 的设计是一样的。org.joda.time
包下有五个常用的时间类,以表格形式列在下面:
类名 | 作用 | 示例(2019 年 10 月 1 日 12 时整) |
---|---|---|
DateTime | 日期+时间(含时区信息) | 2019-10-01T00:00:00.000+08:00 |
Instant | 日期+时间(格林威治时间,存疑) | 2019-10-01T12:00:00.000Z |
LocalDateTime | 日期+时间(不含时区信息) | 2019-10-01T12:00:00.000 |
LocalDate | 日期(不含时区信息) | 2019-10-01 |
LocalTime | 时间(不含时区信息) | 12:00:00.000 |
Instant 类是指时间轴上一个确定的时间点(精确到微妙),但是我自认为用处实在是不多,还是其他四个类:DateTime 类、LocalDateTime 类、LocalDate 类、LocalTime 类使
用比较频繁,如果需要时区信息则使用第一个,如果不需要时区信息,那就使用后面三个以“Local”开头的类。以上五个时间类,使用方法可以用随心所欲来形容,你想怎么用就怎么用。以 DateTime 类为例,介绍 Joda Time 时间类的主要用法:
- 得到一个时间对象,有超级多的方法,以下只列举几种:
// 当前时间
DateTime dateTime1 = new DateTime();
// 任意一种时间类的实例,自动转换
DateTime dateTime2 = new DateTime(dateTime1);
// 手动填写年月日时间等信息,有9种填写规则
DateTime dateTime3 = new DateTime(2020, 04, 2, 12, 0);
// 自1970年1月1日0日整之后的毫秒数
DateTime dateTime4 = new DateTime(1585825773101L);
// 使用静态方法,读取一个字符串转换成时间
DateTime dateTime5 = DateTime.parse("2020-04-02 T12:00:00.000+08:00");
- 获取时间信息,同样有超级多的方法,以下只列举几种:
// 获取当前月份
int monthOfYear = dateTime.getMonthOfYear();
// 获取当天过了多少秒
int secondOfDay = dateTime.getSecondOfDay();
// 获取自1970年1月1日0时之后过了多少微秒
long millis = dateTime.getMillis();
- 修改时间信息(会返回一个全新的 DateTime 实例),再次有超级多的方法,以下只列举几种:
// 天数 + 1(plus)
DateTime plusDateTime = dateTime.plusDays(1);
// 小时数 - 10(minus)
DateTime minusDateTime = dateTime.minusHours(2);
// 设置月份为 8 月(with)
DateTime withDateTime = dateTime.withMonthOfYear(8);
- 其他操作
// 该时间是否比当下时间早
boolean beforeNow = dateTime.isBeforeNow();
// 对比另一个时间(随意一个时间类的实例),判断是否在其之后
boolean afterTime = dateTime.isAfter(dateTime2);
// 转换成废物的java.util.date类
Date date = dateTime.toDate();
你只要知道,Joda Time 几乎无所不能,使用时随心所欲,就可以了。
Joda Time 的格式化与解析类
Joda Time 的格式化与解析类,常用的有三个类,分别是 DateTimeFormatter 类、DateTimeFormat 类和 DateTimeFormatterBuilder 类。其实不使用这三个类,也可以实现时间解析与格式化处理,直接用字符串指定好样式就可以了,使用这三个类是为了简便和统一操作。
DateTimeFormatter 类,时间解析与格式化处理类,用于日期和时间与字符串之间的转换。当时间类 -> 字符串时,使用时间类的toString(DateTimeFormatter formatter)
方法。
DateTimeFormatter dateTimeFormatter = DateTimeFormat.fullDateTime();
String dateTimeStr = dateTime.toString(dateTimeFormatter);
// dateTimeStr: "2020年04月2日 星期四 下午01时00分00秒 CST"
当字符串 -> 时间类时,使用时间类的静态方法parse(String str, DateTimeFormatter formatter)
。
DateTime dateTime = DateTime.parse("2020年04月2日 星期四 下午01时00分00秒 CST", dateTimeFormatter);
// dateTime.toString: "2020-04-02 T13:00:00.000-05:00"
DateTimeFormat 类,这是 DateTimeFormatter 类的工厂类,提供各种创建 DateTimeFormatter 类的实例方法。
工厂方法名 | 示例( 2019 年 10 月 1 日 13 时整) |
---|---|
fullDateTime() | 2019年10月1日 星期二 下午01时00分00秒 CST |
fullDate() | 2019年10月1日 星期二 |
fullTime() | 下午01时00分00秒 CST |
longDateTime() | 2019年10月1日 下午01时00分00秒 |
longDate() | 2019年10月1日 |
longTime() | 下午01时00分00秒 |
mediumDateTime() | 2019-10-1 13:00:00 |
mediumDate() | 2019-10-1 |
mediumTime() | 13:00:00 |
shortDateTime() | 19-10-1 下午01:00 |
shortDate() | 19-10-1 |
shortTime() | 下午01:00 |
forPattern(String pattern) | 自定义格式 |
forStyle(String style) | 按样式,建议阅读官方API |
patternForStyle(String style, Locale locale) | 根据地区告知样式内容,返回一个样式字符串如样式是”MM”且地区为中国时,返回:yyyy’年’M’月’d’日’ EEEE ahh’时’mm’分’ss’秒’ z |
DateTimeFormatterBuilder 类,这个类是用作生成复杂时间样式的类,可以自由拼接时间,自由指定间隔样式等等,例如“十月 01 日 星期二 下午”。这个类本身是可以改变的(非线程安全),但是它可以转换成 DateTimeFormatter 类,此时就是不能改变的(线程安全)。本类的操作跟 StringBuilder 类几乎是一致的,使用场景不多,用起来也比较顺手,只贴出一段代码示例。
DateTimeFormatterBuilder dateTimeFormatterBuilder = new DateTimeFormatterBuilder()
.appendEraText() // 纪元(由于是Text,不需要自己指定精度)
.appendLiteral(' ') // 分隔(此处用空格分隔)
.appendYear(4, 4) // 年(参数表示数字精度,最少4位,最多四位)
.appendLiteral(' ')
.appendMonthOfYear(2) // 月份
.appendLiteral(' ')
.appendDayOfMonth(2) // 日
.appendLiteral(' ')
.appendDayOfWeekText() // 周几
.appendLiteral(" 该天已度过") // 分隔(此处用语句分隔)
.appendFractionOfDay(2, 2) // 当天已过去多少百分比
.appendLiteral("%");
DateTimeFormatter dateTimeFormatter = dateTimeFormatterBuilder.toFormatter();
// 打印示例:公元 2020 04 02 星期四 该天已度过54%
Joda Time 的时间跨度类
Joda Time 设计了三个类,用来表示时间跨度,分别是 Duration 类、Period 类和 Interval 类
。
- Duration 类保存了一个精确的毫秒数,比如你设置它为一天,它会记录下这是 86400 秒(如有毫秒会精确到小数点后三位)。
- Period 类保存了一段时间,例如 1 年 10 个月 1 小时 1 毫秒(它记录成 P1Y10MT-1H-0.001S)
- Interval 类保存了一个开始时刻和一个结束时刻,因而也能够表示一段时间。
java.time 包中的时间类
当我们迎来 JDK 8 时,我们再也不需要 Joda Time 之类的第三方类库了,因为官方给我们提供了全新的时间类,这些类都属于 java.time
包下。当一个个类接触下去之后,你会发现 JDK 8 所提供的 java.time
包中的时间类,和 Joda Time 是何其相似,相似到你觉得简直像是 Joda Time 备胎转正,我猜想这跟 Joda Time 的作者参与到 java.time
包的开发中有关。最主要的的区别只有一处,那就是:JDK 8 的 java.time
包中的时间类,不再具有 public
的构造方法,而只有类静态方法。也就是说,通过类构造方法实例化对象,是错误的,需要使用如下方法实例化:
// 静态方法 now() 当前时间
LocalDate localDate = LocalDate.now();
// 静态方法 of() 构造时间
LocalDate localDate = LocalDate.of(2019, 10, 1);
java.time 包中各类使用示例
- Java 8中获取今天的日期,Java 8 中的 LocalDate 用于表示当天日期。和
java.util.Date
不同,它只有日期,不包含时间。当你仅需要表示日期时就用这个类。
public static void main(String[] args) {
LocalDate today = LocalDate.now();
System.out.println("今天的日期:"+today);
int year = today.getYear();
int month = today.getMonthValue();
int day = today.getDayOfMonth();
System.out.println("year:"+year);
System.out.println("month:"+month);
System.out.println("day:"+day);
}
- Java 8中处理特定日期,我们通过静态工厂方法
now()
非常容易地创建了当天日期,你还可以调用另一个有用的工厂方法LocalDate.of()
创建任意日期, 该方法需要传入年、月、日做参数,返回对应的LocalDate实例。这个方法的好处是没再犯老API的设计错误,比如年度起始于1900,月份是从0开 始等等。
public static void main(String[] args) {
LocalDate date = LocalDate.of(2018,2,6);
System.out.println("自定义日期:"+date);
}
- Java 8中判断两个日期是否相等
public static void main(String[] args) {
LocalDate date1 = LocalDate.now();
LocalDate date2 = LocalDate.of(2018,2,5);
if(date1.equals(date2)){
System.out.println("时间相等");
}else{
System.out.println("时间不等");
}
}
- Java 8中检查像生日这种周期性事件,只要当天的日期和生日匹配,无论是哪一年都会打印出祝贺信息。你可以把程序整合进系统时钟,看看生日时是否会受到提醒,或者写一个单元测试来检测代码是否运行正确。
//import java.time.LocalDate;
//import java.time.MonthDay;
public static void main(String[] args) {
LocalDate date1 = LocalDate.now();
LocalDate date2 = LocalDate.of(2020,4,2);
MonthDay birthday = MonthDay.of(date2.getMonth(),date2.getDayOfMonth());
MonthDay currentMonthDay = MonthDay.from(date1);
if(currentMonthDay.equals(birthday)){
System.out.println("是你的生日");
}else{
System.out.println("你的生日还没有到");
}
}
- Java 8中获取当前时间,通过增加小时、分、秒来计算将来的时间很常见。Java 8除了不变类型和线程安全的好处之外,还提供了更好的
plusHours()
方法替换add()
,并且是兼容的。注意,这些方法返回一个全新的LocalTime实例,由于其不可变性,返回后一定要用变量赋值。
public static void main(String[] args) {
LocalTime time = LocalTime.now();
LocalTime newTime = time.plusHours(3);
System.out.println("三个小时后的时间为:"+newTime);
}
- Java 8如何计算一周后的日期,和上个例子计算3小时以后的时间类似,这个例子会计算一周后的日期。LocalDate日期不包含时间信息,它的
plus()
方法用来增加天、周、月,ChronoUnit类声明了这些时间单位。由于LocalDate也是不变类型,返回后一定要用变量赋值。可以看到新日期离当天日期是7天,也就是一周。你可以用同样的方法增加1个月、1年、1小时、1分钟甚至一个世纪,更多选项可以查看Java 8 API中的ChronoUnit类。
public static void main(String[] args) {
LocalDate today = LocalDate.now();
System.out.println("今天的日期为:"+today);
LocalDate nextWeek = today.plus(1, ChronoUnit.WEEKS);
System.out.println("一周后的日期为:"+nextWeek);
}
- Java 8计算一年前或一年后的日期,利用
minus()
方法计算一年前的日期
public static void main(String[] args) {
LocalDate today = LocalDate.now();
LocalDate previousYear = today.minus(1, ChronoUnit.YEARS);
System.out.println("一年前的日期 : " + previousYear);
LocalDate nextYear = today.plus(1, ChronoUnit.YEARS);
System.out.println("一年后的日期:"+nextYear);
}
- Java 8的Clock时钟类,Java 8增加了一个Clock时钟类用于获取当时的时间戳,或当前时区下的日期时间信息。以前用到
System.currentTimeInMillis()
和TimeZone.getDefault()
的地方都可用Clock替换。
public static void main(String[] args) {
Clock clock = Clock.systemUTC();
System.out.println("Clock : " + clock.millis());
Clock defaultClock = Clock.systemDefaultZone();
System.out.println("Clock : " + defaultClock.millis());
}
- 如何用Java判断日期是早于还是晚于另一个日期,另一个工作中常见的操作就是如何判断给定的一个日期是大于某天还是小于某天?在Java 8中,LocalDate类有两类方法
isBefore()
和isAfter()用
于比较日期。调用isBefore()
方法时,如果给定日期小于当前日期则返回true。
//import java.time.LocalDate;
//import java.time.temporal.ChronoUnit;
public static void main(String[] args) {
LocalDate today = LocalDate.now();
LocalDate tomorrow = LocalDate.of(2018,2,6);
if(tomorrow.isAfter(today)){
System.out.println("之后的日期:"+tomorrow);
}
LocalDate yesterday = today.minus(1, ChronoUnit.DAYS);
if(yesterday.isBefore(today)){
System.out.println("之前的日期:"+yesterday);
}
}
- 如何表示信用卡到期这类固定日期,答案就在YearMonth,与 MonthDay检查重复事件的例子相似,YearMonth是另一个组合类,用于表示信用卡到期日、FD到期日、期货期权到期日等。还可以用这个类得到 当月共有多少天,YearMonth实例的
lengthOfMonth()
方法可以返回当月的天数,在判断2月有28天还是29天时非常有用。
public static void main(String[] args) {
YearMonth currentYearMonth = YearMonth.now();
System.out.printf("Days in month year %s: %d%n", currentYearMonth, currentYearMonth.lengthOfMonth());
YearMonth creditCardExpiry = YearMonth.of(2021, Month.FEBRUARY);
System.out.printf("Your credit card expires on %s %n", creditCardExpiry);
}
- 如何在Java 8中检查闰年
public static void main(String[] args) {
LocalDate today = LocalDate.now();
if(today.isLeapYear()){
System.out.println("This year is Leap year");
}else {
System.out.println("2018 is not a Leap year");
}
}
- 计算两个日期之间的天数和月数,有一个常见日期操作是计算两个日期之间的天数、周数或月数。在Java 8中可以用
java.time.Period
类来做计算。下面这个例子中,我们计算了当天和将来某一天之间的月数。
//import java.time.LocalDate;
//import java.time.Period;
public static void main(String[] args) {
LocalDate today = LocalDate.now();
LocalDate java8Release = LocalDate.of(2018, 12, 14);
Period periodToNextJavaRelease = Period.between(today, java8Release);
System.out.println("Months left between today and Java 8 release : "
+ periodToNextJavaRelease.getMonths() );
}
- 在Java 8中获取当前的时间戳,Instant类有一个静态工厂方法now()会返回当前的时间戳。时间戳信息里同时包含了日期和时间,这和
java.util.Date
很像。实际上Instant类确实等同于 Java 8之前的Date类,你可以使用Date类和Instant类各自的转换方法互相转换,例如:Date.from(Instant)
将Instant转换成java.util.Date
,Date.toInstant()
则是将Date类转换成Instant类。
public static void main(String[] args) {
Instant timestamp = Instant.now();
System.out.println("What is value of this instant " + timestamp.toEpochMilli());
}
- Java 8中如何使用预定义的格式化工具去解析或格式化日期
public static void main(String[] args) {
String dayAfterTommorrow = "20180205";
LocalDate formatted = LocalDate.parse(dayAfterTommorrow,
DateTimeFormatter.BASIC_ISO_DATE);
System.out.println(dayAfterTommorrow+" 格式化后的日期为: "+formatted);
}
- 字符串互转日期类型
public static void main(String[] args) {
LocalDateTime date = LocalDateTime.now();
DateTimeFormatter format1 = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
String str = date.format(format1);
System.out.println("日期转换为字符串:"+str);
DateTimeFormatter format2 = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
LocalDate date2 = LocalDate.parse(str,format2);
System.out.println("日期类型:"+date2);
}