由于種種原因,簡(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.Date 與 java.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.Date 與 java.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]