MySQL日期與時間函數(日期/時間格式化、增減、對比、時區、UTC和UNIX時間)

讀完本文,您將掌握MySQL的各類時間函數。
附錄提供了格式標識、時間間隔兩個速查表,便於日常工作中快速查找。
本文會持續更新、補充,建議您收藏。

更多MySQL函數介紹,可查看《MySQL函數和運算符》

MySQL提供了比較多的、靈活的時間函數,而且基本穩定、已經多個版本不曾修改過了1

獲取當前時間

獲取當前時間很簡單,常用的有CURDATE()/CURTIME()/NOW()三個函數。
下面表格說明了其功能和區別:

功能 推薦函數 同義函數 小數位 效果
獲取當前日期 CURDATE() CURRENT_DATE
CURRENT_DATE()
UTC時間參考時區與UTC時間
不支持 默認YYYY-MM-DD
若上下文有數字則返回YYYYMMDD
獲取當前時間 CURTIME() CURRENT_TIME
CURRENT_TIME()
UTC時間參考時區與UTC時間
默認無
可傳參指定至多6位
CURTIME(3)表示精確到毫秒
默認hh:mm:ss
若上下文有數字則返回hhmmss
獲取當前日期+時間 NOW() CURRENT_TIMESTAMP
CURRENT_TIMESTAMP()
LOCALTIME
LOCALTIME()
LOCALTIMESTAMP
LOCALTIMESTAMP()
UTC時間參考時區與UTC時間
默認無
可傳參指定至多6位
NOW(3)表示精確到毫秒
默認YYYY-MM-DD hh:mm:ss
若上下文有數字則返回YYYYMMDDhhmmss

上下文有數字,指的是函數前後有數字運算,如我現在執行的CURDATE() + 0返回了20191023

如果對“現在”的定義精確一點:上述函數取的都是SQL開始執行的時間,只是輸出格式有所變化。而在存儲過程或觸發器中,返回的是存儲過程或觸發器開始執行的時間。

除了上面的函數,MySQL還提供了更細緻的“現在”定義:SYSDATE()函數,每一行/每個列數據準備時才獲取時間,如下面樣例所示:

mysql> SELECT CURDATE(), CURTIME(6), NOW(6), SYSDATE(6) AS 'SYS1', SLEEP(2), SYSDATE(6) AS 'SYS2' FROM sales limit 5;        
+------------+-----------------+----------------------------+----------------------------+----------+----------------------------+
| CURDATE()  | CURTIME(6)      | NOW(6)                     | SYS1                       | SLEEP(2) | SYS2                       |
+------------+-----------------+----------------------------+----------------------------+----------+----------------------------+
| 2019-10-23 | 23:40:29.086893 | 2019-10-23 23:40:29.086893 | 2019-10-23 23:40:29.092738 |        0 | 2019-10-23 23:40:31.092824 |
| 2019-10-23 | 23:40:29.086893 | 2019-10-23 23:40:29.086893 | 2019-10-23 23:40:31.092868 |        0 | 2019-10-23 23:40:33.098602 |
| 2019-10-23 | 23:40:29.086893 | 2019-10-23 23:40:29.086893 | 2019-10-23 23:40:33.098663 |        0 | 2019-10-23 23:40:35.098779 |
| 2019-10-23 | 23:40:29.086893 | 2019-10-23 23:40:29.086893 | 2019-10-23 23:40:35.098840 |        0 | 2019-10-23 23:40:37.098912 |
| 2019-10-23 | 23:40:29.086893 | 2019-10-23 23:40:29.086893 | 2019-10-23 23:40:37.098959 |        0 | 2019-10-23 23:40:39.099050 |
+------------+-----------------+----------------------------+----------------------------+----------+----------------------------+

可以通過開啓--sysdate-is-now選項,將SYSDATE()變爲NOW()的別名,但不建議這麼做,保留一個額外的函數、多一個選擇。
注意:WHERE index_column = SYSDATE()的寫法,查詢時不走索引。因爲SYSDATE()是執行時動態計算時間,假設通過索引檢索數據、每掃到一行都會生成新值,索引也就失去了比較的意義。

以上就是獲取當前時間的函數。

上面講的是獲取本地時間(服務器時區的時間),要獲取UTC時間需通過UTC_DATE()/UTC_TIME()/UTC_TIMESTAMP(),在後面時區與UTC時間 部分有介紹。
如果不瞭解時區、UTC時間,閱讀《時區是怎麼劃分的?世界各時區的時間如何統一表達?》即可瞭解

當前周/月/年的獲取,MySQL沒有內置函數,需要通過運算得出。接下來時間計算中會講到。

