JavaRush /Java Blog /Random-TW /如何不迷失在時間中 - 日期時間和日曆

如何不迷失在時間中 - 日期時間和日曆

在 Random-TW 群組發布
你好!今天我們將開始使用我們以前從未遇到過的新資料類型,即日期。 如何不迷失在時間中 - 日期時間和日曆 - 1我認為沒有必要解釋什麼是日期:)原則上,在Java中用常規字串編寫當前日期和時間是很有可能的。
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中的另一個包中也有一個類別java.sql.Date。不要混淆!您需要了解的第一件事是,它儲存自 1970 年 1 月 1 日以來經過的日期(以毫秒為單位) 。這個日期甚至還有一個單獨的名稱——「Unix 時間」。這是一個非常有趣的方式,你不同意嗎?:) 要記住的第二件事:如果您Date使用空構造函數建立對象,則結果將是建立物件時的當前日期和時間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 08:20:12 MSD 2008
我們是2008年5月30日拿到的。「Fri」表示一週中的某一天-「Friday」(星期五),而 MSD-「莫斯科夏令時」(莫斯科夏令時)。毫秒以 格式傳輸long,因為它們的數字通常不適合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
但還有一種更方便的方法,就是使用類別的特殊方法Datebefore(),after()equals()。它們都以boolean. 此方法before()檢查我們的日期是否早於我們作為參數傳遞的日期:
public class Main {
   public static void main(String[] args) throws InterruptedException {

       Date date1 = new Date();

       Thread.sleep(2000);//pause 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);//pause the program for 2 seconds
       Date date2 = new Date();

       System.out.println(date1.after(date2));
   }
}
控制台輸出:

false
在我們的範例中,我們讓程式休眠 2 秒,以便確保兩個日期不同。date1在快速計算機上,創建和之間的時間date2可能小於一毫秒,在這種情況下, 和before()和 都會after()返回falseequals()但在這種情況下該方法會返回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));
}
這裡還有一些你需要注意的事情。如果您在 Oracle 網站上開啟該類的文檔Date,您將看到它的許多方法和建構子已被指定為Deprecated(「已棄用」)。這裡,請看:Class Date 以下是 Java 的創建者自己對那些已被棄用的類別部分的評價: 「用 @Deprecated 註解的程式元素是程式設計師不鼓勵使用的東西,通常是因為它很危險,或因為有更好的選擇。” 這並不意味著這些方法根本不能使用。此外,如果您自己嘗試在 IDEA 中使用它們運行程式碼,它很可能會起作用。讓我們以已棄用的方法為例,Date.getHours()該方法傳回物件的小時數Date
public static void main(String[] args) {

   Date date1 = new Date();

   System.out.println(date1.getHours());

}
例如,如果您運行程式碼時,時間是 14:21,它將顯示數字 14。如您所看到的,已棄用的方法被劃掉了,但它運作得很好。這些方法沒有完全刪除,以免破壞一堆已經使用它們編寫的程式碼。也就是說,這些方法並沒有被“破壞”或“刪除”,只是因為有更方便的替代方法,所以不建議使用它們。順便說一句,文檔中對此進行了描述: 如何不迷失在時間中 - 日期時間和日曆 - 2Date 類別的大多數方法已移至其改進的擴充版本 - 類別Calendar。我們會進一步了解他:)

Java日曆

Java 1.1 引進了一個新類別 - Calendar. 他讓 Java 中的日期處理比以前看起來更容易一些。我們將使用的類別的唯一實作Calendar是類別GregorianCalendar(它實現了公曆,世界上大多數國家都按照公曆生活)。它的主要便利之處在於它可以以更方便的格式處理日期。例如,他可以:
  • 在當前日期上添加一個月或一天
  • 檢查年份是否為閏年;
  • 取得單獨的日期組成部分(例如,從整個日期取得月份數)
  • 而且,它內部也開發了一個非常方便的常數系統(我們將在下面看到其中的許多常數)。
這個類別的另一個重要區別Calendar是它實現了一個常數Calendar.Era:您可以將日期設定為BC時代(“Before Christ” - 基督誕生之前,即“在我們的時代之前”)或AC(“After Christ” - “我們的時代」)。讓我們透過範例來看看這一切。讓我們建立一個日期為 2017 年 1 月 25 日的日曆:
public static void main(String[] args) {

  Calendar calendar = new GregorianCalendar(2017, 0 , 25);
}
類別中的月份Calendar(順便說一下Date,如 中所示)從零開始,因此我們傳遞數字 0 作為第二個參數。使用類別時最重要的Calendar是要理解這是一個日曆,而不是一個單獨的日期。 如何不迷失在時間中 - 日期時間和日曆 - 3日期只是代表特定時間段的一系列數字。日曆是一個完整的設備,您可以使用它對日期執行很多操作:) 如果您嘗試將 Calendar 物件輸出到控制台,則可以非常清楚地看到這一點: 輸出:

java.util.GregorianCalendar[time=?,areFieldsSet=false,areAllFieldsSet=false,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Europe/Moscow",offset=10800000,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)。如果,在工作時,您只需要從日曆中取得一個簡單的日期,即 object 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 MSK 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 MSK 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 a value - a negative number must be passed to the method
   System.out.println(calendar.getTime());
}
結論:

Fri Nov 25 19:42:12 MSK 2016
偉大的!我們把日期定在兩個月前。這樣一來,不僅月份變了,年份也變了,從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 個月。但現在代碼的工作方式有所不同:月份從 1 月更改為 11 月,但年份仍與 2017 年相同!結論:

Sat Nov 25 10:42:12 MSK 2017
更遠。上面我們說過,一個物件的所有欄位都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("Number of the week in the month: " + calendar.get(Calendar.WEEK_OF_MONTH));// serial number of the week in the month

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

   System.out.println("Watch: " + 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 
Порядковый номер недели в месяце: 4 
Число: 25 
Часы: 10 
Минуты: 42 
Секунды: 12 
Миллисекунды: 0
也就是說,類別中除了「super-setter」之外,Calendar還有一個「super-getter」:) 另一個有趣的點當然是與eras 一起工作。要創建日期“BC”,您需要使用字段Calendar.Era 例如,讓我們創建一個指示坎尼戰役的日期,漢尼拔在這場戰役中擊敗了羅馬軍隊。這發生在公元前 216 年 8 月 2 日。即:
public static void main(String[] args) {
   GregorianCalendar cannes = new GregorianCalendar(216, Calendar.AUGUST, 2);
   cannes.set(Calendar.ERA, GregorianCalendar.BC);

   DateFormat df = new SimpleDateFormat("dd MMM yyy GG");
   System.out.println(df.format(cannes.getTime()));
}
這裡我們使用該類別SimpleDateFormat以我們更容易理解的格式顯示日期(字母“GG”負責顯示時代)。結論:

02 авг 216 до н.э.
類別中Calendar有更多方法和常數 ,請在文件中閱讀它們:

迄今為止的換行符

要將 String 轉換為 Date,您可以使用 Java 幫助器類別 - SimpleDateFormat。這是將日期轉換為您定義的格式所需的類別。 如何不迷失在時間中 - 日期時間和日曆 - 5反過來,它與DateFormat非常相似。兩者之間唯一顯著的差異是 SimpleDateFormat 可用於格式化(將日期轉換為字串)並將字串解析為區域設定感知的日期,而 DateFormat 不支援區域設定。此外,DateFormat 是一個抽象類,為格式化和解析日期提供基本支持,而 SimpleDateFormat 是一個擴展 DateFormat 類別的特定類別。這是建立 SimpleDateFormat 物件並格式化 Date 的範例:
SimpleDateFormat formater = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = new Date(1212121212121L);

System.out.println(formatter.format(date));
在上面的範例中,我們使用了模式“yyyy-MM-dd HH:mm:ss”,這意味著:
  • 4 位數字表示年份 (yyyy);
  • 2 位數字表示月份 (MM);
  • 2 位數字表示日期 (dd);
  • 2 位數字表示小時,採 24 小時格式 (HH);
  • 2 位數字表示分鐘 (mm);
  • 2 位數字表示秒 (ss)。
分隔標記和圖案符號的順序被保留。控制台輸出:
2008-05-30 08:20:12
SimpleDateFormat類別有相當多的 模板字母。為了避免您感到困惑,我們將它們收集在表格中:
象徵 描述 例子
G 時代(英語在地化 - AD 和 BC) 廣告
y 年份(4 位數字) 2020年
yy 年份(最後 2 位數字) 20
yyyy 年份(4 位數字) 2020年
中號 月份數字(不含前導零) 8
毫米 月份編號(如果月份編號 < 10,則帶有前導零) 04
MMM 三個字母的月份縮寫(根據本地化)
MMMM 月全名 六月
w 一年中的第幾週(不含前導零) 4
WW 一年中的第幾週(帶前導零) 04
一個月中的一週(不含前導零) 3
WW 一個月中的一週(帶前導零) 03
D 一年中的某一天 67
d 一個月中的某一天(不含前導零) 9
DD 一個月中的某一天(帶前導零) 09
F 該月中的星期幾(不含前導零) 9
FF 一個月中的星期幾(帶前導零) 09
星期幾(縮寫)
電子電器設備 星期幾(完整) 星期五
星期幾(不含前導零) 5
一週中的第幾天(帶前導零) 05
A AM/PM 標記 是。
H 24 小時格式的小時,不含前導零 6
赫赫 24 小時格式的時鐘,附前導零 06
k 24 小時格式的小時數 18
K 12 小時格式的小時數 6
H 12 小時格式的時間,不含前導零 6
呵呵 12 小時格式的時間,附前導零 06
不帶前導零的分鐘 32
毫米 分鐘帶前導零 32
s 沒有前導零的秒 十一
SS 帶前導零的秒 十一
S 毫秒 第297章
z 時區 歐洲東部時間
Z RFC 822 格式的時區 300
模式字元組合範例:
樣本 例子
日-月-年 2020年1月11日
年-月-日 2019-10-01
時:分:秒.SSS 23:59.59.999
yyyy-MM-dd HH:mm:ss 2018-11-30 03:09:02
yyyy-MM-dd HH:mm:ss.SSS 2016-03-01 01:20:47.999
yyyy-MM-dd HH:mm:ss.SSS Z 2013-13-13 23:59:59.999 +0100
如果你在格式上犯了一個小錯誤,你就可能成為 java.text.ParseException 的所有者,而這並不是一個特別令人愉快的成就。好了, SimpleDateFormat的簡短介紹就結束了——讓我們回到java string to date的翻譯。 SimpleDateFormat為我們提供了這樣的功能,我們將逐步完成這個過程。
  1. 建立需要設定日期的行:

    String strDate = "Sat, April 4, 2020";
  2. 我們創建一個新的SimpleDateFormat對象,其模板與字串中的內容相符(否則我們將無法解析它):

    SimpleDateFormat formatter = new SimpleDateFormat("EEE, MMMM d, yyyy", Locale.ENGLISH);

    正如您所看到的,我們這裡有一個 Locale 參數。如果我們省略它,它將使用預設的區域設置,該區域設置並不總是英語。

    如果語言環境與輸入字串不匹配,則綁定到該語言的字串資料(例如我們的MonApril)將不會被識別,並且會拋出 java.text.ParseException,即使模式匹配也是如此。

    但是,如果我們使用非特定於語言的模板,則不必指定格式。例如 - yyyy-MM-dd HH:mm:ss

  3. 我們使用格式化程式建立一個日期,然後格式化程式從輸入字串中解析它:

    try {
      Date date = formatter.parse(strDate);
      System.out.println(date);
    }
    catch (ParseException e) {
      e.printStackTrace();
    }

    控制台輸出:

    
    Sat Apr 04 00:00:00 EEST 2020

    嗯...但是格式不再一樣了!

    為了製作相同的格式,我們再次使用格式化程式:

    System.out.println(formatter.format(date));

    控制台輸出:

    
    Sat, April 4, 2020

SimpleDateFormat 與行事曆

SimpleDateFormat 可讓您格式化您建立的所有日期和日曆物件以供日後使用。讓我們考慮一下與eras 合作這樣一個有趣的觀點。要建立「BC」日期,您需要使用Calendar.Era欄位。例如,讓我們建立一個表示坎尼戰役的日期,漢尼拔在這場戰役中擊敗了羅馬軍隊。這發生在公元前 216 年 8 月 2 日。即:
public static void main(String[] args) {
   GregorianCalendar cannes = new GregorianCalendar(216, Calendar.AUGUST, 2);
   cannes.set(Calendar.ERA, GregorianCalendar.BC);

   DateFormat df = new SimpleDateFormat("dd MMM yyy GG");
   System.out.println(df.format(cannes.getTime()));
}
這裡我們使用 SimpleDateFormat 類別以我們更容易理解的格式顯示日期(如上所示,字母「GG」負責顯示時代)。結論:

02 авг 216 до н.э.

Java 日期格式

這是另一個案例。假設這種日期格式不適合我們:

Sat Nov 25 10:42:12 MSK 2017
所以就是這樣。使用我們的 java 日期格式功能,您可以輕鬆地將其變更為您自己的日期格式:
public static void main(String[] args) {

   SimpleDateFormat dateFormat = new SimpleDateFormat("EEEE, d MMMM 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()));
}
結論:

суббота, 25 Ноябрь 2017
好多了,對吧?:)
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION