CodeGym /Java Blog /Toto sisi /如何不迷失在時間裡:DateTime 和 Calendar
John Squirrels
等級 41
San Francisco

如何不迷失在時間裡:DateTime 和 Calendar

在 Toto sisi 群組發布
你好!今天我們將開始使用一種以前從未遇到過的新數據類型,即日期。 如何不迷失在時間裡:DateTime 和 Calendar - 1我認為我不需要解釋什麼是約會。:) 原則上,我們可以將當前日期和時間存儲在普通的 Java String 中。

public class Main {
   public static void main(String[] args) {

       String date = "June 11, 2018";
       System.out.println(date);
   }
}
但是這種方法有很多缺點。該類String旨在處理文本,其方法適用於此任務。如果我們需要以某種方式操縱日期(例如,添加 2 小時),則String效果不佳。或者如果我們想在程序編譯時顯示當前的日期和時間。String在這裡也無濟於事:當您編寫代碼並運行它時,時間已經改變並且控制台將顯示錯誤信息。這就是為什麼 Java 的創建者提供了幾個類來處理日期和時間的原因。其中第一個是java.util.Date

日期類

我們指定了它的全名,因為另一個 Java 包有這個java.sql.Date類。不要混淆它們!您需要了解的第一件事是,它將日期存儲為自 1970 年 1 月 1 日以來經過的毫秒數。這個時間系統甚至有自己的名稱:“ Unix-time ” 一個相當有趣的方法,不會你不同意嗎?:) 第二件值得記住的事情是:如果Date使用默認構造函數創建對象,則結果表示創建對象時的當前日期和時間。還記得我們說過用 a 表示的日期String很難完成這樣的任務嗎?該類Date輕鬆處理它。

public class Main {
   public static void main(String[] args) {

       Date date = new Date();
       System.out.println(date);
   }
}
多次運行此代碼,您會看到時間反複變化。:) 這是可能的,因為時間是以毫秒為單位存儲的:它們是極小的時間單位,因此結果非常準確。該類Date的另一個構造函數:你可以傳遞從1970年1月1日00:00到所需日期的確切毫秒數,並創建一個相應的日期對象:

public class Main {
   public static void main(String[] args) {

       Date date = new Date(1212121212121L);
       System.out.println(date);
   }
}
控制台輸出: Fri May 30 04:20:12 GMT 2008 我們得到 2008 年 5 月 30 日。“Fri”表示星期幾(星期五,duh),GMT 是時區(格林威治標準時間)。毫秒作為longs 傳遞,因為毫秒數通常不適合int. 那麼,我們可能需要執行哪些日期操作?嗯,最明顯的當然是比較。確定一個日期是在另一個日期之前還是之後。這可以通過多種方式完成。例如,您可以調用該Date.getTime()方法,它返回自 1970 年 1 月 1 日午夜以來經過的毫秒數。只需在兩個 Date 對像上調用它並比較結果:

public class Main {
   public static void main(String[] args) {

       Date date1 = new Date();

       Date date2 = new Date();

       System.out.println((date1.getTime() > date2.getTime())?
               "date1 is later than date2" : "date1 is earlier than date2");
   }
}
輸出: date1 早於 date2 但還有一種更方便的方法,即使用 Date 類提供的特殊方法:before(),after()equals()。它們都返回一個布爾值。該before()方法檢查我們的日期是否早於作為參數傳遞的日期:

public class Main {
   public static void main(String[] args) throws InterruptedException {

       Date date1 = new Date();

       Thread.sleep(2000);// Suspend the program for 2 seconds
       Date date2 = new Date();

       System.out.println(date1.before(date2));
   }
}
控制台輸出: true 同樣,該after()方法檢查我們的日期是否晚於作為參數傳遞的日期:

public class Main {
   public static void main(String[] args) throws InterruptedException {

       Date date1 = new Date();

       Thread.sleep(2000);// Suspend the program for 2 seconds
       Date date2 = new Date();

       System.out.println(date1.after(date2));
   }
}
控制台輸出: false 在我們的示例中,我們“讓程序休眠”2 秒,這樣兩個日期就可以保證不同。在快速計算機上,創建date1和之間的時間date2可能小於一毫秒,導致 和 都before()返回after()false。但在這種情況下,該equals()方法將返回 true!畢竟,它會比較每個日期自 1970 年 1 月 1 日 00:00 以來的毫秒數。僅當對象與毫秒匹配時,它們才被認為是相等的:

public static void main(String[] args) {

   Date date1 = new Date();
   Date date2 = new Date();

   System.out.println(date1.getTime());
   System.out.println(date2.getTime());

   System.out.println(date1.equals(date2));
}
這是您需要注意的另一件事。Date如果您在Oracle網站上打開該類的文檔,您會看到它的許多方法和構造函數已被標記為Deprecated(即不推薦使用)。以下是 Java 的創建者對已棄用的部分類的看法:
“不建議程序員使用帶有 @Deprecated 註釋的程序元素,通常是因為它很危險,或者因為有更好的選擇。”
這並不意味著這些方法根本不能使用。如果您嘗試在 IDE 中使用已棄用的方法運行代碼,它很可能會起作用。例如,考慮已棄用的Date.getHours()方法,它返回與對象關聯的小時數Date