時間計算/轉換

先看一個獲取當前月、當月第一天/最後一天的例子(碼字的時候是2019-10-23):

mysql> SELECT CURDATE() as '今天', DATE_FORMAT(CURDATE(), '%Y-%m') AS '當月',
	 DATE_SUB(CURDATE(), INTERVAL DAY(CURDATE())-1 DAY) as '當月第一天', 
	 LAST_DAY(CURDATE()) as '當月最後一天';
+------------+---------+-----------------+--------------------+
| 今天       | 當月    | 當月第一天      | 當月最後一天       |
+------------+---------+-----------------+--------------------+
| 2019-10-23 | 2019-10 | 2019-10-01      | 2019-10-31         |
+------------+---------+-----------------+--------------------+

這個例子中用到的幾種函數,幾乎覆蓋了MySQL時間計算的形式:

  • DATE_FORMAT:將時間轉換成指定格式的字符串;反向函數有STR_TO_DATE()、STR_TO_TIME()
  • DATE_SUB:將指定時間減去指定的週期長度,類似的還有DATE_ADD
  • DAY:等價於DAYOFMONTH(),獲取該天是當月的第幾天;類似概念的還有DAYOFYEAR()(該年的第幾天)、WEEK()(該年的第幾周)、MONTH()(該年的第幾月)
  • LAST_DAY:獲取指定時間當月的最後一天,默認輸出的是YYYY-MM-DD格式。單獨提供了這個函數,應該是出於每月天數不同、便於計算的考慮。

這些函數大致可以分爲四類:

  • 時間抽取:包括EXTRACT()DATE()HOUR()等;不涉及運算、抽取時間的部分內容
  • 時間格式轉換:包括DATE_FORMAT()STR_TO_DATE()、STR_TO_TIME();不涉及運算、抽取時間的指定部分輸出爲字符串
  • 時間增減:包括DATE_ADD()DATE_SUB()LAST_DAY等;運算、得出具體時間
  • 時間比較:包括DATEDIFF()TIMEDIFF()TIMESTAMPDIFF()DAYOFYEAR()等;運算、得出數值/差值

時間抽取

包括EXTRACT()DATE()HOUR()等;
不涉及運算、抽取時間的部分內容

  • 通用抽取函數 EXTRACT(unit FROM date)
    • EXTRACT(YEAR_MONTH FROM NOW()),表示抽取當前時間的年月,結果爲201910;其中YEAR_MONTH就是unit
    • unit有20多個枚舉值,常用的有DAY/MONTH/YEAR等,但在表達式使用、不同枚舉值和不同函數等方面有一些坑,建議使用前閱讀 附錄二:時間間隔速查表及注意事項

    EXTRACT()函數的更多細節,可參考《MySQl官方文檔 - EXTRACT》

  • 特定抽取函數:
    • DATE():抽取指定時間的日期。默認返回YYYY-MM-DD,若上下文有數字則返回YYYYMMDD
    • TIME():抽取指定時間的時分秒。
    • HOUR()MINITE()SECOND()MICROSECOND()分別抽取指定時間的小時(0~23)、分鐘、秒、微妙。
    • 抽取時有一定計算的函數:
      • WEEK()MONTHQUARTER()YEAR():抽取指定時間的是星期幾、月份、第幾季度、年份
      • DAY():等價於DAYOFMONTH(),獲取指定時間是當月的第幾天;類似的還有DAYOFWEEK()/DAYOFYEAR()/MONTH()
      • DAYNAME()MONTHNAME():獲取星期幾名稱和月名稱,如’Saturday’、‘February’。

相比於其他函數,抽取函數職能單一,這類函數平時使用會略少些。但正因爲單一,該函數是其他函數構成的基礎。

時間格式轉換(DATE_FORMAT / STR_TO_DATE)

包括DATE_FORMAT()STR_TO_DATE()、STR_TO_TIME()等函數;
不涉及運算、抽取時間的指定部分輸出爲字符串

  • 時間 => 指定格式的字符串:使用DATE_FORMAT(date,format)、TIME_FORMAT(date,format)函數。
  • 日期/時間字符串 => 日期/時間:使用STR_TO_DATE(str,format)、STR_TO_TIME(str,format)。用法與DATE_FORMAT類似,其本來就是DATE_FORMAT的逆向函數。

在MySQL中,日期/時間對象輸出時會自動轉爲字符串,而運算時字符串又會自動轉爲日期/時間對象完成運算,這中間就是時間轉換函數在發揮作用。

這類函數本身簡單,但有很多格式、難記憶。好在這些格式是各類語言基本通用的,而且也有列表速查(可參考附錄一:時間格式速查表)。

這裏提供一個樣例,包含了常用的格式:

mysql> SELECT temp.*, STR_TO_DATE(`str1`, `format1`) FROM 
	(SELECT `TIME`, `format1`, `format2`, 
		DATE_FORMAT(`TIME`, `format1`) AS 'str1', 
		DATE_FORMAT(`TIME`, `format2`) AS 'str2' 
	FROM
		(SELECT NOW(6) AS 'TIME', '%Y-%m-%d %H:%i:%s.%f' AS 'format1', 
		'%a %b %d %Y %H:%i:%s.%f' AS 'format2'
		) t 
	) temp;
+----------------------------+----------------------+-------------------------+----------------------------+---------------------------------+--------------------------------+
| TIME                       | format1              | format2                 | str1                       | str2                            | STR_TO_DATE(`str1`, `format1`) |
+----------------------------+----------------------+-------------------------+----------------------------+---------------------------------+--------------------------------+
| 2019-10-24 15:29:16.357909 | %Y-%m-%d %H:%i:%s.%f | %a %b %d %Y %H:%i:%s.%f | 2019-10-24 15:29:16.357909 | Thu Oct 24 2019 15:29:16.357909 | 2019-10-24 15:29:16.357909     |
+----------------------------+----------------------+-------------------------+----------------------------+---------------------------------+--------------------------------+

MySQL還提供的GET_FORMAT({DATE|TIME|DATETIME}, {'EUR'|'USA'|'JIS'|'ISO'|'INTERNAL'})函數,可以快速獲取各種風格的格式定義。

STR_TO_DATE的入參不合法時會返回NULL

  • 格式字符串不合法、時間字符串和格式字符串不匹配,都會判定爲不合法

    格式字符串是可以包括可見字符、格式標識符的
    但格式字符串匹配時間字符串時,這些字符在後者中與格式位置要一致;匹配到格式標識符時、時間字符串裏對應位置的字符必需是時間的一個部分。

  • sql_mode系統變量配置了NO_ZERO_DATENO_ZERO_IN_DATE時(可通過show variables like "sql_mode";查看),沒有日期或日期爲0會認爲是不合法日期,見下圖
    在這裏插入圖片描述

更多格式化的介紹,可參考《官方文檔: DATE_FORMAT》《官方文檔: STR_TO_DATE》

時間增減(DATE_ADD等)

包括DATE_ADD()DATE_SUB()LAST_DAY等;
運算、得出具體時間

  • 通用計算函數:對指定時間進行增減、獲取計算後的時間,主要是DATE_ADDDATE_SUB函數。
    語法爲DATE_ADD(date,INTERVAL expr unit),也可以寫作date + INTERVAL expr unit,如獲取昨天(今天是2019-10-24):

    mysql> SELECT DATE_SUB(CURDATE(), INTERVAL 1 DAY),
    -> CURDATE() - INTERVAL 1 DAY;
    +-------------------------------------+----------------------------+
    | DATE_SUB(CURDATE(), INTERVAL 1 DAY) | CURDATE() - INTERVAL 1 DAY |
    +-------------------------------------+----------------------------+
    | 2019-10-23                          | 2019-10-23                 |
    +-------------------------------------+----------------------------+
    

    unitEXTRACT()可選的基本一致,建議使用前閱讀 附錄二:時間間隔速查表及注意事項

  • 特定計算函數:

    • LAST_DAY:獲取指定時間當月的最後一天;如果傳入的時間不合法,則返回NULL。

時間比較(DATEDIFF等)

包括DATEDIFF()TIMEDIFF()TIMESTAMPDIFF()DAYOFYEAR()等;
運算、得出數值/差值

  • 通用比較函數:獲取任意兩個時間的差值。主要有DATEDIFFTIMEDIFFTIMESTAMPDIFF;

    • DATEDIFF(expr1, expr2)
      表示expr1expr2多幾,即expr1 - expr2。返回結果的單位是天。
      入參不嚴格、可自動轉換爲天。
      DATEDIFF('2019-10-24 23:59:59', '2019-10-23');,計算結果是1

    • TIMEDIFF(expr1, expr2)
      表示expr1 - expr2。返回結果是時間表達式hh:mm:ss,但如果入參有毫秒,則返回結果也會包括毫秒。
      入參相對不嚴格,但必需包含時間部分(無時間部分會返回NULL)、且符合時間表達式定義,可以是DATETIMETIME。可以查看下例:

      mysql> SELECT TIMEDIFF("2019-11-08 09:16", "2019-11-08 09/17"), 
      				TIMEDIFF('2019-11-08 09:16', "2019-11-08 09/17/05.123")
      

      返回結果是-00:01:00-00:01:05.123,可以看到單位與入參是有關的。

    • TIMESTAMPDIFF(unit,datetime_expr1,datetime_expr2)
      表示datetime_expr2 - datetime_expr1,注意是參數2在前,與上面兩個函數是反過來的。返回一個整數數字,單位由unit參數指定。

      單位支持MICROSECOND (microseconds), SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, QUARTER, YEAR,與EXTRACT()使用的unit推薦表達式有一定相似性,可以參考 附錄二:時間間隔速查表及注意事項

      入參datetime_expr1datetime_expr2,需是日期或日期時間表達式(可以一個是日期、一個是日期時間)。在必要時,會將DATE轉換爲時間爲00:00:00DATETIME
      例:SELECT TIMESTAMPDIFF(MONTH,'2019-11-01','2019-10-01')結果爲1

    上面例子的入參,分別使用了不同的表達式,但最終還是計算了結果出來。
    這就是前面提過的時間對象自動轉換:所有寫作字符串的時間,運算前都會通過STR_TO_DATE()等函數轉換爲時間對象,因此入參支持什麼樣格式,取決於STR_TO_DATE()等函數的支持範圍。具體可參考時間轉換函數

  • 特定比較函數:

    • DAYOFYEAR(expr):獲取指定時間是當年的第幾天
    • WEEKOFYEAR(expr):獲取指定時間是當年第幾周

時區、UTC時間、UNIX時間戳

如果不瞭解時區、UTC,可參考《時區是怎麼劃分的?世界各時區的時間如何統一表達?GMT、UTC有什麼區別?》

獲取UTC時間

UTC_DATE()/UTC_TIME()/UTC_TIMESTAMP():返回當前UTC日期/時間/日期+時間。格式、小數處理,與CURDATE()/CURTIME()/NOW()一致、僅因時區差別顯示值不同。

獲取UNIX時間戳

如不瞭解UNIX時間戳,可參考《UNIX時間、UNIX 2038問題》

UNIX_TIMESTAMP([date]):用於返回指定時間的UNIX時間戳(自1970-01-01T00:00:00後的秒數)2

  • 若未指定時間,則默認返回當前時間的時間戳,相當於UNIX_TIMESTAMP(NOW())
  • 指定的時間,服務器會按客戶端會話時區來解釋(默認與服務器時區一致,除非手工設定),將其轉換爲UTC的內部Unix時間戳值並返回
  • 指定的時間如果不包含毫秒微秒,則返回時間戳也不包含
  • 指定時間的範圍與TIMESTAMP數據類型不同,TIMESTAMP只支持1970-01-01T00:00:01.0000002038-01-19T03:14:07.999999,但大於該時間的值,UNIX_TIMESTAMP()也可以計算出時間戳來。

    MySQL日期取值範圍可參考《MySQL所有類型的字段長度和存儲開銷》

查看和設置時區

可通過SELECT @@GLOBAL.time_zone, @@SESSION.time_zone;查看當前服務器、客戶端會話的時區。
結果可能有三種:

  • SYSTEM,表示服務器時區與系統時區相同。
  • UTC偏移量,如 ‘+10:00’或’-6:00’。
  • 時區名,例如 ‘Europe/Helsinki’, 'US/Eastern’或 ‘MET’。僅當MySQL已經創建並填寫了數據庫中的時區信息表時,才能使用命名時區 。

MySQL啓動時會自主確定主機所在時區,作爲系統時區。也可以手工指定。
客戶端會話時區默認與服務器時區一致,也可以在會話中使用set time_zone自行設定,但該設定僅在當前會話有效。

關於系統時區默認值、填充時區,可以參考《官方文檔:時區變量》

時區轉換

CONVERT_TZ(dt,from_tz,to_tz):用於將日期時間從一個時區轉換到另外一個時區3

  • 例:從UTC轉到東十區,SELECT CONVERT_TZ('2019-01-01 12:00:00','+00:00','+10:00'); 結果爲 '2019-01-01 22:00:00'
  • from_tzto_tz可以使用UTC偏移量或時區名

關於MySQL對時區的支持,可參考《官方文檔: MySQL服務器時區支持》


以上。感謝您的閱讀。

附錄

附錄一:格式標識符速查表

點擊快速返回 - 時間格式轉換

標識符 描述
%Y 年,4 位
%y 年,2 位
%m 月,數值(00-12)
%c 月,數值
%b 縮寫月名
%M 月名
%d 月的天,數值(00-31)
%D 帶有英文前綴的月中的天
%e 月的天,數值(0-31)
%j 年的天 (001-366)
%H 小時 (00-23)
%h 小時 (01-12)
%k 小時 (0-23)
%I(大寫i) 小時 (01-12)
%l(小寫L) 小時 (1-12)
%i 分鐘,數值(00-59)
%S 秒(00-59)
%s 秒(00-59)
%f 微秒
%p AM 或 PM
%r 時間,12-小時(hh:mm:ss AM 或 PM)
%T 時間, 24-小時 (hh:mm:ss)
%a 縮寫星期名
%U 周 (00-53) 星期日是一週的第一天
%u 周 (00-53) 星期一是一週的第一天
%V 周 (01-53) 星期日是一週的第一天,與 %X 使用
%v 周 (01-53) 星期一是一週的第一天,與 %x 使用
%W 星期名
%w 周的天 (0=星期日, 6=星期六)
%X 年,其中的星期日是周的第一天,4 位,與 %V 使用
%x 年,其中的星期一是周的第一天,4 位,與 %v 使用

附錄二:時間間隔速查表及注意事項

點擊快速返回章節:時間抽取時間增減時間比較

注意:

  • 參數表達式是字符串;其支持各種分隔符,下表中列出的只是推薦的分隔符,比如:也可以寫作/
  • 表達式傳入數字時會自動轉換,但轉換結果可能與預期不同,可以參考下面例子:
    SELECT temp.*, 
    	DATE_ADD(`time`, INTERVAL `str` HOUR_MINUTE) AS 'addByStr', 
    	DATE_ADD(`time`, INTERVAL `number` HOUR_MINUTE) AS 'addByNumber', 
    	DATE_ADD(`time`, INTERVAL `castedNumber` HOUR_MINUTE) AS 'addByCastedNumber' 
    FROM 
    	(SELECT STR_TO_DATE('2019-06-01', '%Y-%m-%d %H') AS 'time', 
    		'6/4' AS 'str', 6/4 AS 'number', CAST(6/4 AS DECIMAL(3,1)) as 'castedNumber'
    	) temp;     
    +---------------------+-----+--------+--------------+---------------------+---------------------+---------------------+
    | time                | str | number | castedNumber | addByStr            | addByNumber         | addByCastedNumber   |
    +---------------------+-----+--------+--------------+---------------------+---------------------+---------------------+
    | 2019-06-01 00:00:00 | 6/4 | 1.5000 |          1.5 | 2019-06-01 06:04:00 | 2019-06-04 12:20:00 | 2019-06-01 01:05:00 |
    +---------------------+-----+--------+--------------+---------------------+---------------------+---------------------+
    
    6/4在sql中運算結果是1.5000,匹配單位HOUR_MINUTE時,小數點被作爲分隔符,小數點前是小時數、爲1,小數點後是分鐘數、爲5000,鎖DATE_ADD後增加了1小時、5000分鐘(≈83h),所以2019-06-01 00:00:00變成了2019-06-04 12:20:00
  • 列表中的單位,在個別函數中會有出入,比如DAY_類在EXTRACT()時並不返回天。
時間間隔的單位 傳入參數表達式
(建議使用字符串,否則容易換算錯誤)
MICROSECOND 微秒數
SECOND 秒數
MINUTE 分鐘數
HOUR 小時數
DAY 天數
WEEK 週數
MONTH 月數
QUARTER 季度數
YEAR 年數
SECOND_MICROSECOND ‘秒數.微秒數’
MINUTE_MICROSECOND ‘分鐘數:秒數.微秒數’
MINUTE_SECOND ‘分鐘數:秒數’
HOUR_MICROSECOND ‘小時數:分鐘數:秒數.微秒數’
HOUR_SECOND ‘小時數:分鐘數:秒數’
HOUR_MINUTE ‘小時數:分鐘數’
DAY_MICROSECOND ‘天數 小時數:分鐘數:秒數.微秒數’ , EXTRACT時不會返回天
DAY_SECOND ‘天數 小時數:分鐘數:秒數’, EXTRACT時不會返回天
DAY_MINUTE ‘天數 小時數:分鐘數’, EXTRACT時不會返回天
DAY_HOUR ‘天數 小時數’, EXTRACT時不會返回天
YEAR_MONTH ‘年數-月數’

《官方文檔: 時間間隔》


  1. 《MySQL官方文檔 - 日期與時間函數》 ↩︎

  2. 《MySQL官方文檔: UNIX_TIMESTAMP》 ↩︎

  3. 《MySQL官方文檔: CONVERT_TZ》 ↩︎

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章