淺談 Java 與 Oracle 中的日期與時(shí)間類型

由于種種原因,簡(jiǎn)書(shū)等第三方平臺(tái)博客不再保證能夠同步更新,歡迎移步 GitHub:https://github.com/kingcos/Perspective/。謝謝!

注:
由于目前個(gè)人對(duì) Java 的理解應(yīng)用僅限于皮毛,故若有不妥,望及時(shí)告知,會(huì)及時(shí)修正。

  • Info:
  • JDK 1.8
  • Eclipse EE neon
  • Oracle 10g XE

前言

暑假學(xué)校培訓(xùn),因此整理一下之前在學(xué)習(xí)過(guò)程中比較困惑的地方。方便未來(lái)查閱,也使自己能夠更深入了解 Java。這次來(lái)說(shuō)一說(shuō)日期與時(shí)間,因?yàn)閿?shù)據(jù)庫(kù)和 Java 本身都有許多存儲(chǔ)日期或時(shí)間的類型,那么如何選擇合適的類型,并正確的存入以及讀取便很重要。網(wǎng)上的資料也有些參差不齊,因此我個(gè)人整理于此,并附上可以實(shí)際運(yùn)行的代碼。

Java 中的日期與時(shí)間類型簡(jiǎn)介

子父類關(guān)系

java.lang.Object java.lang.Object java.lang.Object
java.util.Date java.util.Date java.util.Date
- java.sql.Timestamp java.sql.Date

精度

類型 java.util.Date java.sql.Timestamp java.sql.Date
精度 年 月 日 時(shí) 分 秒 年 月 日 時(shí) 分 秒 毫微秒 年 月 日

初始化

public class TestInitTime {
    public static void main(String[] args) {
        java.util.Date utilDate_1 = new java.util.Date();
        java.util.Date utilDate_2 = new java.util.Date(System.currentTimeMillis());
        java.sql.Timestamp sqlTimestamp = new java.sql.Timestamp(System.currentTimeMillis());
        java.sql.Date sqlDate = new java.sql.Date(System.currentTimeMillis());
        
        System.out.println("utilDate_1 =\t" + utilDate_1);
        System.out.println("utilDate_2 =\t" + utilDate_2);
        System.out.println("sqlTimestamp =\t" + sqlTimestamp);
        System.out.println("sqlDate =\t" + sqlDate);
    }
}

// Console:
// utilDate_1 = Sun Jul 17 09:26:07 CST 2016
// utilDate_2 = Sun Jul 17 09:26:07 CST 2016
// sqlTimestamp = 2016-07-17 09:26:07.342
// sqlDate = 2016-07-17

上述的初始化均使用了各自未過(guò)時(shí)的構(gòu)造函數(shù),輸出打印后,可以看到明顯的精度區(qū)別。

PS
System.currentTimeMillis(): 返回以毫秒為單位的當(dāng)前時(shí)間。
CST 代表 China Standard Time(中國(guó)標(biāo)準(zhǔn)時(shí)間,即東八區(qū),北京時(shí)間)

多種日期類型轉(zhuǎn)換

String -> 時(shí)間

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;

public class TransformDateOrTime {

