1. 程式人生 > >Java開發筆記(四十三)更好用的本地日期時間

Java開發筆記(四十三)更好用的本地日期時間

話說Java一連設計了兩套時間工具,分別是日期型別Date,以及日曆型別Calendar,按理說用在編碼開發中綽綽有餘了。然而隨著Java的日益廣泛使用,人們還是發現了它們的種種弊端。且不說先天不良的Date型別,單說後起之秀的Calendar型別,這個日曆工具在實際開發中仍然存在以下毛病:
1、日曆工具獲取當前月份的時候,與Date一樣都是從0開始計數,比如通過get方法獲得的一月份數值為0;
2、日曆工具獲取當天是星期幾的時候,星期日是排在最前面的,通過get方法獲得的星期日數值為1,而星期一數值居然是2!
3、日曆工具能夠表達的最小時間單位是毫秒,使得時間精度不夠高,難以用在更加精密的科學運算場合。
4、日曆工具沒有提供閏年的判斷方法。
5、日曆工具缺乏自己的格式化工具,居然還得藉助於Date型別那邊的格式化工具SimpleDateFormat,方能將日期時間按照指定格式輸出為字串。
總而言之,不管是Date還是Calendar,在解決複雜問題之時的編碼都很彆扭,故而每個Java工程基本要重新編寫一個日期處理工具DateUtil,在新工具內部封裝常見的日期處理操作,這樣才能滿足實際業務的開發要求。於是Date和Calendar兩個難兄難弟從JDK1.1開始並肩作戰,一路走到Java5、Java6乃至Java7,後來估摸著無可救藥了,乾爹Oracle一不做、二不休,終於在Java8推出了全新的日期時間型別,意圖通過新型別一勞永逸治好Date和Calendar的沉痾宿疾。
全新的日期時間型別不單單是一個型別,而是一個家族,它的成員主要有LocalDate、LocalTime、LocalDateTime等等,接下來分別介紹這幾個日期時間型別:

1、本地日期型別LocalDate

獲取本地日期的例項很簡單,呼叫該型別的now方法即可,並且顧名思義得到的是當前日期。通過本地日期獲取年月日的數值,就是日常生活中的習慣數字,例如一月份對應的數值是1,十二月份對應的數值是12,星期一對應的數值是1,星期日對應的數值是7等等。此外,本地日期額外提供了幾個常用的統計方法,包括:該日期所在的年份一共有多少天、該日期所在的月份一共有多少天、該日期所在的年份是否為閏年等。下面的程式碼便演示瞭如何從本地日期獲取各種數值的例子:

// 獲得本地日期的例項
LocalDate date = LocalDate.now();
System.out.println("date=" + date.toString());
// 獲得該日期所在的年份
int year = date.getYear();
System.out.println("year=" + year);
// 獲得該日期所在的月份。注意getMonthValue方法返回的是數字月份,而getMonth方法返回的是英文月份
int month = date.getMonthValue();
System.out.println("month=" + month + ", english month=" + date.getMonth());
// 獲得該日期所在的日子
int dayOfMonth = date.getDayOfMonth();
System.out.println("dayOfMonth=" + dayOfMonth);
// 獲得該日期在一年當中的序號
int dayOfYear = date.getDayOfYear();
System.out.println("dayOfYear=" + dayOfYear);
// 獲得該日期是星期幾。注意getDayOfWeek方法返回的是英文的星期幾,後面跟著的getValue方法才返回數字的星期幾
int dayOfWeek = date.getDayOfWeek().getValue();
System.out.println("dayOfWeek=" + dayOfWeek + ", english weekday=" + date.getDayOfWeek());
// 獲得該日期所在的年份一共有多少天
int lengthOfYear = date.lengthOfYear();
System.out.println("lengthOfYear=" + lengthOfYear);
// 獲得該日期所在的月份一共有多少天
int lengthOfMonth = date.lengthOfMonth();
System.out.println("lengthOfMonth=" + lengthOfMonth);
// 判斷該日期所在的年份是否為閏年
boolean isLeapYear = date.isLeapYear();
System.out.println("isLeapYear=" + isLeapYear);

除了建立處於當前日期的本地例項,LocalDate還支援建立指定日期的本地例項,就像以下程式碼示範的那樣:

// 構造一個指定年月日的日期例項
LocalDate dateManual = LocalDate.of(2018, 11, 22);
System.out.println("dateManual=" + dateManual.toString());

至於針對某個單位的數值,LocalDate也提供了專門的修改方法,例如以plus打頭的系列方法用來增加日期數值,以minus打頭的系列方法用來減少日期數值,以with打頭的系列方法用來設定日期數值,這些日期修改的具體用法示例如下:

dateManual = dateManual.plusYears(0); // 增加若干年份
dateManual = dateManual.plusMonths(0); // 增加若干月份
dateManual = dateManual.plusDays(0); // 增加若干日子
dateManual = dateManual.plusWeeks(0); // 增加若干星期
dateManual = dateManual.minusYears(0); // 減少若干年份
dateManual = dateManual.minusMonths(0); // 減少若干月份
dateManual = dateManual.minusDays(0); // 減少若干日子
dateManual = dateManual.minusWeeks(0); // 減少若干星期
dateManual = dateManual.withYear(2000); // 設定指定的年份
dateManual = dateManual.withMonth(12); // 設定指定的月份
dateManual = dateManual.withDayOfYear(1); // 設定當年的日子
dateManual = dateManual.withDayOfMonth(1); // 設定當月的日子

