MySQL 中的日期時間類型


日期時間類型中包含以下幾種數據類型:

各類型都有具體的取值范圍,超出或非法的其他值時,MySQL 會回退到 0。TIMESTAMP 類型是個例外,給它設置一個超出范圍的值時,將保存上該類型允許的最大值。

MySQL 按標准格式 YYYY-MM-DD hh:mm:ss[.fraction] 輸出日期時間,但設置或進行日期時間相關的比較時卻支持靈活的多種格式,會自動解析。具體支持的輸入格式可參見 Section 9.1.3, “Date and Time Literals”。其中 fraction 部分為秒后面的小數部分,取值范圍為 0~6 位。

雖然 MySQL 支持多種格式進行日期時間的設置,但日期部分要求必須是 年-月-日 的形式才能正確解析。比如 98-09-04 是按年月日順序解析的,而不是英文里常用的月日年,或者日月年。

年在只給了兩位數的情況下,MySQL 嘗試使用以下規則來補全:

  • 給定的兩位數為 70~99 時解析成 1970 ~ 1999。
  • 給定為 00 ~ 69 時解析成 2000 ~ 2069。

所以,為了避免不可預測的結果,使用時還是指定全一些。

  • 在需要使用數字的語境下,MySQL 會將日期時間自動轉成數字。同理,在需要日期時間的相關操作語境下,會嘗試將數字解析成日期時間。
  • 通過設置 MySQL 相關參數,日期類型可保存原本非法的值,比如開啟 ALLOW_INVALID_DATES 設置項時,可設置日期類型保存一個 2009-11-31 值,但正常情況下我們知道 11 月哪來什么 31 號。此時 MySQL 僅僅只是不檢查月分與日期的關聯性,但月分的取值范圍 112 及日期的取值范圍 131 還是要單獨各自做校驗的。
mysql> INSERT INTO todo (title,created_on) VALUES ('blah','2019-09-31');
ERROR 1292 (22007): Incorrect date value: '2019-09-31' for column 'created_on' at row 1

mysql> SET SESSION sql_mode = 'ALLOW_INVALID_DATES';
Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO todo (title,created_on) VALUES ('blah','2019-09-31');
Query OK, 1 row affected, 1 warning (0.01 sec)
mysql> SELECT * FROM todo;
+----+------+------------+
| id | title | created_on |
+----+------+------------+
| 1 | blah | 2019-09-31 |
+----+------+------------+
1 rows in set (0.00 sec)

某些場景下你可能需要保存部分日期,比如用戶只輸入了年沒輸入月日。所以 MySQL 是支持將月日設置成 0,比如 2019-00-00。但這種情況下就無法從日期相關的操作中獲得到准確的結果,比如使用 DATE_SUB()DATE_ADD() 函數時。禁用月日的零值可通過開啟 MySQL 的 NO_ZERO_IN_DATE 模式。

除了月日可零,MySQL 還支持設置年月日都零的值 0000-00-00,對於日期非必填的情況比較有用,因為此時它比單純的 NULL 更有語義。可通過開啟 MySQL 的 NO_ZERO_DATE 模式來禁用這個全零的值。

各日期時間零值格式如下,但實際時用時,直接簡寫成一個 0 效果是等效的。

Data Type “Zero” Value
DATE '0000-00-00'
TIME '00:00:00'
DATETIME '0000-00-00 00:00:00'
TIMESTAMP '0000-00-00 00:00:00'
YEAR 0000

DATE,DATETIME,及 TIMESTAMP

三者具有相關性,都支持多種格式的自動解析,詳見 Date and Time Literals

DATE 日期格式不帶時間 TIME 部分,查詢時輸出格式為 YYYY-MM-DD,取值范圍為 1000-01-019999-12-31

DATETIME 包含日期及時間,輸出格式為 YYYY-MM-DD hh:mm:ss,取值范圍 1000-01-01 00:00:009999-12-31 23:59:59

TIMESTAMPDATETIME,但取值范圍基於 UTC 時間,較 DATETIME 要小,為 1970-01-01 00:00:01 UTC 到 2038-01-19 03:14:07 UTC。所以使用 TIMESTAMP 格式的時間,到 2038 年會溢出,這就是 Year 2038 problem。關於該問題的討論和解決可參見這個 StackOverflow 的回答

既然如此,為何要使用這個取值范圍更小的呢。TIMESTAMP 存儲的值是帶時區的。在存儲時會根據當前時區轉成 UTC(universal time zone) 存儲,查詢時也會根據時區從 UTC 轉換到具體的時間。對於支持多語及國際化全球部署的應用來說,顯得尤為方便。需要注意的是,這里操作基於的時區默認為服務器的時區,可通過改變 time_zone SET GLOBAL time_zone=time_zone 來修改。時區的設置也可以是以連接為單位,這樣來自不同時區的請求可得到不同的時間。

TIMESTAMPDATETIME 都可包含至多 6 位的小數來表示時間中毫秒(microseconds)的部分。所以,帶上毫秒時完整的格式是 YYYY-MM-DD hh:mm:ss[.fraction]。前者取值范圍為 1970-01-01 00:00:01.0000002038-01-19 03:14:07.999999,后者為 1000-01-01 00:00:00.0000009999-12-31 23:59:59.999999

在寫入時,對於非法的日期時間值,將自動存成零值,即 '0000-00-00' 或 '0000-00-00 00:00:00'。

關於日期時間需要注意的點:

  • 因為 MySQL 支持比較寬松的格式來設置日期時間,所以理論上你可以用你想用的值來做為數字之間的分界符,但使用時需要關注其解析的原理。比如給一個日期格式的列設置 10:11:12,雖然這個值看起來像時間類型,但還是可以正確在被解析成目標列的格式,即日期。如果這這個日期列設置 10:45:15 則會認為是非法值,因為 45 不是一個合法的月份值,所以存儲時變成零值 0000-00-00
  • 日期時間與毫秒的分界符必需是小數點。
  • 默認 MySQL 除了檢查日月值是否有有效范圍 1~ 31,1~12。還會將兩者結合進來檢查,比如 4 月沒有 31。所以對於日期 2004-04-31 算是非法的,會變成零值 0000-00-00。如果不需要這樣的約束檢查,可開啟 MySQL 的 ALLOW_INVALID_DATES 模式。

日期時間的自動初始化及更新

TIMESTAMPDATETIME 還支持自動初始化(auto-initialized)和更新到當前時間(auto-updated)。

  • 創建表定義列時,指定 DEFAULT CURRENT_TIMESTAMP 來使相應的日期時間列自動初始化。
  • 指定 ON UPDATE CURRENT_TIMESTAMP 來使相應的日期時間列自動更新。

兩者可同時作用於一個日期時間列,表示插入記錄時自動初始化成當前時間,后續記錄更新時自動更新到當前時間。

其中 CURRENT_TIMESTAMP 指代當前時間,與其有相同效果的還有 CURRENT_TIMESTAMP(), NOW(), LOCALTIME, LOCALTIME(), LOCALTIMESTAMP 以及 LOCALTIMESTAMP()

DEFAULT 除了可指定成當前時間外,也可指定一個任意的固定值,比如 DEFAULT 0 或 `DEFAULT '2000-01-01 00:00:00'。

對於指定了自動初始化的列,插入時如果沒指定該列的值,則會自動設置為當前的時間。

對於指定為自動更新的列,一旦一條記錄中有字段變更,該日期會自動更新成變更時的時間。如果不想它更新,可在插入其他值時手動設置一下該日期列為原有的值,讓其保持不變。

TIMESTAMPDATETIME 在列的定義時,如果指定了小數部分,那么在配合使用 CURRENT_TIMESTAMP(fsp) 時,這個小數部分的精度需要保持一致。比如:

--
CREATE TABLE t1 (
  ts TIMESTAMP(6) DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6)
);

-- 🚨
CREATE TABLE t1 (
ts TIMESTAMP(6) DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP(3)
);

TIME

時間 TIME 格式即日期時間中時間的部分,輸出格式為 hh:mm:ss 或時間較大時為 hhh:mm:ss,取值范圍 -838:59:59838:59:59。同樣地,也是支持帶至多 6 位小數表示毫秒。

設置時也是支持將多種格式自動解析。對於帶冒號的情況,比如 11:12 解析成 11:12:00 而不是 00:11:12。不帶冒號的情況,將最右邊的兩位數字解析成秒(按逝去的時間來解析),比如 '1112'1112 不是 11:12:00 而會解析成 00:11:12。同理,'12'12 會解析成 00:00:12

YEAR

YEAR 表示日期中年的部分,是一個 1 字節大小的類型,可通過 YEARYEAR(4) 來聲明,其展示寬度(display width)為 4。查詢時輸出格式為 YYYY,取值范圍 1901 到 2155。 0000 也是合法的值。

支持使用以下格式進行設置:

  • 使用 1901 ~ 2155 間的四位數字值。
  • 或將上面的數字以字符串形式給定。
  • 1 ~ 99 之間的數字,此時 1 ~ 69 解析成 2001 ~ 2069,70 ~ 99 解析成 1970 ~ 1999.
  • 其他可返回合法值的方法,比如 NOW()

相關資源


免責聲明!

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



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