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

由于種種原因邑雅,簡(jiǎn)書等第三方平臺(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 繼承但沒有重寫 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)行一次稀并,為方便起見,因此僅查詢 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)系作者
  • 序言:七十年代末氏仗,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子夺鲜,更是在濱河造成了極大的恐慌皆尔,老刑警劉巖呐舔,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異慷蠕,居然都是意外死亡珊拼,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門流炕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)澎现,“玉大人,你說(shuō)我怎么就攤上這事每辟〗1瑁” “怎么了?”我有些...
    開封第一講書人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵影兽,是天一觀的道長(zhǎng)揭斧。 經(jīng)常有香客問(wèn)我,道長(zhǎng)峻堰,這世上最難降的妖魔是什么讹开? 我笑而不...
    開封第一講書人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮捐名,結(jié)果婚禮上旦万,老公的妹妹穿的比我還像新娘。我一直安慰自己镶蹋,他們只是感情好成艘,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著贺归,像睡著了一般淆两。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上拂酣,一...
    開封第一講書人閱讀 51,554評(píng)論 1 305
  • 那天秋冰,我揣著相機(jī)與錄音,去河邊找鬼婶熬。 笑死剑勾,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的赵颅。 我是一名探鬼主播虽另,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼饺谬!你這毒婦竟也來(lái)了捂刺?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎叠萍,沒想到半個(gè)月后芝发,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡苛谷,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年辅鲸,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片腹殿。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡独悴,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出锣尉,到底是詐尸還是另有隱情刻炒,我是刑警寧澤,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布自沧,位于F島的核電站坟奥,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏拇厢。R本人自食惡果不足惜爱谁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望孝偎。 院中可真熱鬧访敌,春花似錦、人聲如沸衣盾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)势决。三九已至阻塑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間果复,已是汗流浹背陈莽。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留据悔,地道東北人传透。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓耘沼,卻偏偏與公主長(zhǎng)得像极颓,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子群嗤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • /Library/Java/JavaVirtualMachines/jdk-9.jdk/Contents/Home...
    光劍書架上的書閱讀 3,880評(píng)論 2 8
  • 一菠隆、Java 簡(jiǎn)介 Java是由Sun Microsystems公司于1995年5月推出的Java面向?qū)ο蟪绦蛟O(shè)計(jì)...
    子非魚_t_閱讀 4,187評(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閱讀 295評(píng)論 0 2
  • 畫魂(一) 秀芬嬸死的這一年躯肌,院子里頗不寧?kù)o。 院子里有棵碩大的梧桐樹破衔,不知道怎么回事清女,樹干上的樹皮一夜時(shí)間脫落了...
    三千晚風(fēng)閱讀 1,262評(píng)論 0 5
  • M號(hào)小姐生活在一個(gè)普通的家庭里,她的“母后”一個(gè)極具有控制欲的女王晰筛;萬(wàn)事都要打著“為你好”的名義干涉著你的事嫡丙;她想...
    m號(hào)小姐閱讀 186評(píng)論 0 0