    public static void main(String[] args) {
        String date = "2016-7-17 14:30:05";
        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        java.util.Date utilDate = null;
        try {
            utilDate = dateFormat.parse(date);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        System.out.println("utilDate: " + utilDate);

        java.sql.Timestamp sqlTimestamp = java.sql.Timestamp.valueOf("2016-7-17 14:30:05");
        java.sql.Date sqlDate = java.sql.Date.valueOf("2016-7-17");

        System.out.println("sqlTimestamp: " + sqlTimestamp);
        System.out.println("sqlDate: " + sqlDate);  
    }
}

// Console:
// utilDate: Sun Jul 17 14:30:05 CST 2016
// sqlTimestamp: 2016-07-17 14:30:05.0
// sqlDate: 2016-07-17

java.util.Datejava.sql.Timestamp

getTime(): 返回調(diào)用對(duì)象表示的自 1970 年 1 月 1 日 00:00:00 GMT 以來(lái)的毫秒數(shù)。

// java.util.Date -> java.sql.Timestamp
java.util.Date utilDate_1 = null;
try {
    utilDate_1 = dateFormat.parse(date);
} catch (ParseException e) {
    e.printStackTrace();
}
java.sql.Timestamp sqlTimestamp_1 = new java.sql.Timestamp(utilDate_1.getTime());
System.out.println(utilDate_1 + " 轉(zhuǎn)換為 " + sqlTimestamp_1);
                
// java.util.Date <- java.sql.Timestamp (精度丟失)
java.sql.Timestamp sqlTimestamp_2 = new java.sql.Timestamp(System.currentTimeMillis());
java.util.Date utilDate_2 = new java.util.Date(sqlTimestamp_2.getTime());
System.out.println(sqlTimestamp_2 + " 轉(zhuǎn)換為 " + utilDate_2);

// Console:
// Sun Jul 17 14:30:05 CST 2016 轉(zhuǎn)換為 2016-07-17 14:30:05.0
// 2016-07-17 10:09:38.736 轉(zhuǎn)換為 Sun Jul 17 10:09:38 CST 2016

java.util.Datejava.sql.Date

// java.util.Date -> java.sql.Date
java.sql.Date sqlDate_1 = new java.sql.Date(utilDate_1.getTime());
System.out.println(utilDate_1 + " 轉(zhuǎn)換為 " + sqlDate_1);
        
// java.util.Date <- java.sql.Date
java.sql.Date sqlDate_2 = new java.sql.Date(System.currentTimeMillis());
java.util.Date utilDate_3 = new java.util.Date(sqlDate_2.getTime());
System.out.println(sqlDate_2 + " 轉(zhuǎn)換為 " + utilDate_3);

// Console:
// Sun Jul 17 14:30:05 CST 2016 轉(zhuǎn)換為 2016-07-17
// 2016-07-17 轉(zhuǎn)換為 Sun Jul 17 11:14:15 CST 2016

如何選擇?

在上面最后由 java.sql.Date 轉(zhuǎn)換為 java.util.Date 中,雖然我們之前查到 java.sql.Date 只能保存年月日,但是這里卻可以轉(zhuǎn)換為帶有時(shí)分秒java.util.Date。而 java.sql.Date 中的 getHours(),getMinutes()getSeconds()(也包括對(duì)應(yīng)的 setter)方法均已過(guò)時(shí),如果調(diào)用會(huì)有 java.lang.IllegalArgumentException 異常。所以 java.sql.Date 只是屏蔽了時(shí)間中的時(shí)分秒,為了和數(shù)據(jù)庫(kù)中的 DATE 類型匹配,查看其源代碼就可以得知,java.sql.Date 繼承但沒(méi)有重寫(xiě) getTime() 方法,而本身的 public Date(long date) 構(gòu)造方法也是調(diào)用了父類的構(gòu)造方法。

而與此不同的是 java.sql.Timestamp,其對(duì)父類做了擴(kuò)充,通過(guò)查看其源代碼,我們可以發(fā)現(xiàn),其 getTime() 中增加了納秒(1s = 1E9nanos),而且單獨(dú)增加了 getNanos() 方法。

因此 java.sql.Date 只是屏蔽年月日,而不是移除,而 java.sql.Timestamp 對(duì)父類進(jìn)行了擴(kuò)充。在下面的 Demo 中,會(huì)實(shí)際操作數(shù)據(jù)庫(kù),這樣一存一取就可以將其特點(diǎn)展現(xiàn)。

在這里以 Oracle 數(shù)據(jù)庫(kù)為例,Oracle 中有兩種主要日期與時(shí)間類型,DATE 以及 TIMESTAMP。

DATE: 僅存 年 月 日
TIMESTAMP: 保存 年 月 日 時(shí) 分 秒 納秒

所以對(duì)應(yīng) Java 中,我們就應(yīng)該在保存合適精度的時(shí)間下,選擇合適的類型。Java 中的 java.util.Date 更為靈活,我們可以在恰當(dāng)?shù)臅r(shí)候?qū)⑵滢D(zhuǎn)為合適的類型存入數(shù)據(jù)庫(kù),或者在取出時(shí)轉(zhuǎn)為該類型。

Demo

SQL

-- 建表
DROP TABLE T_TIME;
CREATE TABLE T_TIME (
    ID NUMBER(10,0) PRIMARY KEY,
    date_1 DATE, 
    timestamp_1 TIMESTAMP, 
    date_2 DATE, 
    timestamp_2 TIMESTAMP
);

-- 創(chuàng)建自增序列
drop sequence time_id;
create sequence time_id
increment by 1  
start with 1 
nomaxvalue 
nominvalue 
nocache

實(shí)體類:TimeEntity.java

public class TimeEntity {
    private int id;
    
    private java.util.Date date_1;
    private java.sql.Date date_2;
    
    private java.util.Date timestamp_1;
    private java.sql.Timestamp timestamp_2;
    
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public java.util.Date getDate_1() {
        return date_1;
    }
    public void setDate_1(java.util.Date date_1) {
        this.date_1 = date_1;
    }
    public java.sql.Date getDate_2() {
        return date_2;
    }
    public void setDate_2(java.sql.Date date_2) {
        this.date_2 = date_2;
    }
    public java.util.Date getTimestamp_1() {
        return timestamp_1;
    }
    public void setTimestamp_1(java.util.Date timestamp_1) {
        this.timestamp_1 = timestamp_1;
    }
    public java.sql.Timestamp getTimestamp_2() {
        return timestamp_2;
    }
    public void setTimestamp_2(java.sql.Timestamp timestamp_2) {
        this.timestamp_2 = timestamp_2;
    }
    
    public String toString() {
        return "TimeEntity [id=" + id + ", date_1=" + date_1 + ", date_2=" + date_2 + ", timestamp_1=" + timestamp_1
                + ", timestamp_2=" + timestamp_2 + "]";
    }
    
}

測(cè)試類:TestTimeDateType.java

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;

