Java十九篇:常用類 時(shí)間日期類

圣誕節(jié)快樂
在計(jì)算機(jī)中,應(yīng)該如何表示日期和時(shí)間呢?
我們經(jīng)常看到的日期和時(shí)間表示方式如下:
2019-11-20 0:15:00 GMT+00:00
2019年11月20日8:15:00
11/19/2019 19:15:00 America/New_York
如果直接以字符串的形式存儲(chǔ),那么不同的格式,不同的語言會(huì)讓表示方式非常繁瑣。
在理解日期和時(shí)間的表示方式之前,我們先要理解數(shù)據(jù)的存儲(chǔ)和展示。
當(dāng)我們定義一個(gè)整型變量并賦值時(shí):
int n = 123400;
編譯器會(huì)把上述字符串(程序源碼就是一個(gè)字符串)編譯成字節(jié)碼。在程序的運(yùn)行期,變量n指向的內(nèi)存實(shí)際上是一個(gè)4字節(jié)區(qū)域:
│00│01│e2│08│
注意到計(jì)算機(jī)內(nèi)存除了二進(jìn)制的0/1外沒有其他任何格式。上述十六機(jī)制是為了簡(jiǎn)化表示。
當(dāng)我們用System.out.println(n)打印這個(gè)整數(shù)的時(shí)候,實(shí)際上println()這個(gè)方法在內(nèi)部把int類型轉(zhuǎn)換成String類型,然后打印出字符串123400。
它們實(shí)際上是數(shù)據(jù)的展示格式,分別按英國(guó)時(shí)區(qū)、中國(guó)時(shí)區(qū)、紐約時(shí)區(qū)對(duì)同一個(gè)時(shí)刻進(jìn)行展示。而這個(gè)“同一個(gè)時(shí)刻”在計(jì)算機(jī)中存儲(chǔ)的本質(zhì)上只是一個(gè)整數(shù),我們稱它為Epoch Time。
Epoch Time是計(jì)算從1970年1月1日零點(diǎn)(格林威治時(shí)區(qū)/GMT+00:00)到現(xiàn)在所經(jīng)歷的秒數(shù),例如:
1574208900表示從從1970年1月1日零點(diǎn)GMT時(shí)區(qū)到該時(shí)刻一共經(jīng)歷了1574208900秒,換算成倫敦、北京和紐約時(shí)間分別是:
1574208900 = 北京時(shí)間2019-11-208:15:00
? ? ? ? ? = 倫敦時(shí)間2019-11-200:15:00
? ? ? ? ? = 紐約時(shí)間2019-11-1919:15:00

古老的Date類
處理年月日的年歷類Calendar
格式化字符串和日期對(duì)象的DateFormat格式轉(zhuǎn)換類
好用的SimpleDateFormat實(shí)現(xiàn)類
Joda-Time庫(kù)
一、古老的Date類Date這個(gè)類自jdk1.0開始就被設(shè)計(jì)出來, 從它的源代碼中我們也是可以看出來,Date類曾經(jīng)扮演過很重要的角色,jdk早期的版本中有關(guān)日期和時(shí)間的操作幾乎都是由Date類完成的,下面我們一起看看它的源碼:
private transient long fastTime;
二、處理年月日的年歷類Calendar以前我們是可以使用Date來處理日期年月日的,但是由于該類不支持國(guó)際化等原因,現(xiàn)在其中大部分方法被注解,不再推薦使用,現(xiàn)在的Date類更像是代表著某一個(gè)時(shí)刻的對(duì)象,而處理年月日的這種轉(zhuǎn)換則完全交給了Calendar類處理。所以Calendar目前是日期時(shí)間處理中的核心類,接下來我們看看其中源碼:
//和Date一樣封裝了毫秒屬性
protected long ?time;
protected int ? ? ? ? ? fields[];
//封裝了十七個(gè)靜態(tài)常量
public final static int ERA = 0;
public final static int YEAR = 1;
public final static int MONTH = 2;
public final static int WEEK_OF_YEAR = 3;
.........
public final static int DST_OFFSET = 16;
三、DateFormat處理格式轉(zhuǎn)換DateFormat是一個(gè)抽象類,該類主要用于實(shí)現(xiàn)Date對(duì)象和字符串之間相互轉(zhuǎn)換, 涉及到兩個(gè)轉(zhuǎn)換的方法:
//將Date類型轉(zhuǎn)換為String類型
public final String format(Date date)
//將String類型轉(zhuǎn)換Date類型
public Date parse(String source)
四、優(yōu)秀的實(shí)現(xiàn)類SimpleDateFormatSimpleDateFormat是DateFormat的一個(gè)優(yōu)秀的實(shí)現(xiàn)類,它增強(qiáng)了一個(gè)重要的性質(zhì)。它允許自定義格式輸出模板。構(gòu)造SimpleDateFormat實(shí)例的時(shí)候,可以傳入一個(gè)pattern作為輸出模板。看個(gè)例子:
? ?public static void main(String[] args) {
? ? ? ?Calendar c = Calendar.getInstance();
? ? ? ?SimpleDateFormat sm = new SimpleDateFormat("yyyy年MM月dd日 E HH時(shí)mm分ss秒");
? ? ? ?System.out.println(sm.format(c.getTime()));
輸出結(jié)果:
2017年05月29日 星期一 20時(shí)25分31秒
上述的代碼中,字符串yyyy年MM月dd日 E HH時(shí)mm分ss秒就是一個(gè)模板pattern,其中:
yyyy表示使用四位數(shù)字輸出年份
MM表示使用兩位數(shù)字表示月份
dd表示使用兩位數(shù)字表示日
E表示星期幾
HH表示使用兩位數(shù)字表示小時(shí)(24以內(nèi))
mm和ss分別表示分鐘和秒數(shù)
五、開源第三方庫(kù)Joda-TimeJoda-Time庫(kù)中的內(nèi)容還是很多的,我們簡(jiǎn)單了解下基本的使用即可,至于深入學(xué)習(xí)該庫(kù),大家可以自行嘗試,此處限于篇幅,不再贅述。在該庫(kù)中DateTime相當(dāng)于jdk中Calendar,主要完成對(duì)日期年月日的計(jì)算操作。首先我們通過簡(jiǎn)單易理解的方式創(chuàng)建DateTime的實(shí)例對(duì)象:
//2017-05-29 21:40
DateTime dt = new DateTime(2017,5,29,21,40);
//2017-05-29 21:40 50秒
DateTime dt2 = new DateTime(2017,5,29,21,40,50);
Java的常用的時(shí)間日期類:
Java8以前在java.util這個(gè)包里,主要包括Date、Calendar和TimeZone這幾個(gè)類;
Java8中推出了全新的日期時(shí)間類在java.time這個(gè)包里面,使用起來更加簡(jiǎn)單和安全,常用的有以下幾個(gè)
LocalDate:本地日期,只包括日期
LocalTime:本地時(shí)間,只包括時(shí)間
LocalDateTime:本地日期時(shí)間,包括日期和時(shí)間
ZonedDateTime:完整的本地日期時(shí)間,包括日期,時(shí)間,和時(shí)區(qū)
代碼片段:
獲取當(dāng)前日期
LocalDate localDate = LocalDate.now();
System.out.println("當(dāng)前日期為:" + localDate);//當(dāng)前日期為:2021-12-24
LocalDate提供了獲取年、月、日的快捷方法
System.out.println("當(dāng)前年為:" + localDate.getYear());//當(dāng)前年為:2021
System.out.println("當(dāng)前月為:" + localDate.getMonth());//當(dāng)前月為:JUNE
System.out.println("當(dāng)前月為:" + localDate.getMonthValue());//當(dāng)前月為:6
System.out.println("當(dāng)前日為當(dāng)月的第:" + localDate.getDayOfMonth() + "天");//當(dāng)前日為當(dāng)月的第:19天
System.out.println("當(dāng)前日為當(dāng)周的:" + localDate.getDayOfWeek());//當(dāng)前日為當(dāng)周的:FRIDAY
System.out.println("當(dāng)前日為當(dāng)年的第:" + localDate.getDayOfYear() + "天");//當(dāng)前日為當(dāng)年的第:171天
指定日期,這里的月對(duì)應(yīng)的就是實(shí)際的月
LocalDate zdDate = LocalDate.of(2020, 06, 18);
System.out.println("指定日期為:" + zdDate);//指定日期為:2021-12-24
判斷兩個(gè)日期是否相等,直接使用equals
boolean flag = localDate.equals(zdDate);
System.out.println("兩個(gè)日期是否相等" + flag);//兩個(gè)日期是否相等false
boolean isEqual = localDate.isEqual(zdDate);
System.out.println("兩個(gè)日期是否相等" + flag);//兩個(gè)日期是否相等false
判斷是否為閏年
System.out.println("是否為閏年:" + localDate.isLeapYear());//是否為閏年:true
只需要年月、月日,獲取方式有多種
YearMonth yearMonth = YearMonth.now();
System.out.println("當(dāng)前年月為:" + yearMonth);//當(dāng)前年月為:2020-06
YearMonth currentYearMonth1 = YearMonth.from(localDate);
System.out.println("當(dāng)前年月為:" + currentYearMonth1);//當(dāng)前年月為:2020-06
YearMonth zdYearMonth = YearMonth.of(zdDate.getYear(), zdDate.getMonth());
System.out.println("指定年月為:" + zdYearMonth);//指定年月為:2020-06
返回當(dāng)月天數(shù)
System.out.println("當(dāng)月天數(shù)為:" + yearMonth.lengthOfMonth());//當(dāng)月天數(shù)為:30
獲取月日
MonthDay monthDay = MonthDay.now();
System.out.println("當(dāng)前月日為:" + monthDay);//當(dāng)前月日為:--06-19
MonthDay currentMonthDay = MonthDay.from(localDate);
System.out.println("當(dāng)前月日為:" + currentMonthDay);//當(dāng)前月日為:--06-19
MonthDay zdMonthDay = MonthDay.of(zdDate.getMonth(), zdDate.getDayOfMonth());
System.out.println("指定月日為:" + zdMonthDay);//指定月日為:--06-18
日期的加減
通過LocalDate的plus方法增加年、月、周、天,也可以使用plus后直接跟年(plusYears)、月(plusMonths)、周(plusWeeks)、日(plusDays);minus方法減少年、月、周、天,也可以使用minus后直接跟年(minusYears)、月(minusMonths)、周(minusWeeks)、日(minusDays)
LocalDate nextWeek = localDate.plus(1, ChronoUnit.WEEKS);
System.out.println("一周后日期為:" + nextWeek);//一周后日期為:2020-06-26
LocalDate newWeek = localDate.plusWeeks(1);
System.out.println("一周后日期為:" + newWeek);//一周后日期為:2020-06-26
LocalDate oldWeek = localDate.minus(1, ChronoUnit.WEEKS);
System.out.println("一周前日期為:" + oldWeek);//一周前日期為:2020-06-12
獲取當(dāng)前時(shí)間,默認(rèn)時(shí)間格式為hh:mm:ss:nnn
LocalTime localTime = LocalTime.now();
System.out.println("當(dāng)前時(shí)間為:" + localTime);//當(dāng)前時(shí)間為:16:17:35.717
LocalTime zdLocalTime = LocalTime.of(14, 40, 30);
System.out.println("指定時(shí)間為:" + zdLocalTime);//指定時(shí)間為:14:40:30
時(shí)間的加減
通過LocalTime的plus方法增加時(shí)、分、秒,也可以使用plus后直接跟時(shí)(plusHours)、分(plusMinutes)、秒(plusSeconds);minus方法減少時(shí)、分、秒,也可以使用minus后直接跟時(shí)(minusHours)、分(minusMinutes)、秒(minusSeconds)
LocalTime newLocalTimeHour = localTime.plusHours(2);
System.out.println("2個(gè)小時(shí)后時(shí)間為:" + newLocalTimeHour);//2個(gè)小時(shí)后時(shí)間為:18:17:35.717
LocalTime oldLocalTimeHour = localTime.minusHours(2);
System.out.println("2個(gè)小時(shí)前時(shí)間為:" + oldLocalTimeHour);//2個(gè)小時(shí)前時(shí)間為:14:17:35.717
LocalTime newLocalTimeMinute = localTime.plusMinutes(20);
System.out.println("20分鐘后時(shí)間為:" + newLocalTimeMinute);//20分鐘后時(shí)間為:16:37:35.717
LocalTime oldLocalTimeMinute = localTime.minusMinutes(20);
System.out.println("20分鐘前時(shí)間為:" + oldLocalTimeMinute);//20分鐘前時(shí)間為:15:57:35.717
使用Duration計(jì)算兩個(gè)時(shí)間間隔多少小時(shí)、分鐘、秒
long minutes = Duration.between(zdLocalTime, localTime).toMinutes();
System.out.println("兩個(gè)時(shí)間間隔為:" + minutes);//兩個(gè)時(shí)間間隔為:97
使用Duration計(jì)算兩個(gè)日期間隔天數(shù)
LocalDate localDate = LocalDate.now();
LocalDate agoDate = LocalDate.of(2020, 12, 20);
long days = Duration.between(agoDate.atStartOfDay(), localDate.atStartOfDay()).toDays();
使用ChronoUnit計(jì)算兩個(gè)日期間隔天數(shù)
LocalDate localDate = LocalDate.now();
LocalDate agoDate = LocalDate.of(2020, 12, 20);
long days = ChronoUnit.DAYS.between(agoDate, localDate);
使用LocalDate計(jì)算兩個(gè)日期間隔天數(shù)
LocalDate localDate = LocalDate.now();
LocalDate agoDate = LocalDate.of(2020, 12, 20);
long days = localDate.toEpochDay() - agoDate.toEpochDay();
使用Period計(jì)算兩個(gè)日期間隔多少年、月、天
long days = Period.between(zdDate, localDate).getDays();
System.out.println("兩個(gè)日期間隔為:" + days);//兩個(gè)日期間隔為:1
Instant時(shí)間戳,默認(rèn)使用 UTC 時(shí)區(qū)(世界協(xié)調(diào)時(shí)間)
Instant instant = Instant.now();
System.out.println(instant);//2020-06-19T08:17:35.718Z
System.out.println("時(shí)間戳秒" + instant.getEpochSecond());//時(shí)間戳秒1592554655
System.out.println("時(shí)間戳毫秒" + instant.toEpochMilli());//時(shí)間戳毫秒1592554655718
System.out.println("在1970-01-01T00:00:00上加上20秒為:" + Instant.ofEpochSecond(20));//在1970-01-01T00:00:00上加上20秒為:1970-01-01T00:00:20Z
System.out.println("在1970-01-01T00:00:00上加上20毫秒為:" + Instant.ofEpochMilli(20));//在1970-01-01T00:00:00上加上20毫秒為:1970-01-01T00:00:00.020Z
使用LocalDateTime獲取時(shí)間戳
LocalDateTime localDateTime = LocalDateTime.now();
long second = localDateTime.toInstant(ZoneOffset.ofHours(8)).getEpochSecond();
System.out.println("當(dāng)前日期時(shí)間戳(精確到秒)為:" + second);
long milli = localDateTime.toInstant(ZoneOffset.ofHours(8)).toEpochMilli();
System.out.println("當(dāng)前日期時(shí)間戳(精確到毫秒)為:" + milli);
將時(shí)間戳轉(zhuǎn)化為L(zhǎng)ocalDateTime
LocalDateTime localDateTime1 = LocalDateTime.ofEpochSecond(second, 0, ZoneOffset.ofHours(8));
System.out.println("將時(shí)間戳(精確到秒)轉(zhuǎn)化為時(shí)間為:" + localDateTime1);
LocalDateTime localDateTime2 = LocalDateTime.ofInstant(Instant.ofEpochSecond(second), ZoneId.systemDefault());
System.out.println("將時(shí)間戳(精確到秒)轉(zhuǎn)化為時(shí)間為:" + localDateTime2);
LocalDateTime localDateTime3 = LocalDateTime.ofInstant(Instant.ofEpochMilli(milli), ZoneId.systemDefault());
System.out.println("將時(shí)間戳(精確到毫秒)轉(zhuǎn)化為時(shí)間為:" + localDateTime3);
時(shí)區(qū)偏移量計(jì)算
OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8));
System.out.println("偏移8小時(shí)后為:" + offsetDateTime);//偏移8小時(shí)后為:2020-06-19T16:30:55.011+08:00
Clock時(shí)鐘類獲取當(dāng)前時(shí)間戳
Clock clock = Clock.systemUTC();
System.out.println("Clock:" + clock);//Clock:SystemClock[Z]
System.out.println("Clock時(shí)間戳為" + clock.millis());//Clock時(shí)間戳為1592555455013
Clock defaultClock = Clock.systemDefaultZone();
System.out.println(defaultClock);//SystemClock[Asia/Shanghai]
System.out.println("Clock系統(tǒng)時(shí)間戳為" + defaultClock.millis());//Clock系統(tǒng)時(shí)間戳為1592555455013
判斷日期早于或晚于
boolean isBeforeDate = localDate.isBefore(zdDate);
System.out.println("當(dāng)前日期是否早于指定日期" + isBeforeDate);//當(dāng)前日期是否早于指定日期false
boolean isAfterDate = localDate.isAfter(zdDate);
System.out.println("當(dāng)前日期是否晚于指定日期" + isAfterDate);//當(dāng)前日期是否晚于指定日期true
判斷時(shí)間早于或晚于
boolean isBeforeTime = localTime.isBefore(zdLocalTime);
System.out.println("當(dāng)前時(shí)間是否早于指定時(shí)間" + isBeforeTime);//當(dāng)前時(shí)間是否早于指定時(shí)間false
boolean isAfterTime = localTime.isAfter(zdLocalTime);
System.out.println("當(dāng)前時(shí)間是否晚于指定時(shí)間" + isAfterTime);//當(dāng)前時(shí)間是否晚于指定時(shí)間true
獲取當(dāng)前日期時(shí)間
LocalDateTime localDateTime = LocalDateTime.now();
System.out.println("當(dāng)前日期時(shí)間為:" + localDateTime);//當(dāng)前日期時(shí)間為:2020-06-19T16:30:55.013
LocalDateTime zdLocalDateTime1 = LocalDateTime.of(2020, 06, 19, 15, 20, 30);
System.out.println("指定日期時(shí)間為:" + zdLocalDateTime1);//指定日期時(shí)間為:2020-06-19T15:20:30
LocalDateTime zdLocalDateTime2 = LocalDateTime.of(zdDate, zdLocalTime);
System.out.println("指定日期時(shí)間為:" + zdLocalDateTime2);//指定日期時(shí)間為:2020-06-18T14:40:30
LocalDateTime nextLocalDateTime = localDateTime.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
System.out.println("下一個(gè)周日的日期時(shí)間為:" + nextLocalDateTime);//下一個(gè)周日的日期時(shí)間為:2020-06-21T16:30:55.013
帶時(shí)區(qū)的日期時(shí)間
ZoneId america = ZoneId.of("America/New_York");
ZonedDateTime zonedDateTimeNewYork = ZonedDateTime.of(localDateTime, america);
System.out.println("指定時(shí)區(qū)日期時(shí)間為:" + zonedDateTimeNewYork);//指定時(shí)區(qū)日期時(shí)間為:2020-06-19T16:30:55.013-04:00[America/New_York]
格式化日期時(shí)間
String formatLocalDate = localDate.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
System.out.println("格式化日期為:" + formatLocalDate);//格式化日期為:20200619
String formatLocalDateTime = localDateTime.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
System.out.println("格式化日期時(shí)間為:" + formatLocalDateTime);//格式化日期時(shí)間為:20200619163055
將字符串轉(zhuǎn)換為日期
LocalDate parseLocalDate = LocalDate.parse("2020-06-19", DateTimeFormatter.ofPattern("yyyy-MM-dd"));
System.out.println("將指定的日期字符串轉(zhuǎn)換為日期為:" + parseLocalDate);//將指定的日期字符串轉(zhuǎn)換為日期為:2020-06-19
Date轉(zhuǎn)換為L(zhǎng)ocalDateTime、LocalDate
Date date = newDate();
Instant instant = date.toInstant();
LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
LocalDateTime localDateTime1 = Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDateTime();
LocalDate localDate1 = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()).toLocalDate();
LocalDate localDate2 = Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDate();
LocalDateTime、LocalDate轉(zhuǎn)換為Date
LocalDateTime localDateTime = LocalDateTime.now();
Date date = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
LocalDate localDate = LocalDate.now();
Date date1 = Date.from(localDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
總結(jié):
1.
Java8以前在java.util這個(gè)包里,主要包括Date、Calendar和TimeZone這幾個(gè)類;
Java8中推出了全新的日期時(shí)間類在java.time這個(gè)包里面,使用起來更加簡(jiǎn)單和安全,常用的有以下幾個(gè)
LocalDate:本地日期,只包括日期
LocalTime:本地時(shí)間,只包括時(shí)間
LocalDateTime:本地日期時(shí)間,包括日期和時(shí)間
ZonedDateTime:完整的本地日期時(shí)間,包括日期,時(shí)間,和時(shí)區(qū)
2.
古老的Date類
處理年月日的年歷類Calendar格式化字符串和日期對(duì)象的DateFormat格式轉(zhuǎn)換類好用的SimpleDateFormat實(shí)現(xiàn)類Joda-Time庫(kù)3.
多敲敲代碼,這在平常的開發(fā)中也經(jīng)常到。多看看源碼,也時(shí)常可以看到。4.
你不是研究的什么高端技術(shù),就多看看代碼,鍛煉自己的思維和邏輯,還是可以讓你拿到你滿意的薪水了,你盡管努力其他的請(qǐng)交給天意,等待時(shí)間來沉淀自己
