工作中我们会遇到些场景需要我们对时间或者日历进行处理,比如要统计账单、报表等,需要获取当前时间、获取当前时间前一天、获取本月第一天和最后一天、获取特定天特定的时刻(精确到秒)等,这时就需要借助一些第三方包的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();// 返回-1System.out.println(dateBefore.compareTo(dateNow));// 返回1System.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:02System.out.println(dateStr);// 结果:Mon Jan 18 14:17:02 CST 2021System.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 2021System.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();// 获取年,结果是:2021System.out.println(calendar.get(Calendar.YEAR));// 获取月,结果是:0System.out.println(calendar.get(Calendar.MONTH));// 获取日,结果是:18System.out.println(calendar.get(Calendar.DAY_OF_MONTH));// 获取24小时制的时,结果是:15System.out.println(calendar.get(Calendar.HOUR_OF_DAY));// 获取周几,结果是:2System.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 2020System.out.println(calendar.getTime());calendar.setTime(new Date());// 获取此刻两天后的时刻calendar.add(Calendar.DAY_OF_MONTH, 2);// 打印结果:Wed Jan 20 15:34:06 CST 2021System.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();// 获取当前月的最大天,结果:31System.out.println(calendar.getActualMaximum(Calendar.DAY_OF_MONTH));// 获取当前年的最小月,结果:1System.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-19System.out.println(localDate);LocalDate localDate1 = LocalDate.of(1993, 5, 16);// 打印结果:1993-05-16System.out.println(localDate1);LocalDate localDate2 = LocalDate.ofYearDay(1993, 256);// 打印结果:1993-09-13System.out.println(localDate2);}}
5.1.2 使用LocalDate读取date
// 获取年份int getYear();// 获取月份的英文名称,比如May、June等Month getMonth();// 获取月份的阿拉伯数字(1-12)int getMonthValue();// 获取一个月中的第几天int getDayOfMonth();// 获取当前日期是一年中的第几天int getDayOfYear();// 获取当前日期是周几,英文名称,比如MondayDayOfWeek getDayOfWeek();// 获取当前日期是周几,阿拉伯数字,1-7int get(ChronoField.DAY_OF_WEEK);// 获取当月有多少天,比如30、31、28...int lengthOfMonth();// 获取当年有多少天,比如365、366int lengthOfYear();// 判断当年是否是闰年,是闰年返回true,否则返回falseboolean 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();// 打印结果:2021System.out.println(localDate.getYear());// 打印结果:JANUARYSystem.out.println(localDate.getMonth());// 打印结果:1System.out.println(localDate.getMonthValue());// 打印结果:19System.out.println(localDate.getDayOfMonth());// 打印结果:19System.out.println(localDate.getDayOfYear());// 打印结果:TUESDAYSystem.out.println(localDate.getDayOfWeek());// 打印结果:2System.out.println(localDate.get(ChronoField.DAY_OF_WEEK));// 打印结果:31System.out.println(localDate.lengthOfMonth());// 打印结果:365System.out.println(localDate.lengthOfYear());// 打印结果:falseSystem.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();// 打印结果:2021System.out.println(localDate.get(ChronoField.YEAR));// 打印结果:1System.out.println(localDate.get(ChronoField.MONTH_OF_YEAR));// 打印结果:19System.out.println(localDate.get(ChronoField.DAY_OF_MONTH));// 打印结果:19System.out.println(localDate.get(ChronoField.DAY_OF_YEAR));// 打印结果:2System.out.println(localDate.get(ChronoField.DAY_OF_WEEK));}}
5.1.4 修改LocalDate的date
通过withAttribute修改不会改变原来的localDate,会在原来localDate的基础上形成新的localDate副本。
// 修改localDate的年份为yearLocalDate withYear(int year);// 修改localDate的月份为monthLocalDate 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-19LocalDate localDate = LocalDate.now();LocalDate localDate1 = localDate.withYear(1993);// 打印结果:1993-01-19System.out.println(localDate1);LocalDate localDate2 = localDate.withMonth(5);// 打印结果:2021-05-19System.out.println(localDate2);LocalDate localDate3 = localDate.withDayOfYear(192);// 打印结果:2021-07-11System.out.println(localDate3);LocalDate localDate4 = localDate.withDayOfMonth(16);// 打印结果:2021-01-16System.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-19LocalDate localDate = LocalDate.now();// 打印结果:2021-01-01System.out.println(localDate.with(TemporalAdjusters.firstDayOfMonth()));// 打印结果:2021-01-01System.out.println(localDate.with(TemporalAdjusters.firstDayOfYear()));// 打印结果:2021-01-31System.out.println(localDate.with(TemporalAdjusters.lastDayOfMonth()));// 打印结果:2021-12-31System.out.println(localDate.with(TemporalAdjusters.lastDayOfYear()));// 打印结果:2021-02-01System.out.println(localDate.with(TemporalAdjusters.firstDayOfNextMonth()));// 打印结果:2022-01-01System.out.println(localDate.with(TemporalAdjusters.firstDayOfNextYear()));// 打印结果:2021-01-25System.out.println(localDate.with(TemporalAdjusters.next(DayOfWeek.TUESDAY)));// 打印结果:2021-01-19System.out.println(localDate.with(TemporalAdjusters.nextOrSame(DayOfWeek.TUESDAY)));// 打印结果:2021-01-12System.out.println(localDate.with(TemporalAdjusters.previous(DayOfWeek.TUESDAY)));// 打印结果:2021-01-19System.out.println(localDate.with(TemporalAdjusters.previousOrSame(DayOfWeek.TUESDAY)));// 打印结果:2021-01-05System.out.println(localDate.with(TemporalAdjusters.firstInMonth(DayOfWeek.TUESDAY)));// 打印结果:2021-01-26System.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);// 打印结果:27System.out.println(between.getYears());// 打印结果:8System.out.println(between.getMonths());// 打印结果:3System.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.586System.out.println(localTime);LocalTime localTime1 = LocalTime.of(11, 22, 33, 44);// 打印结果:11:22:33.000000044System.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.102LocalTime localTime = LocalTime.now();// 打印结果:17System.out.println(localTime.getHour());// 打印结果:43System.out.println(localTime.getMinute());// 打印结果:18System.out.println(localTime.getSecond());// 打印结果:10200000System.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();// 打印结果:17System.out.println(localTime.withHour(15));// 打印结果:43System.out.println(localTime.withMinute(16));// 打印结果:18System.out.println(localTime.withSecond(17));// 打印结果:10200000System.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);//1179System.out.println(nano);//999999980}
5.3 LocalDateTime
5.3.1 LocalDateTime与LocalDate和LocalTime的转换
// LocalDate和LocalTime合并为LocalDateTimeLocalDateTime localDateTime = LocalDateTime.of(localDate, localTime);// LocalDateTime转为LocalDateLocalDate localDate = localDateTime.toLocalDate();// LocalDateTime转为LocalTimeLocalTime 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合并为LocalDateTimeLocalDateTime localDateTime = LocalDateTime.of(localDate, localTime);// 打印结果:1993-05-16T05:06:06System.out.println(localDateTime);// LocalDateTime转为LocalDateLocalDate localDate1 = localDateTime.toLocalDate();// 打印结果:1993-05-16System.out.println(localDate1);// LocalDateTime转为LocalTimeLocalTime localTime1 = localDateTime.toLocalTime();// 打印结果:05:06:06System.out.println(localTime1);}
5.3.2 LocalDateTime对象初始化
// 获取当前时间LocalDateTime localDateTime = LocalDateTime.now();// 指定时间段的值拼装一个LocalDateTimeLocalDateTime localDateTime = LocalDateTime.of(年,月,日,时,分,秒);
举例:
public static void main(String[] args) {LocalDateTime localDateTime = LocalDateTime.now();// 打印结果:2021-01-19T19:37:48.761System.out.println(localDateTime);LocalDateTime localDateTime1 = LocalDateTime.of(1993, 5, 16, 6, 6, 6);// 打印结果:1993-05-16T06:06:06System.out.println(localDateTime1);}
5.3.3 LocalDateTime解析和格式化
注意:String转LocalDateTime时,中间会加一个字符“T”,ISO8601的规定,据说是为了避免歧义,SpirngBoot有个解决方案,如下:
/*** 关于Java8中localDateTime去掉中间的T*/@Configurationpublic class LocalDateTimeSerializerConfig {@org.springframework.beans.factory.annotation.Value("${spring.jackson.date-format:yyyy-MM-dd HH:mm:ss}")private String pattern;@Beanpublic LocalDateTimeSerializer localDateTimeDeserializer() {return new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(pattern));}@Beanpublic Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {return builder -> builder.serializerByType(LocalDateTime.class, localDateTimeDeserializer());}}
举例:
public static void main(String[] args) {// LocalDateTime转StringString 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:34System.out.println(dateStr);// String转LocalDateTimeLocalDateTime localDateTime1 = LocalDateTime.parse(dateStr, ymdhmsFormat);// 打印结果:2021-01-19T19:46:34System.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:51LocalDateTime dateTime = LocalDateTime.of(2020, 1, 29, 14, 35, 51);// 2020-01-29T14:35:50System.out.println(dateTime.minusSeconds(1));// 2020-01-29T14:34:51System.out.println(dateTime.minusMinutes(1));// 2020-01-29T13:35:51System.out.println(dateTime.minusHours(1));// 2020-01-29T13:35:51System.out.println(dateTime.minusDays(1));// 2020-01-28T14:35:51System.out.println(dateTime.minusWeeks(1));// 2020-01-22T14:35:51System.out.println(dateTime.minusMonths(1));// 2019-12-29T14:35:51System.out.println(dateTime.minusYears(1));}}
增加的为对应的plus函数,如dateTime.plusSeconds(1)。
5.3.7 比较LocalDateTime日期
举例:
public static void main(String[] args) {// 2020-01-29T14:35:51.207LocalDateTime now = LocalDateTime.now();// 2020-01-28T14:35:51.207LocalDateTime yesterday = now.minusDays(1);// trueboolean after = now.isAfter(yesterday);// falseboolean before = now.isBefore(yesterday);// falseboolean 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区间是否为0System.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());}
结果:
PT59M18Sfalsefalse35580[Seconds, Nanos]00593558000
说明:
- 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);// 区间是否为0System.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的区别—通俗易懂