此外,作為一種日期型別,LocalDate一如既往地支援判斷兩個日期例項的早晚關係,比如equals方法用於判斷兩個日期是否相等,isBefore方法用於判斷A日期是否在B日期之前,isAfter方法用於判斷A日期是否在B日期之後等。具體的本地日期校驗程式碼如下所示:

// 判斷兩個日期是否相等
boolean equalsDate = date.equals(dateManual);
System.out.println("equalsDate=" + equalsDate);
// 判斷A日期是否在B日期之前
boolean isBeforeDate = date.isBefore(dateManual);
System.out.println("isBeforeDate=" + isBeforeDate);
// 判斷A日期是否在B日期之後
boolean isAfterDate = date.isAfter(dateManual);
System.out.println("isAfterDate=" + isAfterDate);
// 判斷A日期是否與B日期相等
boolean isEqualDate = date.isEqual(dateManual);
System.out.println("isEqualDate=" + isEqualDate);

2、本地時間型別LocalTime

前面介紹的LocalDate只能操作年月日,若要操作時分秒則需通過本地時間型別LocalTime。獲取本地時間的例項依然要呼叫該型別的now方法,接著就能通過該例項分別獲取對應的時分秒乃至納秒(一秒的十億分之一),下面便演示瞭如何呼叫LocalTime的基本方法:

// 獲得本地時間的例項
LocalTime time = LocalTime.now();
System.out.println("time=" + time.toString());
// 獲得該時間所在的時鐘
int hour = time.getHour();
System.out.println("hour=" + hour);
// 獲得該時間所在的分鐘
int minute = time.getMinute();
System.out.println("minute=" + minute);
// 獲得該時間所在的秒鐘
int second = time.getSecond();
System.out.println("second=" + second);
// 獲得該時間秒鐘後面的納秒單位。一秒等於一千毫秒,一毫秒等於一千微秒,一微秒等於一千納秒,算下來一秒等於十億納秒
int nano = time.getNano();
System.out.println("nano=" + nano);

如同本地日期LocalDate那樣,LocalTime也允許建立指定時分秒的時間例項,還支援單獨修改時鐘、分鐘、秒鐘和納秒。當然修改時間的途徑包括plus系列方法、minus系列方法、with系列方法等等,它們的呼叫方式示例如下:

// 構造一個指定時分秒的時間例項
LocalTime timeManual = LocalTime.of(14, 30, 25);
System.out.println("timeManual=" + timeManual.toString());
timeManual = timeManual.plusHours(0); // 增加若干時鐘
timeManual = timeManual.plusMinutes(0); // 增加若干分鐘
timeManual = timeManual.plusSeconds(0); // 增加若干秒鐘
timeManual = timeManual.plusNanos(0); // 增加若干納秒
timeManual = timeManual.minusHours(0); // 減少若干時鐘
timeManual = timeManual.minusMinutes(0); // 減少若干分鐘
timeManual = timeManual.minusSeconds(0); // 減少若干秒鐘
timeManual = timeManual.minusNanos(0); // 減少若干納秒
timeManual = timeManual.withHour(0); // 設定指定的時鐘
timeManual = timeManual.withMinute(0); // 設定指定的分鐘
timeManual = timeManual.withSecond(0); // 設定指定的秒鐘
timeManual = timeManual.withNano(0); // 設定指定的納秒

另外,LocalTime依然提供了equals、isBefore、isAfter等方法用於判斷兩個時間的先後關係,具體的方法呼叫如下所示:

// 判斷兩個時間是否相等
boolean equalsTime = time.equals(timeManual);
System.out.println("equalsTime=" + equalsTime);
// 判斷A時間是否在B時間之前
boolean isBeforeTime = time.isBefore(timeManual);
System.out.println("isBeforeTime=" + isBeforeTime);
// 判斷A時間是否在B時間之後
boolean isAfterTime = time.isAfter(timeManual);
System.out.println("isAfterTime=" + isAfterTime);

3、本地日期時間型別LocalDateTime

現在有了LocalDate專門處理年月日,又有了LocalTime專門處理時分秒,還需要一種型別能夠同時處理年月日和時分秒,它就是本地日期時間型別LocalDateTime。LocalDateTime基本等價於LocalDateTime與LocalTime的合集,它同時擁有二者的絕大部分方法,故這裡不再贅述。下面是建立該型別例項的程式碼片段,讀者可參考之前LocalDateTime與LocalTime的呼叫程式碼,嘗試補齊LocalDateTime的方法呼叫過程。
// 演示LocalDateTime的各種方法
private static void showLocalDateTime() {
// 獲得本地日期時間的例項
LocalDateTime datetime = LocalDateTime.now();
System.out.println(“datetime=” + datetime.toString());
// LocalDateTime的方法是LocalDate與LocalTime的合集,
// 也就是說LocalDate與LocalTime的大部分方法可以直接拿來給LocalDateTime使用,
// 因而下面不再演示LocalDateTime的詳細方法如何呼叫了。
// 注意LocalDateTime不提供lengthOfYear、lengthOfMonth、isLeapYear這三個方法。
}

更多Java技術文章參見《Java開發筆記(序)章節目錄