在我十年的 IT 生涯中,好像幾乎沒有使用過 MySQL 的 date
、datetime
和 timestamp
幾種數據類型。
究其原因,有兩個:一是傻傻的分不清 DATE
、DATETIME
和 TIMESTAMP
三種數據類型,記不住它們的格式;二是,它們或多或少和時區相關,爲了存儲時區無關數據,我一般都使用 int(11)
存儲時間戳格式
今天就稱這個機會,我們一起來熟悉下這三種數據類型
DATE、DATETIME 和 TIMESTAMP 的格式
首先要說明的是,這三種數據類型是息息相關的,它們有很多相似之處,也有各自的特徵,因爲這些特徵,才導致了彼此的不同。
其次,MySQL 可以識別多種格式的 date
、datetime
和 timestamp
,具體支持的格式有
- 對於
date
類型,支持YYYY-MM-DD
或YY-MM-DD
以及它們各自的變形
說到變形,就是指中劃線 ( -
) 可以替換爲任意字符,除了數字 0-9
,比如
格式 | 範例 |
---|---|
YYYY-MM-DD | 2018-09-13 |
YY-MM-DD | 18-09-13 |
YYYY/MM/DD | 2018/09/13 |
YY/MM/DD | 18/09/13 |
YYYY^MM^DD | 2018^09^13 |
YY^MM^DD | 18^09^13 |
YYYY@MM@DD | 2018@09@13 |
YY@MM@DD | 18@09@13 |
同時還支持 YYYYMMDD
和 YYMMDD
的字符串形式,例如 '20180913'
和 '180913'
。
還支持 YYYYMMMDD
和 YYMMDD
的數字格式,例如 20180913
和 180913
。
- 對於
datetime
和timestamp
類型,支持YYYY-MM-DD HH:MM:SS
或YY-MM-DD HH:MM:SS
,一起它們的變形
這個變形其實和 DATE
一樣,而且就是指中劃線 ( -
) 可以替換爲任意字符,除了數字 0-9
。
更進一步,這兩個類型可以針對日期部分和時間部分使用不同的分隔符
格式 | 範例 |
---|---|
YYYY-MM-DD HH:MM:SS | 2018-09-13 21:15:10 |
YY-MM-DD HH:MM:SS | 18-09-13 21:15:10 |
YYYYY^MM^DD HH+MM+SS | 2018^09^13 21+15+10 |
YY^MM^DD HH+MM+SS | 18^09^13 21+15+10 |
YYYY^MM^DD | 2018^09^13 |
YY^MM^DD | 18^09^13 |
YYYY@MM@DD HH^MM^SS | 2018@09@13 21^15^10 |
YY@MM@DD HH^MM^SS | 18@09@13 21^15^10 |
這種格式下也有幾個點要注意
- 日期和時間部分與小數秒部分之間的唯一可用的分隔符號是小數點 (
.
) 例如2018-09-13 21:15:10.11
- 日期與時間部分的分隔符不一定就要使用空格 (
' '
),還可以使用字符T
,例如2018-09-13 21:15:10
和2018-09-13T21:15:10
是等價的
同時還可以使用沒有任何分隔符的形式,比如 YYYYMMDDHHMMSS
或 YYMMDDHHMMSS
,例如 '20180913211510'
與 2018-09-13 21:15:10
是等價的
但 071122129015
則是非法的,並不是因爲年份缺少了,而是因爲分鐘太大 ( 90
),然後會被視爲 0000-00-00 00:00:00
因爲 MySQL 同時支持超大整數類型,所以,數字形式的 YYYYMMDDHHMMSS
或 YYMMDDHHMMSS
也是被支持的,例如 20180913211510
和 180913211510
被認爲與 2018-09-13 21:15:10
是等價的
DATE、DATETIME 和 TIMESTAMP 的特徵
從上面的格式中可以看出,date
類型就是隻有 年月日 沒有 時分秒,MySQL 以 YYYY-MM-DD
格式檢索並顯示 date 類型的值。支持的範圍是 1000-01-01
到 9999-12-31
datetime
類型通常包含完整的日期時間,也就是能夠完整的表達某一個時刻。MySQL 以 YYYY-MM-DD HH:MM:SS
格式檢索並顯示 datetime 類型的值,支持的範圍是 1000-01-01 00:00:00
到 9999-12-31 23:59:59
和 datetime
類型一樣,timestamp
類型通常也包含完整的日期時間,也能夠完整的表達某一個時刻。MySQL 也以 YYYY-MM-DD HH:MM:SS
格式檢索並顯示 timestamp 類型的值,但是,timestamp
支持的範圍是不同的,只能是1970-01-01 00:00:00 UTC
到 2038-12-31 23:59:59 UTC
也就是說,支持的格式和顯示上,datetime
和 timestamp
並沒有什麼不同,但在支持的範圍上是有不同的
DATETIME 和 TIMESTAMP 的小數部分
datetime 和 timestamp 兩種時間類型還可以精確到 微秒 ,具體的形式就是支持在 秒 後面使用 6 位精度的小數來支持,例如 2018-09-13 21:15:10.111111
。
而且,插入 datetime 或 timestamp 列的值中的任何小數部分都將被存儲而不是被丟棄。
當包含小數部分時,datetime 和 timstamp 兩個日期時間類型的格式爲 YYYY-MM-DD HH:MM:SS [.fraction]
,datetime 值的範圍爲 1000-01-01 00:00:00.000000
至 9999 -12-31 23:59:59.999999
而 timestamp 類型的範圍則爲 1970-01-01 00:00:01.000000
到 2038-01-19 03:14:07.999999
注意: 小數部分與其它部分的唯一的分隔符只能是小數點 (
.
),且沒有任何其它可替代的字符。
DATETIME 和 TIMESTAMP 支持自動賦值
MySQL 還爲 datetime 和 timestamp 兩種日期時間類型提供了自動賦值功能。也就是在 MySQL INSERT 時可以自動初始化爲當前日期時間,在 MySQL Update 時自動更新爲當前時間。
而具體的做法,就是創建表結構時爲這兩種日期時間類型添加以下約束:
CREATE TABLE t1 (
ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
dt DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
指示在 insert 操作時自動插入當前日期時間DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
指示在 update 操作時自動更新爲當前日期時間
DATETIME 和 TIMESTAMP 的時區問題
datetime 和 timestamp 兩種類型的另一個區別,就是它們對待 時區 有所不同
- timestamp 會將值的時區從當前時區轉換爲 UTC 然後存儲,並在需要顯示和檢索時從 UTC 轉換回當前時區
- 但對於 datetime 類型,什麼都不會發生,值是什麼時區,存儲的就是什麼時區
默認情況下,timestamp 和 datetime 都會將當前連接的 MySQL 服務器的時區當作當前時區,當然了,這個時區是可配置的,而且可以針對每個連接單獨配置。
從某些方面說,在數據轉移或者在不同地方顯示時,只要設置了一樣的時區,那麼數據就是一致的,否額
- datetime 的值雖然存儲和顯示的時候都是同一個值,但可能不是我們想要的,因爲時區錯了
- timestamp 雖然可以保證時間是正常的,但存儲的值和顯示的值可能會不一樣,可能會導致我們錯覺發生
PS 就是因爲這個原因,導致了我不敢輕易使用 DATETIME 數據類型,連帶 timestamp 也不敢使用
DATE、DATETIME 和 TIMESTAMP 的注意事項
-
對於這三種日期時間類型,無效的值將會轉換爲相應的 零 值,也就是,date 類型會轉換爲
0000-00-00
,datetime
類型會轉換爲0000-00-00 00:00:00
,而timestamp
則會轉換爲1970-01-01 00:00:00
。 -
雖然 MySQL 支持爲指定爲字符串的值使用 寬鬆 格式,其中任何標點字符都可以用作日期部分或時間部分之間的分隔符,但是,在某些情況下,這種語法可能是欺騙性的。
例如,10:11:12
之類的值可能看起來像時間值,因爲它們的分隔符是 :
,但它也可以表示爲一個日期 2010-11-12
。
還有,年月日時分秒中的任何一部分只要非法,那麼整個值就會 歸零,例如 date 類型的值如果爲 10:45:15
那麼就會被轉換爲 0000-00-00
,因爲 45
不是有效月份值
-
上面也提到了,就是日期和時間部分與小數秒部分之間識別的唯一分隔符是小數點
-
MySQL 服務器會真實的判斷一個日期是否有效,而不僅僅是判斷月份和日期是否分別在 1 到 12 和 1 到 31 的範圍內。
在嚴格模式下 ( sql_mode=TRADITIONAL
) 無效的月份和日期 ( 例如 '2004-04-31' )直接會導致一個錯誤發生,
即使在禁用嚴格模式後,'2004-04-31' 等無效日期也會被轉換爲 '0000-00-00' 且生成一個警告
-
對於 timestamp 類型,MySQL 並不支持月份和日期中的
0
值,也就是沒有什麼00
月和00
日,這些都不是有效的值,會直接導致整體的值被轉換爲1970-01-01 00:00:00
-
對於 datetime 和 timestamp 類型,兩位數的年是不確定的日期,因爲不知道是屬於哪個世紀。
對於兩位數的年份,MySQL 使用下面這些規則解釋
- 00-69 範圍內的年份值將轉換爲 2000-2069
- 70-99 範圍內的年份值轉換爲 1970-1999