Java中日期時間API小結


Java中為處理日期和時間提供了大量的API,確實有把一件簡單的事情搞復雜的嫌疑,各種類:Date Time Timestamp Calendar...,但是如果能夠看到時間處理的本質就可以輕松hold住這堆東西了。

常用的類

表示類

  • java.util.Date :能夠准確記錄到毫秒級別的時間表示類,但是其中的各種get set(修改時間或者獲取時間中某一個特殊參數)都已經被廢棄。
  • java.sql.Date :為數據庫提供的日期類,繼承自util包中的Date,但是這個類只能夠操作日期,不能讀取或者修改時間。sql和util中Date內部進行存儲的long,都可以保存到毫秒級別
  • java.sql.Time :為數據庫提供的時間類,和Date相反,它只能獲取和操作時間相關的信息。
  • java.sql.Timestamp:時間戳,繼承util.Date,它不僅能夠完美支持util.Date的功能,而且可以支持到納秒級別(10^-9 s)。

工具類

  • Calendar:主要用來操作一個Date類型,提供了一系列接口來獲取或修改其中的信息。
  • TimeZone:用來配合Calendar 操作Date,主要是考慮時區的問題,值得注意的是,在Date中存儲的信息是一個絕對標准時間(稍后說明),而如果需要進行時區的轉化,那么只需要配合此類即可。
  • SimpleDateFormat:常用的格式化Date的工具,主要是進行String和日期之間的互換。

基本概念

時間的來源

注意,這里並不是討論一個哲學的問題,在大部分的編程語言中,我們都是采用從1970-01-01 00:00:00.000 開始至今的毫秒差作為表示時間的數值,這個時間是絕對公立的,它和時區沒有任何關系。在Java中任何時間的表示類的底層存儲的毫秒數都是一個這樣的標准時間。

在java中獲取當前時間接口是System.currentTimeMillis()

值得一提的是Java還提供了一個更加精確的時間:System.nanoTime(),獲取一個時間精確到納秒,但是它並不是一個當前的精確時間,而是JVM提供的一個時間,它的主要用途是來精確衡量兩個時間段之間的時間,如計算一段代碼的執行時間:

	long startTime = System.nanoTime();
   // ... the code being measured ...
   long estimatedTime = System.nanoTime() - startTime;

可以比較兩個接口返回的內容:
System.currentTimeMillis():1429108246639
System.nanoTime():1429108246640(ms)089000------->多了6位

UTC和GMT

這兩個標准唯一不同之處在於UTC是基於GMT進行微調之后的一個時間,本文不去深究這兩者的差別,在此認為這兩者是一個東西。

初中地理教過我們,地球是24個時區,東部和西部各12個,時區的基准點是倫敦(基准UTC),往東,會領先UTC,往西,會落后UTC。

如北京屬於東八區,那么我們的時間會領先基准,也就是我們在早上9點時,倫敦是早上1點。如果我們在不同時區接發郵件的時候,可以發現這個問題。

這個時間是我收到一份來自華盛頓的郵件的時間:

2014年1月23日(星期四) 晚上7:29 (UTC-05:00 華盛頓、多倫多、古巴、智利時間)

這里我們可以在郵件時間后面發現UTC-05:00,說明這里是落后UTC基准5個小時。注意,前面的時間是發件人的本地時間,如果轉化成北京所在時區的時間應該是加上13h,那我收到這封郵件的本地時間是:2014-01-24 星期五早上8:29。

再談TimeStamp

前面說了,TimeStamp能夠精確到納秒,那它是怎么做到的呢?由於TimeStamp繼承自Date,它把整數秒存儲在超類中,而在子類中專門用一個long類型存儲零的秒數:nanos

需要注意,除非你顯示去調用TimeStamp的這個構造器:
public Timestamp(int year, int month, int date, int hour, int minute, int second, int nano)

顯示去指定nano的值,否則這個構造器的參數
public Timestamp(long time)

的單位實際上是毫秒。

API的使用

最后再來說說日期時間的操作接口,過程基本如下圖:

           SimpleDateFormat <------>  Date   <---------> Calendar

Date負責存儲一個絕對時間,並對兩邊提供操作接口。Calendar負責對Date中特定信息,比如這個時間是改年的第幾個星期,此外,還可以通過set,add,roll接口來進行日期時間的增減。SimpleDateFormat主要作為一些格式化的輸入輸出。

SimpleDateFormat

SimpleDateFormat的構造器接受一個String pattern,其中的pattern是預定義的:

G 年代標志符
y 年
M 月
d 日
h 時 在上午或下午 (1~12)
H 時 在一天中 (0~23)
m 分
s 秒
S 毫秒
E 星期
D 一年中的第幾天
F 一月中第幾個星期幾
w 一年中第幾個星期
W 一月中第幾個星期
a 上午 / 下午 標記符 
k 時 在一天中 (1~24)
K 時 在上午或下午 (0~11)
z 時區

例子1:

 SimpleDateFormat DATETIME_FORMATER_WITHWEEK = new SimpleDateFormat(
            "yyyy-MM-dd E HH:mm");

    java.util.Date date = new java.util.Date();
    System.out.println(DATETIME_FORMATER_WITHWEEK.format(date));
    
    //output: 2015-04-15 星期三 23:59 
    //當然,反過來,我也可以使用這個format將output的字符串轉化成Date

Calendar

Calendar中主要需要了解的各種操作域,感覺這也是Java在做這個API時的一個敗筆,靈活有余,可控性不足,初學者如果亂用域,將會產生各種bug。至於每一個域對應的時間分量,請自行google。

一些常用的filed:

  • YEAR:年
  • MONTH:月(從0 開始,0 表示1月....11表示12月)
  • DAY_OF_MONTH :幾號(等同DATE)
  • DAY_OF_WEEK:星期幾
  • DAY_OF_YEAR:年里面的天
  • DATE:幾號(等同DAY_OF_MONTH)

一個filed通常來說對應了日期時間中的某一個分量,在操作這個分類有些操作會向高位進位,而有的操作則不會【bug高發區域】。

例子2:

SimpleDateFormat DATETIME_FORMATER_WITHWEEK = new SimpleDateFormat(
     "yyyy-MM-dd E");
    Calendar calendarT = Calendar.getInstance(Locale.CHINA);
    System.out.println(DATETIME_FORMATER_WITHWEEK.format(calendarT.getTime()));
    calendarT.set(Calendar.MONTH,12);// 月份進位
    System.out.println(DATETIME_FORMATER_WITHWEEK.format(calendarT.getTime()));
    //output:	2015-04-16 星期四
					2016-01-16 星期六

例子3:

        SimpleDateFormat DATETIME_FORMATER_WITHWEEK = new SimpleDateFormat(
     "yyyy-MM-dd E");
    Calendar calendarT = Calendar.getInstance(Locale.CHINA);
    System.out.println("原始    :"+DATETIME_FORMATER_WITHWEEK.format(calendarT.getTime()));
    calendarT.set(Calendar.DAY_OF_MONTH,1);// 當月第一天
    System.out.println("當月第一天:"+DATETIME_FORMATER_WITHWEEK.format(calendarT.getTime()));
    //roll不會進位
    calendarT.roll(Calendar.DATE,-1);
    System.out.println("roll -1:"+DATETIME_FORMATER_WITHWEEK.format(calendarT.getTime()));
    calendarT.set(Calendar.DAY_OF_MONTH,1);// 當月第一天
    calendarT.add(Calendar.DATE,-1);
    //add產生進位
    System.out.println("add -1:"+DATETIME_FORMATER_WITHWEEK.format(calendarT.getTime()));
    
    //output:原始    :2015-04-16 星期四
				當月第一天:2015-04-01 星期三
				roll -1:2015-04-30 星期四
				add -1:2015-03-31 星期二

最后還是需要說明一點,獲取當前時間指的是當前本地時間對應的UTC時間,和時區沒有關系!有點繞,沒關系看代碼:

例子4:

Calendar calendar1 = Calendar.getInstance(Locale.CHINA);
    Calendar calendar2 = Calendar.getInstance(Locale.GERMAN);
    System.out.println(calendar1.getTimeInMillis());
    System.out.println(calendar2.getTimeInMillis());
    
    //output:
    	1429115150117
		1429115150117

最后貼一個日期處理開源庫:joda-time


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM