程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> 計算機程序的思維邏輯 (32),思維32

計算機程序的思維邏輯 (32),思維32

編輯:JAVA綜合教程

計算機程序的思維邏輯 (32),思維32


Joda-Time
上節介紹了JDK API中的日期和時間類,我們提到了JDK API的一些不足,並提到,實踐中有一個廣泛使用的日期和時間類庫,Joda-Time,本節我們就來介紹Joda-Time。俗話說,工欲善其事,必先利其器,Joda-Time就是操作日期和時間的一把利器。

Joda-Time的官網是http://www.joda.org/joda-time/。它的基本概念和工作原理與上節介紹的是類似的,比如說,都有時刻和年歷的概念,都有時區和Locale的概念,主要工作,都是在毫秒和年月日等年歷信息之間進行相互轉換。

Joda-Time的主要類和Java API的類也有一個粗略的對應關系:

Joda-Time
Java API
說明
Instant Date 時刻 DateTime Calendar 年歷 DateTimeZone TimeZone 時區 DateTimeFormatter DateFormat 格式化

需要說明的是,這只是一個非常粗略的對應,並不嚴謹,Joda-Time也還有非常多的其他類。

雖然基本概念是類似的,但API的設計卻有很大不同,Joda-Time的API更容易理解和使用,代碼也更為簡潔,下面我們會通過例子來說明。

另外,與Date/Calendar的設計有一個很大的不同,Joda-Time中的主要類都被設計為了不可變類,我們之前介紹過不可變類,包裝類/String都是不可變類,不可變類有一個很大的優點,那就是簡單、線程安全,所有看似的修改操作都是通過創建新對象來實現的。

本文並不打算全面介紹Joda-Time的每個類,相反,我們主要通過一些例子來說明其基本用法,體會其方便和強大,同時,學習其API的設計理念。

創建對象

新建一個DateTime對象,表示當前日期和時間:

DateTime dt = new DateTime();

新建一個DateTime對象,給定年月日時分秒等信息:

//2016-08-18 15:20
DateTime dt = new DateTime(2016,8,18,15,20);

//2016-08-18 15:20:47
DateTime dt2 = new DateTime(2016,8,18,15,20,47);

//2016-08-18 15:20:47.345
DateTime dt3 = new DateTime(2016,8,18,15,20,47,345);

獲取日歷信息

與Calendar不同,DateTime為每個日歷字段都提供了單獨的方法,取值的范圍也都是符合常識的,易於理解和使用,來看代碼:

//2016-08-18 15:20:47.345
DateTime dt = new DateTime(2016,8,18,15,20,47,345);
System.out.println("year: "+dt.getYear());
System.out.println("month: "+dt.getMonthOfYear());
System.out.println("day: "+dt.getDayOfMonth());
System.out.println("hour: "+dt.getHourOfDay());
System.out.println("minute: "+dt.getMinuteOfHour());
System.out.println("second: "+dt.getSecondOfMinute());
System.out.println("millisecond: " +dt.getMillisOfSecond());
System.out.println("day_of_week: " +dt.getDayOfWeek());

輸出為:

year: 2016
month: 8
day: 18
hour: 15
minute: 20
second: 47
millisecond: 345
day_of_week: 4

每個字段的輸出都符合常識,且保持一致,都是從1開始,比如dayOfWeek,周四就是4, 易於理解。

格式化

Java API中,格式化必須使用一個DateFormat對象,而Joda-Time中,DateTime自己就有一個toString方法,可以接受一個pattern參數,看例子:

//2016-08-18 14:20:45.345
DateTime dt = new DateTime(2016,8,18,14,20,45,345);
System.out.println(dt.toString("yyyy-MM-dd HH:mm:ss"));

輸出為:

2016-08-18 14:20:45

Joda-Time也有與DateFormat類似的類,看代碼:

DateTime dt = new DateTime(2016,8,18,14,20);
DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm");
System.out.println(formatter.print(dt));

輸出為:

2016-08-18 14:20

這裡有兩個類,一個是DateTimeFormat,另一個是DateTimeFormatter。DateTimeFormatter是具體的格式化類,提供了print方法將DateTime轉換為字符串。DateTimeFormat是一個工廠類,專門生成具體的格式化類,除了forPattern方法,它還有一些別的工廠方法,本文就不介紹了。

程序設計的一個基本思維是關注點分離,程序一般總是比較復雜的,涉及方方面面,解決的思路就是分解,將復雜的事情盡量分解為不同的方面,或者說關注點,各個關注點之間耦合度要盡量低。

具體來說,對應到Java,每個類應該只關注一點。上面的例子中,因為生成DateTimeFormatter的方式比較多,就將生成DateTimeFormatter這個事單獨拿了出來,就有了工廠類DateTimeFormat,只關注生產DateTimeFormatter,Joda-Time中還有別的工廠類,比如ISODateTimeFormat,工廠類是一種常見的設計模式。

除了將DateTime轉換為字符串,DateTimeFormatter還可以將字符串轉化為DateTime,代碼如下:

DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm");
DateTime dt = formatter.parseDateTime("2016-08-18 14:20");

與上節介紹的格式化類不同,Joda-Time的DateTimeFormatter是線程安全的,可以安全的被多個線程共享。

設置和修改時間

上節介紹Calendar時提到,修改時期和時間有兩種方式,一種是直接設置絕對值,另一種是在現有值的基礎上進行相對增減操作,DateTime也支持這兩種方式。

不過,需要注意的是,DateTime是不可變類,修改操作是通過創建並返回新對象來實現的,原對象本身不會變。

我們來看一些例子。

調整時間為下午3點20

DateTime dt = new DateTime();
dt = dt.withHourOfDay(15).withMinuteOfHour(20);

DateTime有很多withXXX方法來設置絕對時間。DateTime中非常方便的一點是,方法的返回值是修改後的DateTime對象,可以接著進行下一個方法調用,這樣,代碼就非常簡潔,也非常容易閱讀,這種一種流行的設計風格,稱為流暢接口 (Fluent Interface),相比之下,使用Calendar,就必須要寫多行代碼,比較臃腫,下面我們會看到更多例子。

另外,注意需要將最後的返回值賦值給dt,否則dt的值不會變。

三小時五分鐘後

DateTime dt = new DateTime().plusHours(3).plusMinutes(5);

DateTime有很多plusXXX和minusXXX方法,用於相對增加和減少時間。

今天0點

DateTime dt = new DateTime().withMillisOfDay(0);
System.out.println(dt.toString("yyyy-MM-dd HH:mm:ss.SSS"));

當前時間為2016-08-18,所以輸出為

2016-08-18 00:00:00.000

withMillisOfDay直接設置當天毫秒信息,會同時將時分秒等信息進行修改。

下周二上午10點整

DateTime dt = new DateTime().plusWeeks(1).withDayOfWeek(2)
        .withMillisOfDay(0).withHourOfDay(10);

明天最後一刻

DateTime dt = new DateTime().plusDays(1).millisOfDay().withMaximumValue();
System.out.println(dt.toString("yyyy-MM-dd HH:mm:ss.SSS"));

當前時間為2016-08-18,所以輸出為

2016-08-19 23:59:59.999

這裡說明一下,plusDays(1)容易理解,設為第二天。millisOfDay()的返回值比較特別,它是一個屬性,具體類為DateTime的一個內部類Property,這個屬性代表當天毫秒信息,這個屬性有一些方法,可以接著對日期進行修改,withMaximumValue就是將該屬性的值設為最大值。

這樣,代碼是不是非常簡潔?除了millisOfDay,DateTime還有很多類似屬性。我們來看更多的例子。

本月最後一天最後一刻

DateTime dt = new DateTime().dayOfMonth().withMaximumValue().millisOfDay().withMaximumValue();

下個月第一個周一的下午5點整

DateTime dt = new DateTime().plusMonths(1).dayOfMonth().withMinimumValue()
        .plusDays(6).withDayOfWeek(1).withMillisOfDay(0).withHourOfDay(17);

我們稍微解釋下:

new DateTime().plusMonths(1).dayOfMonth().withMinimumValue()

將時間設為了下個月的第一天。.plusDays(6).withDayOfWeek(1)將時間設為第一個周一。

時間段的計算

JDK API中沒有關於時間段計算的類,而Joda-Time包含豐富的表示時間段和用於時間段計算的方法,我們來看一些例子。

計算兩個時間之間的差

Joda-Time有一個類,Period,表示按日歷信息的時間段,看代碼:

DateTime start = new DateTime(2016,8,18,10,58);
DateTime end = new DateTime(2016,9,19,12,3);
Period period = new Period(start,end);        
System.out.println(period.getMonths()+"月"+period.getDays()+"天"
        +period.getHours()+"小時"+period.getMinutes()+"分");

輸出為:

1月1天1小時5分

只要給定起止時間,Period就可以自動計算出來,兩個時間之間有多少月、多少天、多少小時等。

如果只關心一共有多少天,或者一共有多少周呢?Joda-Time有專門的類,比如Years用於年,Days用於日,Minutes用於分鐘,來看一些例子。

根據生日計算年齡

年齡只關心年,可以使用Years,看代碼:

DateTime born = new DateTime(1990,11,20,12,30);
int age = Years.yearsBetween(born, DateTime.now()).getYears();

計算遲到分鐘數

假定早上9點是上班時間,過了9點算遲到,遲到要統計遲到的分鐘數,怎麼計算呢?看代碼:

int lateMinutes = Minutes.minutesBetween(
        DateTime.now().withMillisOfDay(0).withHourOfDay(9),
        DateTime.now()).getMinutes(); 

單獨的日期和時間類

我們一直在用DateTime表示完整的日期和時間,但在年齡的例子中,只需要關心日期,在遲到的例子中,只需要關心時間,Joda-Time分別有單獨的日期類LocalDate和時間類LocalTime。

使用LocalDate計算年齡

LocalDate born = new LocalDate(1990,11,20);
int age = Years.yearsBetween(born, LocalDate.now()).getYears();

使用LocalTime計算遲到時間

int lateMinutes = Minutes.minutesBetween(
        new LocalTime(9,0),
        LocalTime.now()).getMinutes();

LocalDate和LocalTime可以與DateTime進行相互轉換,比如:

DateTime dt = new DateTime(1990,11,20,12,30);
LocalDate date = dt.toLocalDate();
LocalTime time = dt.toLocalTime();
DateTime newDt = DateTime.now().withDate(date).withTime(time);

與JDK API的互操作

Joda-Time中的類可以方便的與JDK中的類進行相互轉換。

JDK -> Joda

Date、Calendar可以方便的轉換為DateTime對象:

DateTime dt = new DateTime(new Date());
DateTime dt2 = new DateTime(Calendar.getInstance());

也可以方便的轉換為LocalDate和LocalTime對象:

LocalDate.fromDateFields(new Date());
LocalDate.fromCalendarFields(Calendar.getInstance());
LocalTime.fromDateFields(new Date());
LocalTime.fromCalendarFields(Calendar.getInstance());

Joda -> JDK

DateTime對象也可以方便的轉換為JDK對象:

DateTime dt = new DateTime();
Date date = dt.toDate();
Calendar calendar = dt.toCalendar(Locale.CHINA);

LocalDate也可以轉換為Date對象:

LocalDate localDate = new LocalDate(2016,8,18);
Date date = localDate.toDate();

小結

本節介紹了Joda-Time,一個方便和強大的日期和時間類庫,本文並未全面介紹,主要是通過一些例子展示了其基本用法。

我們也介紹了Joda-Time之所以易用的一些設計思維,比如,關注點分離,為方便操作,提供單獨的功能明確的類和方法,設計API為流暢接口,設計為不可變類,使用工廠類等。

下一節,我們來討論一個有趣的話題,那就是隨機。

----------------

未完待續,查看最新文章,敬請關注微信公眾號“老馬說編程”(掃描下方二維碼),從入門到高級,深入淺出,老馬和你一起探索Java編程及計算機技術的本質。用心寫作,原創文章,保留所有版權。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved