Java 日期時間處理
Java 日期時間處理
標籤: Java基礎
Date
java.util.Date對象表示一個精確到毫秒的瞬間; 但由於Date從JDK1.0起就開始存在了,曆史悠久,而且功能強大(既包含日期,也包含時間),所以他的大部分構造器/方法都已Deprecated,因此就不再推薦使用(如果貿然使用的話,可能會出現效能/安全方面的問題);下面我僅介紹它還剩下的為數不多的幾個方法(這些方法的共同點是Date與毫秒值的轉換):
構造器
Date(): 在底層調用
System.currentTimeMillis()作為日期參數.
Date(long date): 根據指定的long整數(從1970-1-1 00:00:00以來經過的毫秒數)來產生Date對象.
方法
boolean after(Date when): 測試this日期是否在指定日期when之後;
boolean before(Date when): 測試this日期是否在指定日期when之前;
long getTime(): 擷取從1979-01-01 00:00:00 到Date對象之間經過的毫秒值;
void setTime(long time): 設定時間,time含義上同.
/** * Created by jifang on 15/12/30. */public class DateTest { @Test public void test() { Date dateBefore = new Date(); Date dateAfter = new Date(System.currentTimeMillis() + 1); System.out.println("before: " + dateBefore.getTime()); System.out.println("after: " + dateAfter.getTime()); System.out.println(dateBefore.before(dateAfter)); System.out.println(dateAfter.after(dateBefore)); dateBefore.setTime(System.currentTimeMillis()); System.out.println(dateBefore.getTime()); System.out.println(dateBefore.before(dateAfter)); }}
Calendar
由於Date存在缺陷,所以JDK又提供了java.util.Calendar來處理日期和時間.Calendar是一個抽象類別,是所有日曆類的模板,因此,我們可以繼承Calendar來實現其他的曆法(比如陰曆);
Java中提供了一種Calendar的預設實現java.util.GregorianCalendar格裡高利日曆(其實JDK還預設提供了一款日本曆法java.util.JapaneseImperialCalendar),也就是我們所說的西曆. 使用Calendar.getInstance();擷取的就是預設的GregorianCalendar,getInstance()方法的內部會調用cal = new GregorianCalendar(zone, aLocale);來產生一個格裡高利日曆執行個體.
Calendar還可以和Date自由轉換.
public class CalendarTest { @Test public void test() { Calendar calendar = Calendar.getInstance(); Date date = calendar.getTime(); Calendar newCalendar = Calendar.getInstance(); newCalendar.setTime(date); System.out.println(calendar.get(Calendar.DATE)); }}
Calendar類提供了大量訪問/修改日期/時間的方法, 常用的方法如下:
| Method |
Description |
void add(int field, int amount) |
Adds or subtracts the specified amount of time to the given calendar field, based on the calendar’s rules. |
int get(int field) |
Returns the value of the given calendar field. |
int getActualMaximum(int field) |
Returns the maximum value that the specified calendar field could have, given the time value of this Calendar. |
int getActualMinimum(int field) |
Returns the minimum value that the specified calendar field could have, given the time value of this Calendar. |
void roll(int field, int amount) |
Adds the specified (signed) amount to the specified calendar field without changing larger fields. |
void set(int field, int value) |
Sets the given calendar field to the given value. |
void set(int year, int month, int date) |
Sets the values for the calendar fields YEAR, MONTH, and DAY_OF_MONTH. |
void set(int year, int month, int date, int hourOfDay, int minute, int second) |
Sets the values for the fields YEAR, MONTH, DAY_OF_MONTH, HOUR, MINUTE, and SECOND. |
void setTimeInMillis(long millis) |
Sets this Calendar’s current time from the given long value. |
long getTimeInMillis() |
Returns this Calendar’s time value in milliseconds. |
上面的很多方法都需要一個int類型的field參數, field是Calendar類的類變數, 如:Calendar.DATE Calendar.MONTH Calendar.HOUR Calendar.DAY_OF_WEEK, 但需要指出的是Calendar.MONTH月份的起始值不是1, 而是0(一月:0, 二月:1 …), Calendar.DAY_OF_WEEK代表的星期, 起始值是周日(周日:1, 周一:2 …)(其他細節請參考JDK文檔).
注意:
如果
Calendar沒有設定相關的值, 就以當前系統時間來設定.
add(int field, int amount)的功能非常強大, 如果需要增加某欄位, 則讓amount為正數, 如果要減少某欄位的值, 讓amount為負數. 且當超出他的允許範圍時, 會發生
進位.
roll()的含義與用法和
add()的類似,但是當被修改的欄位超出它允許的範圍時, 他不會進位.
set(int field, int value)方法具有延遲修改的功能:他內部設定了一個成員變數,以指示日曆欄位field已經被修改,但是該Calendar所代表的時間不會立即修改, 他會直到下次調用
get/getTime/getTimeInMillis/add/roll時才會重新計算日曆時間.
public int get(int field){ complete(); return internalGet(field);}
public long getTimeInMillis() { if (!isTimeSet) { updateTime(); } return time;}
測試
public class CalendarTest { @Test public void test() { Calendar calendar = Calendar.getInstance(); calendar.set(2011, Calendar.JULY, 31); calendar.set(Calendar.MONTH, Calendar.SEPTEMBER); // 將下面注釋放開, 再測試 // System.out.println(calendar.get(Calendar.MONTH) + 1 + "月" + calendar.get(Calendar.DATE) + "日"); calendar.set(Calendar.DATE, 5); System.out.println(calendar.get(Calendar.MONTH) + 1 + "月" + calendar.get(Calendar.DATE) + "日"); }}
日期格式化
完成字串與日期對象的轉化(format/parse)
DateFormat
java.text.DateFormat是一個抽象類別, 他提供了如下幾個方法擷取DateFormat對象.
| 方法 |
描述 |
static DateFormat getDateInstance() |
Gets the date formatter with the default formatting style for the default locale. |
static DateFormat getDateTimeInstance() |
Gets the date/time formatter with the default formatting style for the default locale. |
static DateFormat getTimeInstance() |
Gets the time formatter with the default formatting style for the default locale. |
其實上面三個方法還可以指定日期/時間的樣式, 如FULL/LONG/MEDIUM/SHOT, 通過這四個樣式參數可以控制產生的格式化字串. 但由於在我們的實際開發中很少直接用DateFormat類,因此就不對其做過多的介紹.而我們比較常用的是其子類SimpleDateFormat(其實上面幾個getXxxInstance方法返回的也是SimpleDateFormat執行個體)
DateFormat dateFormat = DateFormat.getTimeInstance();System.out.println(dateFormat.getClass().getName());
SimpleDateFormat
java.text.SimpleDateFormat可以非常靈活的格式化Date, 也可以用於解析各種格式的日期文字.建立SimpleDateFormat對象時需要傳入一個pattern字串,這個pattern不是Regex,而是一個日期模板字串.
/** * Created by jifang on 15/12/30. */public class FormatTest { @Test public void client() throws ParseException { DateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); // Date -> String Date date = new Date(System.currentTimeMillis()); System.out.println(format.format(date)); // String -> Date String timeString = "2015-12-30 08:53:21"; Date newDate = format.parse(timeString); System.out.println(newDate); }}
在時間日期格式化時, 有下面幾個方法是最常用的:
| 方法 |
描述 |
小結 |
String format(Date date) |
Formats a Date into a date/time string. |
Date -> String |
Date parse(String source) |
Parses text from the beginning of the given string to produce a date. |
String -> Date |
當然, pattern我們還可以根據我們的需求有其他的定製形式:
@Test public void client() throws ParseException { DateFormat format = new SimpleDateFormat("yy年MM月dd日 hh時mm分ss秒"); // Date -> String Date date = new Date(System.currentTimeMillis()); System.out.println(format.format(date)); // String -> Date String timeString = "15年12月30日 09時00分29秒"; Date newDate = format.parse(timeString); System.out.println(newDate); }
可以看出SimpleDateFormat把日期格式化成怎樣的字串以及能把怎樣的字串解析成Date, 完全取決於建立對象時指定的pattern參數,其他的pattern參數以及SimpleDateFormat的方法可以參考JDK文檔.
資料庫儲存時間實戰
由於時間儲存會涉及到跨時區的問題(同一個UTC時間在各個時區顯示的是不同的數值).因此,在我們向資料庫中插入時間是需要小心謹慎,不能簡單單單的使用資料庫提供的TIMESTAMP或是DATETIME 類型,比較推薦的是選用一個整數類型(如BIGINT64位與Java的Long類型相同),來儲存從`1970-01-01 00:00:00到時間點所經過的毫秒數(具體原因詳見:如何正確地處理時間).
這樣做的優點是:讀取時間時(一個Long類型整數),只需要按照使用者的時區格式化為字串就能正確地顯示出來. 當然這樣做也存在缺陷,那就是當我們開發人員/DB直接查看資料庫時,看到的只是一串數字,並不能清楚的知曉其對應的時間日期.
上面講完了資料庫該如何儲存時間值,下面我們再聊一聊時間[存入/讀出]資料庫的轉化問題:
從Date轉換成Long 很簡單:
Date date = new Date();long time = date.getTime();
從Long轉換成一個時間的String我們需要
SimpleDateFormat的一個方法:
// Formats an object to produce a string.| Object -> StringString format(Object obj);
// 當然pattern字串可以指定為其他值String time = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(System.currentTimeMillis());
配置單例Formatter
由於在一個項目中時間格式化和解析的格式一般只有一種(我們應該不希望格式化之後的time到最後反而解析不出來),因此我們沒有必要每次使用時都new出一個Formatter來,這樣不光會造成效能下降還有可能造成時間形式不統一而出錯.因此,我們可以在Sring的容器中裝載一個Formatter Bean,使用時@Autowired就可以了:
@Autowiredprivate DateFormat dateFormatter;...String time = dateFormatter.format(System.currentTimeMillis());