public class TestTimeDateType {
    public static void main(String[] args) {
        try {
            Class.forName("oracle.jdbc.OracleDriver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        // url 中的地址要替換為自己數(shù)據(jù)的地址
        String url = "jdbc:oracle:thin:@localhost:1521:XE";
        // 數(shù)據(jù)庫(kù)用戶名及密碼需要設(shè)置為自己的
        String user = "demo";
        String password = "123456";
        Connection connection = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            connection = DriverManager.getConnection(url, user, password);
            String sql = "insert into t_time values (time_id.nextVal, ?, ?, ?, ?)";
            ps = connection.prepareStatement(sql);
            
            ps = setAll(ps);
            
            ps.executeUpdate();
            
            // 這里我們只運(yùn)行一次,為方便起見(jiàn),因此僅查詢 id 為 1 的記錄
            sql = "select * from t_time where id = 1";
            ps = connection.prepareStatement(sql);
            TimeEntity te = new TimeEntity();
            rs = ps.executeQuery();
            while (rs.next()) {
                te.setId(rs.getInt(1));
                te.setDate_1(rs.getDate(2));
                te.setTimestamp_1(rs.getTimestamp(3));
                
                te.setDate_2(rs.getDate(4));
                te.setTimestamp_2(rs.getTimestamp(5));
            }
            System.out.println(te);
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                rs.close();
                ps.close();
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    
    static PreparedStatement setAll(PreparedStatement ps) throws Exception {
        ps.setDate(1, returnSqlDateWithSqlDate());
        ps.setTimestamp(2, returnTimestampWithTimestamp());
        ps.setDate(3, new java.sql.Date(returnSqlDateWithUtilDate().getTime()));
        ps.setTimestamp(4,new java.sql.Timestamp(returnTimestampWithUtilDate().getTime()));
        
        return ps;
    }
    
    static java.sql.Date returnSqlDateWithSqlDate() {
        java.sql.Date sqlDate = java.sql.Date.valueOf("2012-2-2");
        return sqlDate;
    }
    
    static java.sql.Timestamp returnTimestampWithTimestamp() {
        java.sql.Timestamp timestamp = java.sql.Timestamp.valueOf("2015-5-5 5:55:55.555");
        return timestamp;
    }
    
    static java.util.Date returnSqlDateWithUtilDate() throws Exception {
        String date = "2013-3-3 3:33:33";
        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        java.util.Date utilDate = dateFormat.parse(date);
        return utilDate;
    }
    
    static java.util.Date returnTimestampWithUtilDate() throws Exception {
        String date = "2016-6-6 6:6:6";
        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        java.util.Date utilDate = dateFormat.parse(date);
        return utilDate;
    }
}

// Console:
// TimeEntity [id=1, date_1=2012-02-02, date_2=2013-03-03, timestamp_1=2015-05-05 05:55:55.555, timestamp_2=2016-06-06 06:06:06.0]

小結(jié)

從上面實(shí)例中,就可以基本清楚這幾個(gè)類型的差別,以及其中的轉(zhuǎn)換,因此在實(shí)際使用中便可以通過(guò)需要的不同精度,來(lái)確定所選的類型即可。

java.util.Date 與 Calendar

java.util.Date 中有許多過(guò)時(shí)方法,查看其注釋,有許多都被 Calendar 所代替。由于在現(xiàn)實(shí)中,java.util.Date 不再能勝任國(guó)際化的操作,因此建議使用 Calendar 進(jìn)行日期與時(shí)間處理。由于 Calendar 類是抽象類,且 Calendar 類的構(gòu)造方法是 protected 的,所以無(wú)法使用Calendar類的構(gòu)造方法來(lái)創(chuàng)建對(duì)象,但提供了 getInstance() 靜態(tài)方法來(lái)創(chuàng)建對(duì)象。

轉(zhuǎn)化

測(cè)試類:TestCalendar.java

import java.util.Calendar;
import java.util.Date;

public class TestCalendar {
    public static void main(String[] args) {
        // Calendar 轉(zhuǎn)化為 Date
        Calendar calendar_1 = Calendar.getInstance();
        System.out.println(calendar_1.getTimeInMillis());
        Date date_1 = calendar_1.getTime();
        System.out.println("Calendar -> Date" + date_1);
        
        // Date 轉(zhuǎn)化為 Calendar
        Date date_2 = new Date();
        Calendar calendar_2 = Calendar.getInstance();
        calendar_2.setTime(date_2);
        System.out.println("Date -> Calendar " + calendar_2);
    }
}

// Console:
1468741779943
Calendar -> DateSun Jul 17 15:49:39 CST 2016
Date -> Calendar java.util.GregorianCalendar[time=1468741779978,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=19,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2016,MONTH=6,WEEK_OF_YEAR=30,WEEK_OF_MONTH=4,DAY_OF_MONTH=17,DAY_OF_YEAR=199,DAY_OF_WEEK=1,DAY_OF_WEEK_IN_MONTH=3,AM_PM=1,HOUR=3,HOUR_OF_DAY=15,MINUTE=49,SECOND=39,MILLISECOND=978,ZONE_OFFSET=28800000,DST_OFFSET=0]

參考資料

本人博客:https://maimieng.com

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • /Library/Java/JavaVirtualMachines/jdk-9.jdk/Contents/Home...
    光劍書(shū)架上的書(shū)閱讀 4,194評(píng)論 2 8
  • 一、Java 簡(jiǎn)介 Java是由Sun Microsystems公司于1995年5月推出的Java面向?qū)ο蟪绦蛟O(shè)計(jì)...
    子非魚(yú)_t_閱讀 4,564評(píng)論 1 44
  • Episode 11 運(yùn)營(yíng)數(shù)據(jù)的一二三 談完了內(nèi)容運(yùn)營(yíng)、活動(dòng)運(yùn)營(yíng)、用戶運(yùn)營(yíng)的基礎(chǔ)知識(shí),我們來(lái)聊聊運(yùn)營(yíng)數(shù)據(jù)的一些事兒...
    songshu閱讀 358評(píng)論 0 2
  • 畫(huà)魂(一) 秀芬嬸死的這一年,院子里頗不寧?kù)o。 院子里有棵碩大的梧桐樹(shù),不知道怎么回事,樹(shù)干上的樹(shù)皮一夜時(shí)間脫落了...
    三千晚風(fēng)閱讀 1,462評(píng)論 0 5
  • M號(hào)小姐生活在一個(gè)普通的家庭里,她的“母后”一個(gè)極具有控制欲的女王;萬(wàn)事都要打著“為你好”的名義干涉著你的事;她想...
    m號(hào)小姐閱讀 235評(píng)論 0 0

友情鏈接更多精彩內(nèi)容