工作中我们会遇到些场景需要我们对时间或者日历进行处理,比如要统计账单、报表等,需要获取当前时间、获取当前时间前一天、获取本月第一天和最后一天、获取特定天特定的时刻(精确到秒)等,这时就需要借助一些第三方包的api来完成这些操作。Java 8的对时间/日期处理的类/第三方包主要有以下6种:
- java.util包下的Date;
- Instant;
- SimpleDateFormat;
- DateTimeFormatter;
- Calendar;
- LocalDateTime。
1、Date类
Date类是最基础也是最早发布的一个时间类,Date类重名的有4个相关的类,用于处理日期的类是位于java.util包下的Date类。Date类由于发布的比较早,很多方法已经不推荐使用了,可以用Calendar类代替。
1.1 Date类构造方法
Date类有6个构造方法,其中的4个已经被@Deprecated注解标注表明不推荐使用了,这里介绍还在使用的2中构造方法。
// 无参构造方法,将会创建一个当前系统时间的Date对象,时间精确到毫秒
public Date();
// 接收一个参数,该参数是从1970年1月1日起的毫秒数
public Date(long date);
举例:打印当前时间
package Time.Date;
import java.util.Date;
public class DateMain {
public static void main(String[] args) {
Date date = new Date();
System.out.println(date);
}
}
结果:
Mon Jan 18 10:59:42 CST 2021
可以看到Date获取的日期格式是格林威治时间,并不那么直观,我们更希望的是年-月-日-时-分-秒这种格式,所以会用到第3节的SimpleDateFormat类对Date产生的日期进行格式化,转换成我们希望的格式。
1.2 Date类常用方法
Date类里面的方法也大部分都是废弃的,不建议使用的,这里介绍几个常用的方法。
1.2.1 boolean before(Date date)
调用此方法的Date对象在指定日期之前返回true,否则返回false。
举例:
public class DateMain {
public static void main(String[] args) {
Date dateBefore = new Date();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Date dateNow = new Date();
System.out.println(dateBefore.before(dateNow));
}
}
1.2.2 boolean after(Date date)
调用此方法的Date对象在指定日期之后返回true,否则返回false。
举例:
public class DateMain {
public static void main(String[] args) {
Date dateBefore = new Date();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Date dateNow = new Date();
System.out.println(dateNow.after(dateBefore));
}
}
1.2.3 int compareTo(Date anotherDate)
比较两个日期的顺序:
- 如果调用方Date对象的时间早于anotherDate对象的时间返回-1;
- 如果调用方Date对象的时间晚于anotherDate对象的时间返回1;
- 如果调用方Date对象的时间等于anotherDate对象的时间返回0。
举例:
public class DateMain {
public static void main(String[] args) {
Date dateBefore = new Date();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Date dateNow = new Date();
// 返回-1
System.out.println(dateBefore.compareTo(dateNow));
// 返回1
System.out.println(dateNow.compareTo(dateBefore));
}
}
2、SimpleDateFormat
2.1 SimpleDateFormat使用
上面介绍了Date类的得到的日期是格林威治时间,并不直观,需要将格林威治时间转换为我们习惯的时间格式,SimpleDateFormat就是格林威治时间和直观的日期字符串之间转换的工具。
2.1.1 SimpleDateFormat构造方法
SimpleDateFormat有以下4种构造方法:
public SimpleDateFormat(){}
public SimpleDateFormat(String pattern){}
public SimpleDateFormat(String pattern, Locale locale){}
public SimpleDateFormat(String pattern, DateFormatSymbols formatSymbols){}
最经常使用的还是第二种构造方法,该方法传入一个日期格式字符串,通过该字符串定义的日期格式返回具体的时间。一般会写一个DateUtils将这些时间类处理封装。
2.1.2 String format(Date date)
2.1.3 Date parse(String dateStr)
利用给定的日期字符串转成Date对象。
举例:
DateUtils:
package Time.SimpleDateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateUtils {
// 注意:这些写是线程不安全的
private static final String YM_FORMAT = "yyyy-MM";
private static final String YMD_FORMAT = "yyyy-MM-dd";
private static final String YMDH_FORMAT = "yyyy-MM-dd HH:mm:ss";
private static final SimpleDateFormat ymFormat = new SimpleDateFormat(YM_FORMAT);
private static final SimpleDateFormat ymdFormat = new SimpleDateFormat(YMD_FORMAT);
private static final SimpleDateFormat ymdhFormat = new SimpleDateFormat(YMDH_FORMAT);
public static Date stringToDate(String dateStr)
{
Date date = null;
try {
date = ymdhFormat.parse(dateStr);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
public static String dateToString(Date date)
{
return ymdhFormat.format(date);
}
}
SDFMain:
package Time.SimpleDateFormat;
import java.util.Date;
public class SDFMain {
public static void main(String[] args) {
Date nowDate = new Date();
String dateStr = DateUtils.dateToString(nowDate);
// 结果:2021-01-18 14:17:02
System.out.println(dateStr);
// 结果:Mon Jan 18 14:17:02 CST 2021
System.out.println(DateUtils.stringToDate(dateStr));
}
}
2.2 SimpleDateFormat线程不安全
2.2.1 为什么SimpleDateFormat是线程不安全的?
单线程或者并发量比较少的情况下SimpleDateFormat不会出现这个问题,但当并发量上去后会出现这个问题。一般为了节省内存开销,会把SimpleDateFormat对象声明成static类型,SimpleDateFormat的两个核心方法:format和parse里会使用一个Calendar类型的成员变量来保存时间,那么这个被static修饰的SimpleDateFormat对象就是一个共享变量,SimpleDateFormat中的Calendar类型的成员变量也就可以被多个线程访问到,这就是线程不安全的原因。
举个例子:假设一个线程A刚执行完calendar.setTime把时间设置成2020-05-07,这个线程还没执行完,线程B又执行了calendar.setTime把时间改成了2020-06-07。这时候线程A继续往下执行,拿到的calendar.getTime得到的时间就是线程B改过之后的。
2.2.2 解决方案是什么?
- 每次使用时new一个SimpleDateFormat 的实例,这样可以保证每个实例使用自己的Calendar实例,但是每次使用都需要new一个对象,并且使用后由于没有其他引用,又需要回收,开销会很大;
- 可以使用synchronized 对SimpleDtaFormat实例进行同步;
- 使用ThreadLocal,这样每个线程只需要使用一个SimpleDateFormate实例,这相比第一种方式节省了对象的创建销毁开销,并且不需要使多个线程同步;
- 使用JDK8中的 DateTimeFormatter。
这里推荐使用第4种方法,因为ThreadLocal有内存泄漏的风险。
3、DateTimeFormatter
鉴于第2节中介绍的SimpleDateFormat是线程不安全的,jdk1.8中新增了 LocalDate 与 LocalDateTime等类来解决日期处理方法,同时引入了一个新的类DateTimeFormatter来解决日期格式化问题。LocalDateTime,DateTimeFormatter都是线程安全的,且二者是搭配使用的。
与创建一个SimpleDateFormat实例是用new的方法不同,创建一个DateTimeFormat实例是用一个静态方法ofPattern(String formatStr)
。可以创建static修饰的DateTimeFormatter实例,且是线程安全的。
举例:
public class DateUtils {
private static final String YM_FORMAT = "yyyy-MM";
private static final String YMD_FORMAT = "yyyy-MM-dd";
private static final String YMDH_FORMAT = "yyyy-MM-dd HH:mm:ss";
private static DateTimeFormatter ymFormatter = DateTimeFormatter.ofPattern(YM_FORMAT);
private static DateTimeFormatter ymdFormatter = DateTimeFormatter.ofPattern(YMD_FORMAT);
private static DateTimeFormatter ymdhFormatter = DateTimeFormatter.ofPattern(YMDH_FORMAT);
}
4、Calendar
Calendar类是一个抽象类,GregorianCalendar是Calendar类的一个具体实现,Calendar 的getInstance()方法返回一个默认用当前的语言环境和时区初始化的GregorianCalendar对象,下面介绍方法时也是GregorianCalendar类的实例方法。
Calendar类中用以下这些常量表示不同的意义,即年、月、日、时、分、秒,如下:
常量 | 描述 |
---|---|
Calendar.YEAR | 年份 |
Calendar.MONTH | 月份 |
Calendar.DATE | 日期 |
Calendar.DAY_OF_MONTH | 日,一个月的第几天 |
Calendar.HOUR | 12小时制的小时 |
Calendar.HOUR_OF_DAY | 24小时制的小时 |
Calendar.MINUTE | 分钟 |
Calendar.SECOND | 秒 |
Calendar.DAY_OF_WEEK | 星期几 |
4.1 getInstance
是Calendar类提供的获取GregorianCalendar实例的静态方法,因此创建Calendar实例不用new,用getInstance。
注意Calendar类的getInstance方法获取的是当前日期。
Calendar calendar = Calendar.getInstance();
4.2 getTime
Date getTime(),获取当前时间(格林威治时间)。
举例:
public class CalendarMain {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
// 打印结果:Mon Jan 18 14:40:00 CST 2021
System.out.println(calendar.getTime());
}
}
4.3 setTime
void setTime(Date date),用给定的日期设置Calendar的当前时间,参数类型为Date实例。
举例:
public class CalendarMain {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
Date date = new Date();
calendar.setTime(date);
System.out.println(calendar.getTime());
}
}
4.4 get
int get(int field),获取指定字段的时间值,返回int类型,入参field,指定字段,即介绍Calendar开始时的常量表格。
注意:
- 获取当前时间的月份,calendar.get(Calendar.MONTH),结果要+1才是当前时间的月份;
- 获取周几,返回的int类型的结果,1代表周日,2代表周一,3代表周二…以此类推,7代表周六。
举例:
public class CalendarMain {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
// 获取年,结果是:2021
System.out.println(calendar.get(Calendar.YEAR));
// 获取月,结果是:0
System.out.println(calendar.get(Calendar.MONTH));
// 获取日,结果是:18
System.out.println(calendar.get(Calendar.DAY_OF_MONTH));
// 获取24小时制的时,结果是:15
System.out.println(calendar.get(Calendar.HOUR_OF_DAY));
// 获取周几,结果是:2
System.out.println(calendar.get(Calendar.DAY_OF_WEEK));
}
}
4.5 set
void set(int field, int value),设置指定字段的时间值,这里的指定字段field即介绍Calendar开始时的常量表格。
注意:
- 下方的例子,如果某些字段没有set,则保留不变(下方例子里即当天的分钟、秒数);
- 设置月份时,设置的value为目标月份-1,比如设置为5月,则
calendar.set(Calendar.MONTH, 4);
- 设置周几时,1代表周天,2代表周一,以此类推…
举例:
public class CalendarMain {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR, 1993);
calendar.set(Calendar.MONTH, 4);
calendar.set(Calendar.DAY_OF_MONTH, 16);
calendar.set(Calendar.HOUR_OF_DAY, 5);
calendar.set(Calendar.DAY_OF_WEEK, 1);
// 打印结果:Sun May 16 05:19:05 CST 1993 我的生日
System.out.println(calendar.getTime());
}
}
4.6 add
void add(int field, int amount),日历的偏移量,可以指定一个日历中的字段field,进行整数amount的偏移,这里的指定字段field即介绍Calendar开始时的常量表格。
·举例:
public class CalendarMain {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());
// 获取此刻上一个月份的时刻
calendar.add(Calendar.MONTH, -1);
// 打印结果:Fri Dec 18 15:34:06 CST 2020
System.out.println(calendar.getTime());
calendar.setTime(new Date());
// 获取此刻两天后的时刻
calendar.add(Calendar.DAY_OF_MONTH, 2);
// 打印结果:Wed Jan 20 15:34:06 CST 2021
System.out.println(calendar.getTime());
}
}
4.7 int getActualMaximum(int field)/int getActualMinimum(int field)
返回当前日期,给定字段的最大值/最小值。
应用场景:比如2月有28天,有的月31天,有的月30天,如果想获得当前月的最后一天,可以用calendar.getActualMaximum(Calendar.DAY_OF_MONTH)
。
举例:
public class CalendarMain {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
// 获取当前月的最大天,结果:31
System.out.println(calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
// 获取当前年的最小月,结果:1
System.out.println(calendar.getActualMinimum(Calendar.YEAR));
}
}
4.8 boolean isLeapYear(int year)
4.9 before/after/compareTo
上面三个方法都是Calender实例的比较方法:
- boolean before(Objecr when):实际应用时入参多为Calender实例,如果调用方时间早于入参,返回true,否则返回false;
- boolean after(Objecr when):实际应用时入参多为Calender实例,如果调用方时间晚于入参,返回true,否则返回false;
int compareTo(Calender calender):如果调用方时间早于入参,返回-1;如果调用方时间晚于入参,返回1,如果相等返回0。
5、LocalDateTime
Java8之后日期类推荐使用LocalDateTime类,这个类的使用跟其他几个类密切相关,涉及到的类:
LocalDate:获取日期,即年、月、日的信息;
- LocalTime:获取时间,即时、分、秒的信息;
- LocalDateTime:获取年、月、日、时、分、秒的信息;
- Instant:时间戳相关的类;
- DateTimeFormatter:日期格式化类,对应之前的SimpleDateFormat。
疑问:LocalDateTime、LocalDate和LocalTime三个类只保留第一个不就行了,后两个还有存在的意义么?
5.1 LocalDate
LocalDate是专门用来获取日期中的年、月和日的信息的。
5.1.1 初始化LocalDate
LocalDate对象的获取方式主要有以下三种:
LocalDate.now();
LocalDate.of(year, month, dayOfMonth);
LocalDate.ofYearDay(year, 当年的第几天);
举例:
public class LDMain {
public static void main(String[] args) {
LocalDate localDate = LocalDate.now();
// 打印结果:2021-01-19
System.out.println(localDate);
LocalDate localDate1 = LocalDate.of(1993, 5, 16);
// 打印结果:1993-05-16
System.out.println(localDate1);
LocalDate localDate2 = LocalDate.ofYearDay(1993, 256);
// 打印结果:1993-09-13
System.out.println(localDate2);
}
}
5.1.2 使用LocalDate读取date
// 获取年份
int getYear();
// 获取月份的英文名称,比如May、June等
Month getMonth();
// 获取月份的阿拉伯数字(1-12)
int getMonthValue();
// 获取一个月中的第几天
int getDayOfMonth();
// 获取当前日期是一年中的第几天
int getDayOfYear();
// 获取当前日期是周几,英文名称,比如Monday
DayOfWeek getDayOfWeek();
// 获取当前日期是周几,阿拉伯数字,1-7
int get(ChronoField.DAY_OF_WEEK);
// 获取当月有多少天,比如30、31、28...
int lengthOfMonth();
// 获取当年有多少天,比如365、366
int lengthOfYear();
// 判断当年是否是闰年,是闰年返回true,否则返回false
boolean isLeapYear()
举例:
package Time.LocalDateTime;
import java.time.LocalDate;
import java.time.temporal.ChronoField;
public class LDMain {
public static void main(String[] args) {
LocalDate localDate = LocalDate.now();
// 打印结果:2021
System.out.println(localDate.getYear());
// 打印结果:JANUARY
System.out.println(localDate.getMonth());
// 打印结果:1
System.out.println(localDate.getMonthValue());
// 打印结果:19
System.out.println(localDate.getDayOfMonth());
// 打印结果:19
System.out.println(localDate.getDayOfYear());
// 打印结果:TUESDAY
System.out.println(localDate.getDayOfWeek());
// 打印结果:2
System.out.println(localDate.get(ChronoField.DAY_OF_WEEK));
// 打印结果:31
System.out.println(localDate.lengthOfMonth());
// 打印结果:365
System.out.println(localDate.lengthOfYear());
// 打印结果:false
System.out.println(localDate.isLeapYear());
}
}
5.1.3 TemporalField 读取 LocalDate 的值
ChronoField 是个枚举类,其实现了TemporalField接口,除了5.1.2的方式读取date之外,我们还可以使用ChronoField方式读取date,这里列举几个常用的ChronoField枚举类。
// 获取当前日期的年份
int get(ChronoField.YEAR);
// 获取当前日期是第几月
int get(ChronoField.MONTH_OF_YEAR);
// 获取当前日期是当年的第几月
int get(ChronoField.DAY_OF_MONTH);
// 获取当前日期是当年的第几天
int get(ChronoField.DAY_OF_YEAR);
// 获取当前日期是本周的第几天
int get(ChronoField.DAY_OF_WEEK);
举例:
package Time.LocalDateTime;
import java.time.LocalDate;
import java.time.temporal.ChronoField;
public class LDMain {
public static void main(String[] args) {
LocalDate localDate = LocalDate.now();
// 打印结果:2021
System.out.println(localDate.get(ChronoField.YEAR));
// 打印结果:1
System.out.println(localDate.get(ChronoField.MONTH_OF_YEAR));
// 打印结果:19
System.out.println(localDate.get(ChronoField.DAY_OF_MONTH));
// 打印结果:19
System.out.println(localDate.get(ChronoField.DAY_OF_YEAR));
// 打印结果:2
System.out.println(localDate.get(ChronoField.DAY_OF_WEEK));
}
}
5.1.4 修改LocalDate的date
通过withAttribute修改不会改变原来的localDate,会在原来localDate的基础上形成新的localDate副本。
// 修改localDate的年份为year
LocalDate withYear(int year);
// 修改localDate的月份为month
LocalDate withMonth(int month);
// 修改localDate的日期为dayOfMonth,为当月的第几天
LocalDate withDayOfMonth(int dayOfMonth);
// 修改localDate的日期为dayOfYear,为当年的第几天
LocalDate withDayOfYear(int dayOfYear);
举例:
package Time.LocalDateTime;
import java.time.LocalDate;
public class LDMain {
public static void main(String[] args) {
// 2021-01-19
LocalDate localDate = LocalDate.now();
LocalDate localDate1 = localDate.withYear(1993);
// 打印结果:1993-01-19
System.out.println(localDate1);
LocalDate localDate2 = localDate.withMonth(5);
// 打印结果:2021-05-19
System.out.println(localDate2);
LocalDate localDate3 = localDate.withDayOfYear(192);
// 打印结果:2021-07-11
System.out.println(localDate3);
LocalDate localDate4 = localDate.withDayOfMonth(16);
// 打印结果:2021-01-16
System.out.println(localDate4);
}
}
5.1.5 使用 TemporalAdjuster 修改日期
TemporalAdjuster 时间矫正器修改时间也不会改变原来的localDate,会新生成LocalDate 副本,相比于withAttribute,其API更加丰富,提供大量的静态工厂方法,能满足我们日常开发需求。
// 获取localDate对应的本月第一天的日期
localDate.with(TemporalAdjusters.firstDayOfMonth());
// 获取localDate对应的本年第一天的日期
localDate.with(TemporalAdjusters.firstDayOfYear());
// 获取localDate对应的本月最后一天的日期
localDate.with(TemporalAdjusters.lastDayOfMonth());
// 获取localDate对应的本年的最后一天的日期
localDate.with(TemporalAdjusters.lastDayOfYear());
// 获取localDate对应的下个月的第一天的日期
localDate.with(TemporalAdjusters.firstDayOfNextMonth());
// 获取localDate对应的下一年的第一天的日期
localDate.with(TemporalAdjusters.firstDayOfNextYear());
// 获取localDate的下一个dayOfWeek(比如DayOfWeek.Monday)对应的日期,如果当天就是dayOfWeek,则往后找下一个
// 比如当天是周二,localDate.with(TemporalAdjusters.next(DayOfWeek.Tuesday))返回的是下一个周二的日期
localDate.with(TemporalAdjusters.next(DayOfWeek dayOfWeek));
// 获取localDate的下一个dayOfWeek(比如DayOfWeek.Monday)对应的日期,如果当天就是dayOfWeek,则返回当天的日期
// 比如当天是周二,localDate.with(TemporalAdjusters.nextOrSame(DayOfWeek.Tuesday))返回的就是当天的日期
localDate.with(TemporalAdjusters.nextOrSame(DayOfWeek dayOfWeek));
// 获取localDate的上一个dayOfWeek(比如DayOfWeek.Monday)对应的日期,如果当天就是dayOfWeek,则往前找上一个
// 比如当天是周二,localDate.with(TemporalAdjusters.previous(DayOfWeek.Tuesday))返回的是上一个周二的日期
localDate.with(TemporalAdjusters.previous(DayOfWeek dayOfWeek));
// 获取localDate的上一个dayOfWeek(比如DayOfWeek.Monday)对应的日期,如果当天就是dayOfWeek,则返回当天的日期
// 比如当天是周二,localDate.with(TemporalAdjusters.previous(DayOfWeek.Tuesday))返回的就是当天的日期
localDate.with(TemporalAdjusters.previousOrSame(DayOfWeek dayOfWeek));
// 获取localDate对应月份的第一个dayOfWeek(星期)
localDate.with(TemporalAdjusters.firstInMonth(DayOfWeek dayOfWeek));
// 获取localDate对应月份的最后一个dayOfWeek(星期)
localDate.with(TemporalAdjusters.lastInMonth(DayOfWeek dayOfWeek));
举例:
package Time.LocalDateTime;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;
public class LDMain {
public static void main(String[] args) {
// 2021-01-19
LocalDate localDate = LocalDate.now();
// 打印结果:2021-01-01
System.out.println(localDate.with(TemporalAdjusters.firstDayOfMonth()));
// 打印结果:2021-01-01
System.out.println(localDate.with(TemporalAdjusters.firstDayOfYear()));
// 打印结果:2021-01-31
System.out.println(localDate.with(TemporalAdjusters.lastDayOfMonth()));
// 打印结果:2021-12-31
System.out.println(localDate.with(TemporalAdjusters.lastDayOfYear()));
// 打印结果:2021-02-01
System.out.println(localDate.with(TemporalAdjusters.firstDayOfNextMonth()));
// 打印结果:2022-01-01
System.out.println(localDate.with(TemporalAdjusters.firstDayOfNextYear()));
// 打印结果:2021-01-25
System.out.println(localDate.with(TemporalAdjusters.next(DayOfWeek.TUESDAY)));
// 打印结果:2021-01-19
System.out.println(localDate.with(TemporalAdjusters.nextOrSame(DayOfWeek.TUESDAY)));
// 打印结果:2021-01-12
System.out.println(localDate.with(TemporalAdjusters.previous(DayOfWeek.TUESDAY)));
// 打印结果:2021-01-19
System.out.println(localDate.with(TemporalAdjusters.previousOrSame(DayOfWeek.TUESDAY)));
// 打印结果:2021-01-05
System.out.println(localDate.with(TemporalAdjusters.firstInMonth(DayOfWeek.TUESDAY)));
// 打印结果:2021-01-26
System.out.println(localDate.with(TemporalAdjusters.lastInMonth(DayOfWeek.TUESDAY)));
}
}
5.1.6 解析LocalDate
就是String和LocalDate对象之间的相互转换。
// 字符串转LocalDate对象
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("*******");
LocalDate date = LocalDate.parse(dateStr, dateTimeFormatter);
// LocalDate对象转字符串
String dateStr = localDate.toString();
举例:将指定格式的字符串转成LocalDate对象
public class LDMain {
public static void main(String[] args) {
DateTimeFormatter ymdFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
String dateStr = "1993/05/16";
LocalDate localDate = LocalDate.parse(dateStr, ymdFormatter);
System.out.println(localDate);
}
}
5.1.7 使用Period操纵LocalDate
不好用,有大坑。
public class LDMain {
public static void main(String[] args) {
LocalDate date1 = LocalDate.of(1993, 5, 16);
LocalDate date2 = LocalDate.of(2021, 1, 19);
Period between = Period.between(date1, date2);
// 打印结果:27
System.out.println(between.getYears());
// 打印结果:8
System.out.println(between.getMonths());
// 打印结果:3
System.out.println(between.getDays());
}
}
5.2 LocalTime
5.2.1 初始化LocalTime
获取LocalTime对象主要有以下两种方式:
// 获取当前时刻的LocalTime对象
LocalTime.now();
// 获取指定时、分、秒、纳秒的LocalTime对象
LocalTime localTime1 = LocalTime.of(时, 分, 秒, 纳秒);
举例:
public class LTMain {
public static void main(String[] args) {
LocalTime localTime = LocalTime.now();
// 打印结果:17:38:55.586
System.out.println(localTime);
LocalTime localTime1 = LocalTime.of(11, 22, 33, 44);
// 打印结果:11:22:33.000000044
System.out.println(localTime1);
}
}
5.2.2 使用LocalTime读取时间
// 获取localTime的时
int getHour();
// 获取localTime的分
int getMinute();
// 获取localTime的秒
int getSecond();
// 获取localTime的纳秒
int getNano();
举例:
public class LTMain {
public static void main(String[] args) {
// 17:43:18.102
LocalTime localTime = LocalTime.now();
// 打印结果:17
System.out.println(localTime.getHour());
// 打印结果:43
System.out.println(localTime.getMinute());
// 打印结果:18
System.out.println(localTime.getSecond());
// 打印结果:10200000
System.out.println(localTime.getNano());
}
}
5.2.3 修改LocalTime的值
跟LocalDate一样,withPropertity方法也是创建副本并返回。
// 设置localTime对象的时
localTime.withHour(hour);
// 设置localTime对象的分
localTime.withMinute(minute);
// 设置localTime对象的秒
localTime.withSecond(second);
// 设置localTime对象的纳秒
localTime.withNano(nanosecond);
举例:
public class LTMain {
public static void main(String[] args) {
LocalTime localTime = LocalTime.now();
// 打印结果:17
System.out.println(localTime.withHour(15));
// 打印结果:43
System.out.println(localTime.withMinute(16));
// 打印结果:18
System.out.println(localTime.withSecond(17));
// 打印结果:10200000
System.out.println(localTime.withNano(18));
}
}
5.2.4 解析LocalTime
举例:
public void localTimeParse(){
// 默认支持格式解析
LocalTime parse = LocalTime.parse("22:50:00");
System.out.println(parse);// 22:50
// 指定格式解析
LocalTime time = LocalTime.parse("22:50:00", DateTimeFormatter.ISO_TIME);
System.out.println(time);// 22:50
}
5.2.5 使用Duration获得时间差值
举例:
public static void main(String[] args) {
LocalTime time1 = LocalTime.of(22, 50, 20, 20);
LocalTime time2 = LocalTime.of(23, 10);
// 差值
Duration duration = Duration.between(time1, time2);
long seconds = duration.getSeconds();
int nano = duration.getNano();
System.out.println(seconds);//1179
System.out.println(nano);//999999980
}
5.3 LocalDateTime
5.3.1 LocalDateTime与LocalDate和LocalTime的转换
// LocalDate和LocalTime合并为LocalDateTime
LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime);
// LocalDateTime转为LocalDate
LocalDate localDate = localDateTime.toLocalDate();
// LocalDateTime转为LocalTime
LocalTime localTime = localDateTime.toLocalTime();
举例:
public static void main(String[] args) {
LocalDate localDate = LocalDate.of(1993, 5, 16);
LocalTime localTime = LocalTime.of(5, 6, 6);
// LocalDate和LocalTime合并为LocalDateTime
LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime);
// 打印结果:1993-05-16T05:06:06
System.out.println(localDateTime);
// LocalDateTime转为LocalDate
LocalDate localDate1 = localDateTime.toLocalDate();
// 打印结果:1993-05-16
System.out.println(localDate1);
// LocalDateTime转为LocalTime
LocalTime localTime1 = localDateTime.toLocalTime();
// 打印结果:05:06:06
System.out.println(localTime1);
}
5.3.2 LocalDateTime对象初始化
// 获取当前时间
LocalDateTime localDateTime = LocalDateTime.now();
// 指定时间段的值拼装一个LocalDateTime
LocalDateTime localDateTime = LocalDateTime.of(年,月,日,时,分,秒);
举例:
public static void main(String[] args) {
LocalDateTime localDateTime = LocalDateTime.now();
// 打印结果:2021-01-19T19:37:48.761
System.out.println(localDateTime);
LocalDateTime localDateTime1 = LocalDateTime.of(1993, 5, 16, 6, 6, 6);
// 打印结果:1993-05-16T06:06:06
System.out.println(localDateTime1);
}
5.3.3 LocalDateTime解析和格式化
注意:String转LocalDateTime时,中间会加一个字符“T”,ISO8601的规定,据说是为了避免歧义,SpirngBoot有个解决方案,如下:
/**
* 关于Java8中localDateTime去掉中间的T
*/
@Configuration
public class LocalDateTimeSerializerConfig {
@org.springframework.beans.factory.annotation.Value("${spring.jackson.date-format:yyyy-MM-dd HH:mm:ss}")
private String pattern;
@Bean
public LocalDateTimeSerializer localDateTimeDeserializer() {
return new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(pattern));
}
@Bean
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
return builder -> builder.serializerByType(LocalDateTime.class, localDateTimeDeserializer());
}
}
举例:
public static void main(String[] args) {
// LocalDateTime转String
String ymdhmsPattern = "yyyy-MM-dd HH:mm:ss";
DateTimeFormatter ymdhmsFormat = DateTimeFormatter.ofPattern(ymdhmsPattern);
LocalDateTime localDateTime = LocalDateTime.now();
String dateStr = localDateTime.format(ymdhmsFormat);
// 打印结果:2021-01-19 19:46:34
System.out.println(dateStr);
// String转LocalDateTime
LocalDateTime localDateTime1 = LocalDateTime.parse(dateStr, ymdhmsFormat);
// 打印结果:2021-01-19T19:46:34
System.out.println(localDateTime1);
}
5.3.4 get各种时间值
api就是LocalDate和LocalTime的get相关的api。
5.3.5 set各种时间值
api就是LocalDate和LocalTime的set相关的api。
5.3.6 自定义日期 / 日期加减
加减后得到的LocalDateTime对象是一个新的LocalDateTime对象,而不是在原来的LocalDateTime对象上做加减,是在副本上进行的。
举例:
package Time.LocalDateTime;
import java.time.LocalDateTime;
public class LDTMain {
public static void main(String[] args) {
// 2020-01-29T14:35:51
LocalDateTime dateTime = LocalDateTime.of(2020, 1, 29, 14, 35, 51);
// 2020-01-29T14:35:50
System.out.println(dateTime.minusSeconds(1));
// 2020-01-29T14:34:51
System.out.println(dateTime.minusMinutes(1));
// 2020-01-29T13:35:51
System.out.println(dateTime.minusHours(1));
// 2020-01-29T13:35:51
System.out.println(dateTime.minusDays(1));
// 2020-01-28T14:35:51
System.out.println(dateTime.minusWeeks(1));
// 2020-01-22T14:35:51
System.out.println(dateTime.minusMonths(1));
// 2019-12-29T14:35:51
System.out.println(dateTime.minusYears(1));
}
}
增加的为对应的plus函数,如dateTime.plusSeconds(1)。
5.3.7 比较LocalDateTime日期
举例:
public static void main(String[] args) {
// 2020-01-29T14:35:51.207
LocalDateTime now = LocalDateTime.now();
// 2020-01-28T14:35:51.207
LocalDateTime yesterday = now.minusDays(1);
// true
boolean after = now.isAfter(yesterday);
// false
boolean before = now.isBefore(yesterday);
// false
boolean equal = now.isEqual(yesterday);
}
5.3.8 Duration && Period
5.3.8.1 Duration
Duration是用来统计两个LocalDateTime对象之间的时间间隔的,粒度是时、分、秒、纳秒。
举例:
public static void main(String[] args) {
LocalTime localTime = LocalTime.of(18, 20, 1);
LocalTime localTime2 = LocalTime.of(19, 19, 19);
Duration duration = Duration.between(localTime, localTime2);
System.out.println(duration);
// Duration区间是否为0
System.out.println(duration.isZero());
// Duration区间是否为负
System.out.println(duration.isNegative());
// Duration区间值的秒数
System.out.println(duration.getSeconds());
// Duration区间值的纳秒数
System.out.println(duration.getNano());
// Duration的度量单位
System.out.println(duration.getUnits());
// Duration区间相差几天
System.out.println(duration.toDays());
// Duration区间差几小时
System.out.println(duration.toHours());
// Duration区间相差几分钟
System.out.println(duration.toMinutes());
// Duration区间相差几毫秒
System.out.println(duration.toMillis());
}
结果:
PT59M18S
false
false
3558
0
[Seconds, Nanos]
0
0
59
3558000
说明:
- duration.toMinutes()和duration.toMillis()方法得到的是时间间隔换算成分钟和秒,且取整数,比如相差5分钟10秒,则duration.toMinutes()返回的是5,duration.toMillis()方法返回的是(5 60 + 10) 1000 = 310000;
toProperty方法返回的值可以是正数,也可以是负数,如果是负数,说明Duration.between(localTime, localTime2)方法中,localTime比localTime2晚,即后面减去前面的。
5.3.8.2 Period
Period是LocalDateTime用来求两个LocalDateTime之间的时间间隔的,粒度为年、月、日。
举例:public static void main(String[] args) {
LocalDate localDate = LocalDate.of(2021, 11, 2);
LocalDate localDate2 = LocalDate.of(2020, 12, 12);
Period period = Period.between(localDate, localDate2);
System.out.println(period);
// 区间是否为0
System.out.println(period.isZero());
// 区间是否为为负
System.out.println(period.isNegative());
// 区间的相差几年
System.out.println(period.getYears());
// 区间的相差几月
System.out.println(period.getMonths());
// 区间的相差几日
System.out.println(period.getDays());
// 区间相差多少个月
System.out.println(period.toTotalMonths());
}
结果:
P-10M-21D false true 0 -10 -21 -10
说明:
period.getDays()方法是个大坑,求的是当月相差的天数,并不是一共差多少天。
5.3.9 ChronoUnit
ChronoUnit是用来表示时间单位的,是一个枚举类,但同时提供了两个非常有用的方法:between和plus。
5.3.9.1 between
between方法用来计算两个LocalDateTime对象的差值,比如差几年、几个月、几天等,上面不是说period.getDays()方法是个大坑嘛,统计具体时间差值还是用ChronoUnit的between方法。
ChronoUnit.时间枚举类.between(localDateTime1, localDateTime2);
举例:
public static void main(String[] args) { LocalDateTime localDateTime = LocalDateTime.of(2021, 11, 2, 12, 34, 54); LocalDateTime localDateTime2 = LocalDateTime.of(2020, 12, 12, 2, 43, 5); long betweenYears = ChronoUnit.YEARS.between(localDateTime, localDateTime2); // 0 System.out.println(betweenYears); long betweenMonths = ChronoUnit.MONTHS.between(localDateTime, localDateTime2); // -10 System.out.println(betweenMonths); long betweenDays = ChronoUnit.DAYS.between(localDateTime, localDateTime2); // -325 System.out.println(betweenDays); long betweenWeeks = ChronoUnit.WEEKS.between(localDateTime, localDateTime2); // -46 System.out.println(betweenWeeks); long betweenHours = ChronoUnit.HOURS.between(localDateTime, localDateTime2); // -7809 System.out.println(betweenHours); long betweenMinutes = ChronoUnit.MINUTES.between(localDateTime, localDateTime2); // -468591 System.out.println(betweenMinutes); long betweenSeconds = ChronoUnit.SECONDS.between(localDateTime, localDateTime2); // -28115509 System.out.println(betweenSeconds); }
5.3.9.2 plus
plus是ChronoUnit提供的加减特定时间的方法,跟LocalDateTime的minusMinutes和plusDays方法是一个功能,返回的也是LocalDateTime对象,不同的是ChronoUnit将加和减统一用一个方法plus完成,只需传入正数或者负数即可。
LocalDateTime localDateTime1 = localDateTime.plus(1, ChronoUnit.YEARS)
举例: ```java package com.Jerry.Date.LocalDateTime;
import java.time.LocalDate; import java.time.LocalDateTime; import java.time.Period; import java.time.temporal.ChronoUnit;
public class LDTMain { public static void main(String[] args) { LocalDateTime localDateTime = LocalDateTime.of(2021, 11, 2, 12, 34, 54);
LocalDateTime plusYear = localDateTime.plus(1, ChronoUnit.YEARS);
// 2022-11-02T12:34:54
System.out.println(plusYear);
LocalDateTime plusMonth = localDateTime.plus(-1, ChronoUnit.MONTHS);
// 2021-10-02T12:34:54
System.out.println(plusMonth);
LocalDateTime plusDay = localDateTime.plus(1, ChronoUnit.DAYS);
// 2021-11-03T12:34:54
System.out.println(plusDay);
LocalDateTime plusWeek = localDateTime.plus(1, ChronoUnit.WEEKS);
// 2021-11-09T12:34:54
System.out.println(plusWeek);
LocalDateTime plusHour = localDateTime.plus(1, ChronoUnit.HOURS);
// 2021-11-02T13:34:54
System.out.println(plusHour);
LocalDateTime plusMinute = localDateTime.plus(1, ChronoUnit.MINUTES);
// 2021-11-02T12:35:54
System.out.println(plusMinute);
LocalDateTime plusSecond = localDateTime.plus(1, ChronoUnit.SECONDS);
// 2021-11-02T12:34:55
System.out.println(plusSecond);
}
}
<a name="FOLdv"></a>
# 6、Instant
Instant类在Java.time包下,Instant对象表示的就是在时间轴上的一个点,与时间戳一一对应。该类一般用于处理时间戳,并不直接处理具体的日期时间,因此用的不多。下面给出一些Instant类的常用方法:
```java
public static void main(String[] args) {
// 获取当前Instant对象
Instant instant = Instant.now();
// 以ISO-8601输出
System.out.println(instant);
//将java.util.Date转换为Instant
Instant instant1 = Instant.ofEpochMilli(new Date().getTime());
System.out.println(instant1);
//从字符串类型中创建Instant类型的时间
Instant instant2 = Instant.parse("1995-10-23T10:12:35Z");
System.out.println(instant2);
}
至于Instant对象的加减和求时间间隔,可以用LocalDateTime去处理,不用Instant的方法。
参考
Java工具类——日期相关的类
Java 日期时间
2020 年,你还在使用 Java 中的 SimpleDateFormat 吗?
Java基础-Calendar类常用方法介绍
LocalDateTime用法大全
java8新特性 时间操作类 Duration Period和 LocalDateTime LocalDate LocalTimejava8在日常开发中使用LocalDate和LocalTime
Duration和Period的区别—通俗易懂