日期格式匯總
DateFormat java.text.DateFormat
public abstract class DateFormat extends Format
DateFormat 是日期/時間格式化子類的抽象類,它以與語言無關的方式格式化並解析日期或時間。日期/時間格式化子類(如 SimpleDateFormat)允許進行格式化(也就是日期 -> 文本)、解析(文本-> 日期)和標准化。將日期表示為 Date 對象,或者表示為從 GMT(格林尼治標准時間)1970 年 1 月 1 日 00:00:00 這一刻開始的毫秒數。
DateFormat 提供了很多類方法,以獲得基於默認或給定語言環境和多種格式化風格的默認日期/時間 Formatter。格式化風格包括 FULL、LONG、MEDIUM 和 SHORT。方法描述中提供了使用這些風格的更多細節和示例。
DateFormat 可幫助進行格式化並解析任何語言環境的日期。對於月、星期,甚至日歷格式(陰歷和陽歷),其代碼可完全與語言環境的約定無關。
要格式化一個當前語言環境下的日期,可使用某個靜態工廠方法:
myString = DateFormat.getDateInstance().format(myDate);
如果格式化多個日期,那么獲取該格式並多次使用它是更為高效的做法,這樣系統就不必多次獲取有關環境語言和國家/地區約定的信息了。
DateFormat df = DateFormat.getDateInstance();
for (int i = 0; i < myDate.length; ++i) {
output.println(df.format(myDate[i]) + "; ");
}
要格式化不同語言環境的日期,可在 getDateInstance() 的調用中指定它。
DateFormat df = DateFormat.getDateInstance(DateFormat.LONG, Locale.FRANCE);
還可使用 DateFormat 進行解析。
myDate = df.parse(myString);
另外還提供了一些其他靜態工廠方法。使用 getDateInstance 來獲取該國家/地區的標准日期格式。使用 getTimeInstance 可獲取該國家/地區的時間格式。使用 getDateTimeInstance 可獲取日期和時間格式。可以將不同選項傳入這些工廠方法,以控制結果的長度(從 SHORT 到 MEDIUM 到 LONG 再到 FULL)。確切的結果取決於語言環境,但是通常:
- SHORT 完全為數字,如 12.13.52 或 3:30pm
- MEDIUM 較長,如 Jan 12, 1952
- LONG 更長,如 January 12, 1952 或 3:30:32pm
- FULL 是完全指定,如 Tuesday、April 12、1952 AD 或 3:30:42pm PST。
如果想對格式化或解析施加更多的控制(或者給予用戶更多的控制),可以嘗試將從工廠方法所獲取的 DateFormat 強制轉換為 SimpleDateFormat。這適用於大多數國家/地區;只是要記住將其放入一個 try 代碼塊中,以防遇到特殊的格式。
還可以使用借助 ParsePosition 和 FieldPosition 的解析和格式化方法形式來
- 逐步地解析字符串的各部分。
- 對齊任意特定的字段,或者找出字符串在屏幕上的選擇位置。
同步
日期格式不是同步的。建議為每個線程創建獨立的格式實例。如果多個線程同時訪問一個格式,則它必須保持外部同步。
SimpleDateFormat java.text.SimpleDateFormat
SimpleDateFormat 是一個以與語言環境有關的方式來格式化和解析日期的具體類。它允許進行格式化(日期 -> 文本)、解析(文本 -> 日期)和規范化。
SimpleDateFormat 使得可以選擇任何用戶定義的日期-時間格式的模式。但是,仍然建議通過 DateFormat 中的 getTimeInstance、getDateInstance 或 getDateTimeInstance 來創建日期-時間格式器。每一個這樣的類方法都能夠返回一個以默認格式模式初始化的日期/時間格式器。可以根據需要使用 applyPattern 方法來修改格式模式。
日期和時間模式
日期和時間格式由日期和時間模式 字符串指定。在日期和時間模式字符串中,未加引號的字母 'A' 到 'Z' 和 'a' 到 'z' 被解釋為模式字母,用來表示日期或時間字符串元素。文本可以使用單引號 (') 引起來,以免進行解釋。"''" 表示單引號。所有其他字符均不解釋;只是在格式化時將它們簡單復制到輸出字符串,或者在解析時與輸入字符串進行匹配。定義了以下模式字母(所有其他字符 'A' 到 'Z' 和 'a' 到 'z' 都被保留):
- Text: 對於格式化來說,如果模式字母的數量大於等於 4,則使用完全形式;否則,在可用的情況下使用短形式或縮寫形式。對於解析來說,兩種形式都是可接受的,與模式字母的數量無關。
- Number: 對於格式化來說,模式字母的數量是最小的數位,如果數位不夠,則用 0 填充以達到此數量。對於解析來說,模式字母的數量被忽略,除非必須分開兩個相鄰字段。
- Year: 如果格式器的 Calendar 是格里高利歷,則應用以下規則。
對於格式化來說,如果模式字母的數量為 2,則年份截取為 2 位數,否則將年份解釋為 number。 對於解析來說,如果模式字母的數量大於 2,則年份照字面意義進行解釋,而不管數位是多少。因此使用模式 "MM/dd/yyyy",將 "01/11/12" 解析為公元 12 年 1 月 11 日。在解析縮寫年份模式("y" 或 "yy")時,SimpleDateFormat 必須相對於某個世紀來解釋縮寫的年份。這通過將日期調整為 SimpleDateFormat 實例創建之前的 80 年和之后 20 年范圍內來完成。例如,在 "MM/dd/yy" 模式下,如果 SimpleDateFormat 實例是在 1997 年 1 月 1 日創建的,則字符串 "01/11/12" 將被解釋為 2012 年 1 月 11 日,而字符串 "05/04/64" 將被解釋為 1964 年 5 月 4 日。在解析時,只有恰好由兩位數字組成的字符串(如 Character.isDigit(char) 所定義的)被解析為默認的世紀。其他任何數字字符串將照字面意義進行解釋,例如單數字字符串,3 個或更多數字組成的字符串,或者不都是數字的兩位數字字符串(例如"-1")。因此,在相同的模式下, "01/02/3" 或 "01/02/003" 解釋為公元 3 年 1 月 2 日。同樣,"01/02/-3" 解析為公元前 4 年 1 月 2 日。
否則,則應用日歷系統特定的形式。對於格式化和解析,如果模式字母的數量為 4 或大於 4,則使用日歷特定的 long form。否則,則使用日歷特定的 short or abbreviated form。
- Month: 如果模式字母的數量為 3 或大於 3,則將月份解釋為 text;否則解釋為 number。
同步
日期格式是不同步的。建議為每個線程創建獨立的格式實例。如果多個線程同時訪問一個格式,則它必須是外部同步的。
Date java.util.Date
public class Date extends Object implements Serializable, Cloneable, Comparable<Date>
類 Date 表示特定的瞬間,精確到毫秒。
在 JDK 1.1 之前,類 Date 有兩個其他的函數。它允許把日期解釋為年、月、日、小時、分鍾和秒值。它也允許格式化和解析日期字符串。不過,這些函數的 API 不易於實現國際化。從 JDK 1.1 開始,應該使用 Calendar 類實現日期和時間字段之間轉換,使用 DateFormat 類來格式化和解析日期字符串。Date 中的相應方法已廢棄。
盡管 Date 類打算反映協調世界時 (UTC),但無法做到如此准確,這取決於 Java 虛擬機的主機環境。當前幾乎所有操作系統都假定 1 天 = 24 × 60 × 60 = 86400 秒。但對於 UTC,大約每一兩年出現一次額外的一秒,稱為“閏秒”。閏秒始終作為當天的最后一秒增加,並且始終在 12 月 31 日或 6 月 30 日增加。例如,1995 年的最后一分鍾是 61 秒,因為增加了閏秒。大多數計算機時鍾不是特別的准確,因此不能反映閏秒的差別。
一些計算機標准是按照格林威治標准時 (GMT) 定義的,格林威治標准時和世界時 (UT) 是相等的。GMT 是標准的“民間”名稱;UT 是相同標准的“科學”名稱。UTC 和 UT 的區別是:UTC 是基於原子時鍾的,UT 是基於天體觀察的,兩者在實際應用中難分軒輊。因為地球的旋轉不是均勻的(它以復雜的方式減速和加速),所以 UT 始終不是均勻地流過。閏秒是根據需要引入 UTC 的,以便把 UTC 保持在 UT1 的 0.9 秒之內,UT1 是應用了某些更正的 UT 版本。還有其他的時間和日期系統;例如,基於衛星的全球定位系統 (GPS) 使用的時間刻度與 UTC 同步,但沒有 針對閏秒進行調整。
在類 Date 所有可以接受或返回年、月、日期、小時、分鍾和秒值的方法中,將使用下面的表示形式:
- 年份 y 由整數 y - 1900 表示。
- 月份由從 0 至 11 的整數表示;0 是一月、1 是二月等等;因此 11 是十二月。
- 日期(一月中的某天)按通常方式由整數 1 至 31 表示。
- 小時由從 0 至 23 的整數表示。因此,從午夜到 1 a.m. 的時間是 0 點,從中午到 1 p.m. 的時間是 12 點。
- 分鍾按通常方式由 0 至 59 的整數表示。
- 秒由 0 至 61 的整數表示;值 60 和 61 只對閏秒發生,盡管那樣,也只用在實際正確跟蹤閏秒的 Java 實現中。於按當前引入閏秒的方式,兩個閏秒在同一分鍾內發生是極不可能的,但此規范遵循 ISO C 的日期和時間約定。
從以下版本開始: JDK1.0
Calender java.util.Calendar
public abstract class Calendar extends Object implements Serializable, Cloneable, Comparable<Calendar>
Calendar 類是一個抽象類,它為特定瞬間與一組諸如 YEAR、MONTH、DAY_OF_MONTH、HOUR 等 日歷字段之間的轉換提供了一些方法,並為操作日歷字段(例如獲得下星期的日期)提供了一些方法。瞬間可用毫秒值來表示,它是距歷元(即格林威治標准時間 1970 年 1 月 1 日的 00:00:00.000,格里高利歷)的偏移量。
該類還為實現包范圍外的具體日歷系統提供了其他字段和方法。這些字段和方法被定義為 protected。
與其他語言環境敏感類一樣,Calendar 提供了一個類方法 getInstance,以獲得此類型的一個通用的對象。Calendar 的 getInstance 方法返回一個 Calendar 對象,其日歷字段已由當前日期和時間初始化:
Calendar rightNow = Calendar.getInstance();
Calendar 對象能夠生成為特定語言和日歷風格實現日期-時間格式化所需的所有日歷字段值,例如,日語-格里高里歷,日語-傳統日歷。Calendar 定義了某些日歷字段返回值的范圍,以及這些值的含義。例如,對於所有日歷,日歷系統第一個月的值是 MONTH == JANUARY。其他值是由具體子類(例如 ERA)定義的。有關此內容的細節,請參閱每個字段的文檔和子類文檔。
獲得並設置日歷字段值
可以通過調用 set 方法來設置日歷字段值。在需要計算時間值(距歷元所經過的毫秒)或日歷字段值之前,不會解釋 Calendar 中的所有字段值設置。調用 get、getTimeInMillis、getTime、add 和 roll 涉及此類計算。
寬松性
Calendar 有兩種解釋日歷字段的模式,即 lenient 和 non-lenient。當 Calendar 處於 lenient 模式時,它可接受比它所生成的日歷字段范圍更大范圍內的值。當 Calendar 重新計算日歷字段值,以便由 get() 返回這些值時,所有日歷字段都被標准化。例如,lenient 模式下的 GregorianCalendar 將 MONTH == JANUARY、DAY_OF_MONTH == 32 解釋為 February 1。
當 Calendar 處於 non-lenient 模式時,如果其日歷字段中存在任何不一致性,它都會拋出一個異常。例如,GregorianCalendar 總是在 1 與月份的長度之間生成 DAY_OF_MONTH 值。如果已經設置了任何超出范圍的字段值,那么在計算時間或日歷字段值時,處於 non-lenient 模式下的 GregorianCalendar 會拋出一個異常。
第一個星期
Calendar 使用兩個參數定義了特定於語言環境的 7 天制星期:星期的第一天和第一個星期中的最小一天(從 1 到 7)。這些數字取自構造 Calendar 時的語言環境資源數據。還可以通過為其設置值的方法來顯式地指定它們。
在設置或獲得 WEEK_OF_MONTH 或 WEEK_OF_YEAR 字段時,Calendar 必須確定一個月或一年的第一個星期,以此作為參考點。一個月或一年的第一個星期被確定為開始於 getFirstDayOfWeek() 的最早七天,它最少包含那一個月或一年的 getMinimalDaysInFirstWeek() 天數。第一個星期之前的各星期編號為 ...、-1、0;之后的星期編號為 2、3、...。注意,get() 返回的標准化編號方式可能有所不同。例如,特定 Calendar 子類可能將某一年第 1 個星期之前的那個星期指定為前一年的第 n 個星期。
日歷字段解析
在計算日歷字段中的日期和時間時,可能沒有足夠的信息用於計算(例如只有年和月,但沒有日),或者可能有不一致的信息( 例如 "Tuesday, July 15, 1996"(格林威治時間)——實際上,1996 年 7 月 15 日是星期一 )。Calendar 將解析日歷字段值,以便用以下方式確定日期和時間。如果日歷字段值中存在任何沖突,則 Calendar 將為最近設置的日歷字段提供優先權。以下是日歷字段的默認組合。將使用由最近設置的單個字段所確定的最近組合。
對於日期字段:
YEAR + MONTH + DAY_OF_MONTH
YEAR + MONTH + WEEK_OF_MONTH + DAY_OF_WEEK
YEAR + MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK
YEAR + DAY_OF_YEAR
YEAR + DAY_OF_WEEK + WEEK_OF_YEAR
對於時間字段:
HOUR_OF_DAY
AM_PM + HOUR
如果在選定的字段組合中,還有尚未設置值的任一日歷字段,那么 Calendar 將使用其默認值。每個字段的默認值可能依據具體的日歷系統而有所不同。例如,在 GregorianCalendar 中,字段的默認值與歷元起始部分的字段值相同:即 YEAR = 1970、MONTH = JANUARY、DAY_OF_MONTH = 1,等等。
注: 對於某些特別時間的解釋可能會有某些歧義,可以用下列方式解決:
- 23:59 是一天中的最后一分鍾,而 00:00 是下一天的第一分鍾。因此,1999 年 12 月 31 日的 23:59 < 2000 年 1 月 1 日的 00:00。
- 盡管從歷史上看不夠精確,但午夜也屬於 "am",,中午屬於 "pm",所以在同一天,12:00 am ( 午夜 ) < 12:01 am,12:00 pm ( 中午 ) < 12:01 pm。
字段操作
可以使用三種方法更改日歷字段:set()、add() 和 roll()。set(f, value)
將日歷字段 f 更改為 value。此外,它設置了一個內部成員變量,以指示日歷字段 f 已經被更改。盡管日歷字段 f 是立即更改的,但是直到下次調用 get()、getTime()、getTimeInMillis()、add() 或 roll() 時才會重新計算日歷的時間值(以毫秒為單位)。因此,多次調用 set() 不會觸發多次不必要的計算。使用 set() 更改日歷字段的結果是,其他日歷字段也可能發生更改,這取決於日歷字段、日歷字段值和日歷系統。此外,在重新計算日歷字段之后,get(f) 沒必要通過調用 set 方法返回 value 集合。具體細節是通過具體的日歷類確定的。
示例:假定 GregorianCalendar 最初被設置為 1999 年 8 月 31 日。調用 set(Calendar.MONTH, Calendar.SEPTEMBER) 將該日期設置為 1999 年 9 月 31 日。如果隨后調用 getTime(),那么這是解析 1999 年 10 月 1 日的一個暫時內部表示。但是,在調用 getTime() 之前調用 set(Calendar.DAY_OF_MONTH, 30) 會將該日期設置為 1999 年 9 月 30 日,因為在調用 set() 之后沒有發生重新計算。
add(f, delta)
將 delta 添加到 f 字段中。這等同於調用 set(f, get(f) + delta),但要帶以下兩個調整:
- Add 規則 1。調用后 f 字段的值減去調用前 f 字段的值等於 delta,以字段 f 中發生的任何溢出為模。溢出發生在字段值超出其范圍時,結果,下一個更大的字段會遞增或遞減,並將字段值調整回其范圍內。
- Add 規則 2。如果期望某一個更小的字段是不變的,但讓它等於以前的值是不可能的,因為在字段 f 發生更改之后,或者在出現其他約束之后,比如時區偏移量發生更改,它的最大值和最小值也在發生更改,然后它的值被調整為盡量接近於所期望的值。更小的字段表示一個更小的時間單元。HOUR 是一個比 DAY_OF_MONTH 小的字段。對於不期望是不變字段的更小字段,無需進行任何調整。日歷系統會確定期望不變的那些字段。
示例:假定 GregorianCalendar 最初被設置為 1999 年 8 月 31 日。調用 add(Calendar.MONTH, 13) 將日歷設置為 2000 年 9 月 30 日。Add 規則 1 將 MONTH 字段設置為 September,因為向 August 添加 13 個月得出的就是下一年的 September。因為在 GregorianCalendar 中,DAY_OF_MONTH 不可能是 9 月 31 日,所以 add 規則 2 將 DAY_OF_MONTH 設置為 30,即最可能的值。盡管它是一個更小的字段,但不能根據規則 2 調整 DAY_OF_WEEK,因為在 GregorianCalendar 中的月份發生變化時,該值也需要發生變化。
roll(f, delta)
將 delta 添加到 f 字段中,但不更改更大的字段。這等同於調用 add(f, delta),但要帶以下調整:
Roll 規則。在完成調用后,更大的字段無變化。更大的字段表示一個更大的時間單元。DAY_OF_MONTH 是一個比 HOUR 大的字段。使用模型
為了幫助理解 add() 和 roll() 的行為,假定有一個用戶界面組件,它帶有用於月、日、年和底層 GregorianCalendar 的遞增或遞減按鈕。如果從界面上讀取的日期為 1999 年 1 月 31 日,並且用戶按下月份的遞增按鈕,那么應該得到什么?如果底層實現使用 set(),那么可以將該日期讀為 1999 年 3 月 3 日。更好的結果是 1999 年 2 月 28 日。此外,如果用戶再次按下月份的遞增按鈕,那么該日期應該讀為 1999 年 3 月 31 日,而不是 1999 年 3 月 28 日。通過保存原始日期並使用 add() 或 roll(),根據是否會影響更大的字段,用戶界面可以像大多數用戶所期望的那樣運行。
從以下版本開始: JDK1.1