public static void main(String[] args) {

   Date date1 = new Date();

   System.out.println(date1.getHours());
}
如果您在 14:21(下午 2:21)啟動代碼,它將顯示數字 14。如您所見,已棄用的方法已被劃掉,但它仍然有效。不會刪除這些方法是為了不破壞使用它們的大量現有代碼。換句話說,這些方法既沒有被“破壞”也沒有被“移除”。根本不推薦使用它們,因為有更方便的替代方法。順便說一下,文檔特別提到了這個替代方案:
如何不迷失在時間裡:DateTime 和 Calendar - 2
該類的大部分Date方法已移至改進和擴展Calendar類。接下來我們將熟悉那個類。:)

日曆類

JDK 1.1 引入了一個新類:Calendar. 它使在 Java 中處理日期比以前更容易。Calendar我們將使用的類的唯一實現是GregorianCalendar類。它執行世界上大多數國家都遵守的公曆。它的主要優點是它可以以更方便的格式處理日期。例如,它可以:
  • 在當前日期上加一個月或一天
  • 檢查年份是否為閏年;
  • 返回日期的各個組成部分(例如,從整個日期中提取月份數)
  • 它還包含一個非常方便的常量系統(我們將在下面看到其中的許多)。
該類的另一個重要增強Calendar是它的Calendar.ERA常量:您可以指示共同時代(BC - 基督之前)或共同時代(AD - Anno Domini)之前的日期。讓我們用例子來看看這一切。讓我們創建一個calendar日期為 2017 年 1 月 25 日的對象:

public static void main(String[] args) {

  Calendar calendar = new GregorianCalendar(2017, 0 , 25);
}
Calendar類(以及與Date此相關的類)中,月份從零開始,因此我們將數字 0 作為第二個參數傳遞。在課堂上工作時Calendar,重要的是要了解這只是一個日曆,而不是一個單獨的日期。 如何不迷失在時間裡:DateTime 和 Calendar - 3 日期只是指示特定時間間隔的幾個數字。日曆是一個完整的系統,可以讓你用日期做很多事情。:) 如果您嘗試顯示Calendar對象,這將非常明顯: 輸出: java.util.GregorianCalendar[time=?,areFieldsSet=false,areAllFieldsSet=false,lenient=true,zone=sun.util.calendar.ZoneInfo[id="歐洲/倫敦",offset=0,dstSavings=0,useDaylight= false,transitions=79,lastRule=null],firstDayOfWeek=2,minimalDaysInFirstWeek=1,ERA=?,YEAR=2017,MONTH=0,WEEK_OF_YEAR=?,WEEK_OF_MONTH=?,DAY_OF_MONTH=25,DAY_OF_YEAR=?,DAY_OF_WEEK=? ,DAY_OF_WEEK_IN_MONTH=?,AM_PM=0,HOUR=0,HOUR_OF_DAY=0,MINUTE=0,SECOND=0,MILLISECOND=?,ZONE_OFFSET=?,DST_OFFSET=?] 看看你得到了多少信息 !一個日曆有一堆普通日期沒有的屬性,並且全部顯示出來(這就是該方法toString()在類中的工作Calendar)。如果您只需要從日曆中獲取一個簡單的日期,即一個Date對象,請使用Calendar.getTime()方法(名稱不是最合乎邏輯的,但你能做什麼?):

public static void main(String[] args) {

   Calendar calendar = new GregorianCalendar(2017, 0 , 25);
   Date date = calendar.getTime();
   System.out.println(date);
}
輸出: Wed Jan 25 00:00:00 GMT 2017 現在我們將日曆“縮減”為一個普通日期。讓我們更進一步。除了按數字指定月份外,您還可以使用Calendar類的常量字段值。這些常量是類的靜態字段,Calendar具有無法更改的預設值。這實際上是一個更好的選擇,因為使用它們可以提高代碼的可讀性。

public static void main(String[] args) {
   GregorianCalendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
}
Calendar.JANUARY是表示一年中月份的常量之一。使用這些命名常量,沒有人會忘記,例如,數字 3 表示四月,而不是我們喜歡稱之為三月的第三個月。只需編寫Calendar.APRIL即可。:) 所有日曆字段(數字、月份、分鐘、秒等)都可以使用該set()方法單獨指定。這種方法非常方便,因為Calendar類中的每個字段都有一個常量,生成的代碼非常容易閱讀。 在上一個示例中,我們創建了一個日期,但沒有為其設置時間。讓我們把時間定在 19:42:12

public static void main(String[] args) {
   Calendar calendar = new GregorianCalendar();
   calendar.set(Calendar.YEAR, 2017);
   calendar.set(Calendar.MONTH, 0);
   calendar.set(Calendar.DAY_OF_MONTH, 25);
   calendar.set(Calendar.HOUR_OF_DAY, 19);
   calendar.set(Calendar.MINUTE, 42);
   calendar.set(Calendar.SECOND, 12);

   System.out.println(calendar.getTime());
}
輸出: Wed Jan 25 19:42:12 GMT 2017 我們調用該set()方法,傳遞一個常量(取決於我們要更改的字段)和該字段的新值。事實證明,這種set()方法是一種“超級設置器”,它知道如何不僅為一個字段設置值,而且為多個字段設置值。:) 該類Calendar使用該add()方法來添加和減去值。您傳入要更改的字段和一個數字(確切地說是要從當前值中添加/減去多少)。例如,讓我們獲取比創建日期早 2 個月的日期:

public static void main(String[] args) {
   Calendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
   calendar.set(Calendar.HOUR, 19);
   calendar.set(Calendar.MINUTE, 42);
   calendar.set(Calendar.SECOND, 12);

   calendar.add(Calendar.MONTH, -2); // To subtract, pass a negative number
   System.out.println(calendar.getTime());
}
輸出: Fri Nov 25 19:42:12 GMT 2016 很好!我們在 2 個月前得到了約會。這不僅導致月份發生變化:年份也從 2017 年變為 2016 年。當然,在轉換日期時,會自動計算當前年份,無需您手動跟踪。但如果出於某種原因您需要禁用此行為,您可以這樣做。該方法可以在不影響剩餘值的情況下roll()添加和減去值。例如,像這樣:

public static void main(String[] args) {
   Calendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
   calendar.set(Calendar.HOUR, 10);
   calendar.set(Calendar.MINUTE, 42);
   calendar.set(Calendar.SECOND, 12);

   calendar.roll(Calendar.MONTH, -2);
   System.out.println(calendar.getTime());
}
我們做了與上一個示例完全相同的事情:我們從當前日期開始計算了 2 個月。但現在代碼做了一些不同的事情:月份從一月變成了十一月,但年份保持不變——2017 年!輸出: 2017 年 11 月 25 日星期六 10:42:12 GMT 繼續前進。正如我們上面所說,我們可以Calendar單獨獲取所有字段。我們使用以下get()方法執行此操作:

public static void main(String[] args) {
   GregorianCalendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
   calendar.set(Calendar.HOUR, 10);
   calendar.set(Calendar.MINUTE, 42);
   calendar.set(Calendar.SECOND, 12);

   System.out.println("Year: " + calendar.get(Calendar.YEAR));
   System.out.println("Month: " + calendar.get(Calendar.MONTH));
   System.out.println("Week in the month: " + calendar.get(Calendar.WEEK_OF_MONTH));// Week in this month?

   System.out.println("Day: " + calendar.get(Calendar.DAY_OF_MONTH));

   System.out.println("Hours: " + calendar.get(Calendar.HOUR));
   System.out.println("Minutes: " + calendar.get(Calendar.MINUTE));
   System.out.println("Seconds: " + calendar.get(Calendar.SECOND));
   System.out.println("Milliseconds: " + calendar.get(Calendar.MILLISECOND));

}
輸出: 年:2017 月:0 月中的星期:5 日:25 時:10 分:42 秒:12 毫秒:0 那麼, 除了Calendar班級的“super-setter”,還有一個“super-getter” ”。:) 當然,本課程的另一個有趣方面是使用時代。要創建“BC”日期,您需要使用Calendar.ERA字段 例如,讓我們為坎尼戰役創建一個日期,漢尼拔在該戰役中擊敗了羅馬軍隊。這發生在公元前 216 年 8 月 2 日:

public static void main(String[] args) {
   GregorianCalendar cannae = new GregorianCalendar(216, Calendar.AUGUST, 2);
   cannae.set(Calendar.ERA, GregorianCalendar.BC);

   DateFormat df = new SimpleDateFormat("MMM dd, yyy GG");
   System.out.println(df.format(cannae.getTime()));
}
這裡我們使用SimpleDateFormat類以我們更容易理解的格式打印日期(字母“GG”表示我們希望顯示時代)。輸出: 公元前 216 年 8 月 2 日。 該類Calendar有更多的方法和常量。您可以在文檔中閱讀有關它們的信息。如果不喜歡這種日期格式 Sat Nov 25 10:42:12 GMT 2017 ,那麼您可以使用SimpleDateFormat它輕鬆地使其成為您想要的樣子。

public static void main(String[] args) {

   SimpleDateFormat dateFormat = new SimpleDateFormat("EEEE, MMMM d, yyyy");
   Calendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
   calendar.set(Calendar.HOUR, 10);
   calendar.set(Calendar.MINUTE, 42);
   calendar.set(Calendar.SECOND, 12);

   calendar.roll(Calendar.MONTH, -2);
   System.out.println(dateFormat.format(calendar.getTime()));
}
輸出: 2017 年 11 月 25 日,星期六, 好多了,不是嗎?:)
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION