当前位置:网站首页>【MySQL 使用秘籍】一网打尽 MySQL 时间和日期类型与相关操作函数(三)
【MySQL 使用秘籍】一网打尽 MySQL 时间和日期类型与相关操作函数(三)
2022-07-05 13:18:00 【TakingCoding4Granted】
文章目录
建议首先阅读前序两篇博文:
1. 实现日期和天或时间和秒之间的转换
问题
你希望将日期或时间转化为更基本的单位,例如将日期转换为天数,将时间转换为秒数。通常,这对于进行日期或时间的计算非常有帮助。
解决方案
转换的方式取决于待转换值的类型:
- 实现时间和秒数之间的转换,使用
TIME_TO_SEC()以及SEC_TO_TIME()函数; - 实现日期和天数之间的转换,使用
TO_DAYS()以及FROM_DAYS()函数; - 时间日期+时间和秒数之间的转换,使用
UNIX_TIMESTAMP()或FROM_UNIXTIME()函数。
讨论
在下面的讨论中,介绍了如何进行几种不同日期(时间)和天(秒)之间的相互转换。
时间和秒相互转换
如上所述,可以使用 TIME_TO_SEC() 以及 SEC_TO_TIME()函数。
函数 TIME_TO_SEC() 将一个时间值转换为对应的秒数,反之,则使用函数 SEC_TO_TIME() 。
mysql> SELECT t1,
-> TIME_TO_SEC(t1) AS 'TIME to seconds',
-> SEC_TO_TIME(TIME_TO_SEC(t1)) AS 'TIME to seconds to TIME'
-> FROM time_val;
+----------+-----------------+-------------------------+
| t1 | TIME to seconds | TIME to seconds to TIME |
+----------+-----------------+-------------------------+
| 15:00:00 | 54000 | 15:00:00 |
| 05:01:30 | 18090 | 05:01:30 |
| 12:30:20 | 45020 | 12:30:20 |
+----------+-----------------+-------------------------+
3 rows in set (0.00 sec)
如果希望将时间值转换为以分钟,小时或天为单位,则需要进行对应的除法运算:
mysql> SELECT t1,
-> TIME_TO_SEC(t1) AS 'seconds',
-> TIME_TO_SEC(t1)/60 AS 'minutes',
-> TIME_TO_SEC(t1)/(60*60) AS 'hours',
-> TIME_TO_SEC(t1)/(24*60*60) AS 'days'
-> FROM time_val;
+----------+---------+----------+---------+--------+
| t1 | seconds | minutes | hours | days |
+----------+---------+----------+---------+--------+
| 15:00:00 | 54000 | 900.0000 | 15.0000 | 0.6250 |
| 05:01:30 | 18090 | 301.5000 | 5.0250 | 0.2094 |
| 12:30:20 | 45020 | 750.3333 | 12.5056 | 0.5211 |
+----------+---------+----------+---------+--------+
3 rows in set (0.00 sec)
如果你不希望保留小数位,那么你可以对上述结果使用 FLOOR() 函数。
如果你在使用函数 TIME_TO_SEC() 时传入了一个日期+时间格式的值,那么该函数会将时间部分提取出来,然后丢弃日期部分。实际上,这也可以被当做从 DATETIME 或 TIMESTAMP 提取时间部分的另一种方式。例如:
mysql> SELECT dt,
-> TIME_TO_SEC(dt) AS 'time part in seconds',
-> SEC_TO_TIME(TIME_TO_SEC(dt)) AS 'time part as TIME'
-> FROM datetime_val;
+---------------------+----------------------+-------------------+
| dt | time part in seconds | time part as TIME |
+---------------------+----------------------+-------------------+
| 1970-01-01 00:00:00 | 0 | 00:00:00 |
| 1999-12-31 09:00:00 | 32400 | 09:00:00 |
| 2000-06-04 15:45:30 | 56730 | 15:45:30 |
| 2017-03-16 12:30:15 | 45015 | 12:30:15 |
+---------------------+----------------------+-------------------+
4 rows in set (0.00 sec)
日期和天相互转换
如上所述,如果你有一个时间值,但是你希望将其和天数之间相互转换,你可以使用 TO_DAYS()以及 FROM_DAYS()函数。
mysql> SELECT d,
-> TO_DAYS(d) AS 'DATE to days',
-> FROM_DAYS(TO_DAYS(d)) AS 'DATE to days to DATE'
-> FROM date_val;
+------------+--------------+----------------------+
| d | DATE to days | DATE to days to DATE |
+------------+--------------+----------------------+
| 1864-02-28 | 680870 | 1864-02-28 |
| 1900-01-15 | 693975 | 1900-01-15 |
| 1999-12-31 | 730484 | 1999-12-31 |
| 2000-06-04 | 730640 | 2000-06-04 |
| 2017-03-16 | 736769 | 2017-03-16 |
+------------+--------------+----------------------+
5 rows in set (0.00 sec)
类似地,如果你为 TO_DAYS() 函数传入日期+时间值,那么该函数会将日期部分提取出来,然后丢弃时间部分。例如:
mysql> SELECT dt,
-> TO_DAYS(dt) AS 'date part in days',
-> FROM_DAYS(TO_DAYS(dt)) AS 'date part as DATE'
-> FROM datetime_val;
+---------------------+-------------------+-------------------+
| dt | date part in days | date part as DATE |
+---------------------+-------------------+-------------------+
| 1970-01-01 00:00:00 | 719528 | 1970-01-01 |
| 1999-12-31 09:00:00 | 730484 | 1999-12-31 |
| 2000-06-04 15:45:30 | 730640 | 2000-06-04 |
| 2017-03-16 12:30:15 | 736769 | 2017-03-16 |
+---------------------+-------------------+-------------------+
4 rows in set (0.00 sec)
日期+时间和秒相互转换
对于 DATETIME 和 TIMESTAMP 类型的值,如果这两个类型的值在 TIMESTAMP 类型的范围内,即从 1970 年到 2038 年,那么可以使用 UNIX_TIMESTAMP() 和 FROM_UNIXTIME() 来实现二者和秒数之间的相互转换。例如:
mysql> SELECT dt,
-> UNIX_TIMESTAMP(dt) AS seconds,
-> FROM_UNIXTIME(UNIX_TIMESTAMP(dt)) AS timestamp
-> FROM datetime_val;
+---------------------+------------+---------------------+
| dt | seconds | timestamp |
+---------------------+------------+---------------------+
| 1970-01-01 00:00:00 | 0 | 1970-01-01 08:00:00 |
| 1999-12-31 09:00:00 | 946602000 | 1999-12-31 09:00:00 |
| 2000-06-04 15:45:30 | 960104730 | 2000-06-04 15:45:30 |
| 2017-03-16 12:30:15 | 1489638615 | 2017-03-16 12:30:15 |
+---------------------+------------+---------------------+
4 rows in set (0.00 sec)
函数 UNIX_TIMESTAMP() 也可以将 DATE 类型的值转换为秒数,该函数默认这些日期的时间部分都是 00:00:00 :
mysql> SELECT
-> CURDATE(),
-> UNIX_TIMESTAMP(CURDATE()),
-> FROM_UNIXTIME(UNIX_TIMESTAMP(CURDATE()));
+------------+---------------------------+------------------------------------------+
| CURDATE() | UNIX_TIMESTAMP(CURDATE()) | FROM_UNIXTIME(UNIX_TIMESTAMP(CURDATE())) |
+------------+---------------------------+------------------------------------------+
| 2022-07-02 | 1656691200 | 2022-07-02 00:00:00 |
+------------+---------------------------+------------------------------------------+
1 row in set (0.00 sec)
2. 计算日期或时间的间隔
问题
你希望知道两个日期或时间之间的间隔。
解决方案
想要计算两个日期或时间之间的间隔,你可以使用专门的函数,也可以先将日期(时间)转换为天(秒),然后计算二者之间的间隔。至于使用具体哪一个函数,这主要取决于你想要计算的两个值究竟是日期还是时间类型。
讨论
下面介绍了几种用于计算日期(时间)间隔的方式。
使用日期(时间)函数计算间隔
想要计算两个日期值之间间隔的天数,可以使用 DATEDIFF() 函数:
mysql> SET @d1 = '2022-01-01', @d2 = '2021-12-01';
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT DATEDIFF(@d1, @d2) AS 'd1 - d2', DATEDIFF(@d2, @d1) AS 'd2 - d1';
+---------+---------+
| d1 - d2 | d2 - d1 |
+---------+---------+
| 31 | -31 |
+---------+---------+
1 row in set (0.00 sec)
函数 DATEDIFF() 也适用于两个日期+时间类型的值,但是会在计算时忽略时间部分。因此,该函数适用于两个 DATE ,DATETIME 或 TIMESTAMP 类型的值之间进行间隔天数的计算。
想要计算两个时间值之间的间隔,可以使用 TIMEDIFF() 函数:
mysql> SET @t1 = '12:00:00', @t2 = '16:30:00';
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT TIMEDIFF(@t1, @t2) AS 't1 - t2', TIMEDIFF(@t2, @t1) AS 't2 - t1';
+------------------+-----------------+
| t1 - t2 | t2 - t1 |
+------------------+-----------------+
| -04:30:00.000000 | 04:30:00.000000 |
+------------------+-----------------+
1 row in set (0.00 sec)
函数 TIMEDIFF() 也适用于两个日期+时间类型的值。
两个时间值之间的间隔可以表示为一个 TIME 类型的值,而后者又可以进一步使用 HOUR() ,MINUTE() 以及 SECOND() 函数进行拆分。例如:
mysql> SELECT t1, t2,
-> TIMEDIFF(t2, t1) AS 't2 - t1 as TIME',
-> IF(TIMEDIFF(t2, t1) >= 0,'+','-') AS sign,
-> HOUR(TIMEDIFF(t2, t1)) AS hour,
-> MINUTE(TIMEDIFF(t2, t1)) AS minute,
-> SECOND(TIMEDIFF(t2, t1)) AS second
-> FROM time_val;
+----------+----------+-----------------+------+------+--------+--------+
| t1 | t2 | t2 - t1 as TIME | sign | hour | minute | second |
+----------+----------+-----------------+------+------+--------+--------+
| 15:00:00 | 15:00:00 | 00:00:00 | + | 0 | 0 | 0 |
| 05:01:30 | 02:30:20 | -02:31:10 | - | 2 | 31 | 10 |
| 12:30:20 | 17:30:45 | 05:00:25 | + | 5 | 0 | 25 |
+----------+----------+-----------------+------+------+--------+--------+
3 rows in set (0.00 sec)
如果你希望计算两个日期或两个日期+时间值之间的间隔,也可以使用 TIMESTAMPDIFF() 函数。该函数比较灵活,你可以指定期望的间隔单位。例如:
mysql> SET @dt1 = '1900-01-01 00:00:00', @dt2 = '1910-01-01 00:00:00';
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT
-> TIMESTAMPDIFF(MINUTE, @dt1, @dt2) AS minutes,
-> TIMESTAMPDIFF(HOUR, @dt1, @dt2) AS hours,
-> TIMESTAMPDIFF(DAY, @dt1, @dt2) AS days,
-> TIMESTAMPDIFF(WEEK, @dt1, @dt2) AS weeks,
-> TIMESTAMPDIFF(YEAR, @dt1, @dt2) AS years;
+---------+-------+------+-------+-------+
| minutes | hours | days | weeks | years |
+---------+-------+------+-------+-------+
| 5258880 | 87648 | 3652 | 521 | 10 |
+---------+-------+------+-------+-------+
1 row in set (0.00 sec)
实际上,函数 TIMESTAMPDIFF() 除了支持上述间隔单位外,还支持 MICROSECOND ,MONTH ,QUARTER 。此外,函数 TIMESTAMP() 可以用来很方便地计算出一个人的年龄。
对于函数 TIMESTAMPDIFF() ,有以下两点需要特别注意:
- 如果第一个日期(时间)值大于第二个,那么结果将为负数,这和
DATEDIFF()以及TIMEDIFF()函数刚好相反; - 虽然函数
TIMESTAMPDIFF()的函数名中包含TIMESTAMP,但是其参数的范围却不受TIMESTAMP类型取值范围的限制。
使用基本单位计算间隔
计算两个日期(时间)之间间隔的另一种方式是使用如:秒和天在内的基本单位:
- 将两个日期(时间)转换为天或秒;
- 使用上述两个转换后的值进行间隔计算;
- 将以天为秒为单位的计算结果转回日期或时间。
根据希望计算间隔的是日期还是时间,需要使用不同的转换函数:
- 使用函数
TIME_TO_SEC()和SEC_TO_TIME()进行时间和秒之间的转换; - 使用函数
TO_DAYS()和FROM_DAYS()进行日期和天数之间的转换; - 使用函数
UNIX_TIMESTAMP()和FROM_UNIXTIME()进行日期+时间和秒之间的转换。
使用秒计算时间间隔
mysql> SELECT t1, t2,
-> TIME_TO_SEC(t2) - TIME_TO_SEC(t1) AS 't2 - t1 (in seconds)',
-> SEC_TO_TIME(TIME_TO_SEC(t2) - TIME_TO_SEC(t1)) AS 't2 - t1 (as TIME)'
-> FROM time_val;
+----------+----------+----------------------+-------------------+
| t1 | t2 | t2 - t1 (in seconds) | t2 - t1 (as TIME) |
+----------+----------+----------------------+-------------------+
| 15:00:00 | 15:00:00 | 0 | 00:00:00 |
| 05:01:30 | 02:30:20 | -9070 | -02:31:10 |
| 12:30:20 | 17:30:45 | 18025 | 05:00:25 |
+----------+----------+----------------------+-------------------+
3 rows in set (0.00 sec)
使用基本单位计算两个日期或日期+时间的间隔
当你希望先将两个日期相对于同一个给定的参考时间点转换为共同的基本单位,然后计算二者的间隔时,日期的取值范围决定了可用的转换单位:
- 对于自 1970-01-01 00:00:00 UTC 往后的
DATE,DATETIME或TIMESTAMP类型,你可以先将这些类型的值转换为以秒为单位,以实现精确到秒级的间隔精确度; - 对于从格里高利历实行的开始时间即 1582 年至 1970-01-01 00:00:00 UTC 的日期,你可以先将日期转换为以秒为单位,以实现精确到天级别的间隔精确度;
- 对于更早的日期而言,先将日期转换为基本单位,然后计算间隔的方式就比较困难了。此时,需要使用编程语言的 API ,因为使用 SQL 语句会比较困难或压根不可行。
想要计算两个日期或日期+时间值之间间隔的天数,可以先使用 TO_DAYS() 函数将其转换为天数,然后计算二者以天为单位的间隔。如果希望得到二者以星期为单位的间隔,可以再将结果除以七:
mysql> SET @days = TO_DAYS('1884-01-01') - TO_DAYS('1883-06-05');
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT @days AS days, @days/7 AS weeks;
+------+---------+
| days | weeks |
+------+---------+
| 210 | 30.0000 |
+------+---------+
1 row in set (0.00 sec)
需要注意的是,你不能直接通过类似的方式将天数转换为月数或年数,因为这二者的长度不尽相同。如果想要实现这样的效果,需要使用上面介绍的 TIMESTAMPDIFF() 函数。
针对介于 TIMESTAMP 类型范围内,即介于 1970 至 2038 年之间的日期+时间值,你可以通过使用 UNIX_TIMESTAMP() 函数来实现两个这样日期+时间值之间精确到秒级的时间间隔。想要得到以其他单位表示的间隔,可以很容易将秒数转换为分钟数,小时数,天数或星期数。例如:
mysql> SET @dt1 = '1984-01-01 09:00:00';
Query OK, 0 rows affected (0.00 sec)
mysql> SET @dt2 = @dt1 + INTERVAL 14 DAY;
Query OK, 0 rows affected (0.00 sec)
mysql> SET @interval = UNIX_TIMESTAMP(@dt2) - UNIX_TIMESTAMP(@dt1);
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT @interval AS seconds,
-> FLOOR(@interval / 60) AS minutes,
-> FLOOR(@interval / (60 * 60)) AS hours,
-> FLOOR(@interval / (24 * 60 * 60)) AS days,
-> FLOOR(@interval / (7 * 24 * 60 * 60)) AS weeks;
+---------+---------+-------+------+-------+
| seconds | minutes | hours | days | weeks |
+---------+---------+-------+------+-------+
| 1209600 | 20160 | 336 | 14 | 2 |
+---------+---------+-------+------+-------+
1 row in set (0.00 sec)
对于 TIMESTAMP 类型可以表示的范围以外的值,因为无法像上述情况一样使用 UNIX_TIMESTAMP() 函数,所以计算两个日期+时间类型值之间间隔的计算方式更加繁琐一点:
- 首先使用
TO_DAYS()函数计算出二者的日期部分相差的天数,然后乘以 24 × 60 × 60 得到秒数; - 接着使用
TIME_TO_SEC()函数计算出二者时间部分相差的秒数。
mysql> SET @dt1 = '1800-02-14 07:30:00';
Query OK, 0 rows affected (0.00 sec)
mysql> SET @dt2 = '1800-02-17 06:30:00';
Query OK, 0 rows affected (0.00 sec)
mysql> SET @interval =
-> ((TO_DAYS(@dt2) - TO_DAYS(@dt1)) * 24*60*60)
-> + TIME_TO_SEC(@dt2) - TIME_TO_SEC(@dt1);
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT @interval AS seconds, SEC_TO_TIME(@interval) AS TIME;
+---------+----------+
| seconds | TIME |
+---------+----------+
| 255600 | 71:00:00 |
+---------+----------+
1 row in set (0.00 sec)
3. 日期或时间相加
问题
你希望实现日期或时间相加。例如,你希望将给定的秒数加到一个时间值上,或者你希望确定从今天往后三周的日期。
解决方案
实现日期或时间相加,你有以下几个选择:
- 使用专用的函数;
- 使用
+ INTERVAL或- INTERVAL操作符; - 先将对应值转换为基本单位,然后进行相加求和。
需要注意的是,具体适用的函数或操作符取决于日期或时间值的类型。
讨论
使用专用函数或操作符实现日期或时间相加
想要将一个时间类型值加到另一个时间类型值或日期+时间类型值上,可以使用 ADDTIME() 函数:
mysql> SELECT ADDTIME(@t1, @t2);
+-------------------+
| ADDTIME(@t1, @t2) |
+-------------------+
| 27:30:00 |
+-------------------+
1 row in set (0.00 sec)
mysql> SET @dt = '1984-03-01 12:00:00', @t = '12:00:00';
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT ADDTIME(@dt, @t);
+---------------------+
| ADDTIME(@dt, @t) |
+---------------------+
| 1984-03-02 00:00:00 |
+---------------------+
1 row in set (0.00 sec)
想要将一个时间类型值加到另一个日期类型值或日期+时间类型值上,可以使用 TIMESTAMP() 函数:
mysql> SET @d = '1984-03-01', @t = '15:30:00';
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT TIMESTAMP(@d, @t);
+----------------------------+
| TIMESTAMP(@d, @t) |
+----------------------------+
| 1984-03-01 15:30:00.000000 |
+----------------------------+
1 row in set (0.00 sec)
mysql> SET @dt = '1984-03-01 12:00:00', @t = '12:00:00';
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT TIMESTAMP(@dt, @t);
+----------------------------+
| TIMESTAMP(@dt, @t) |
+----------------------------+
| 1984-03-02 00:00:00.000000 |
+----------------------------+
1 row in set (0.00 sec)
MySQL 还提供了 DATE_ADD() 和 DATE_SUB() 两个函数,这两个函数可用于将一个时间段加到一个日期上,或者从一个日期上减去一个时间段。每个函数都接受一个日期或日期+时间类型的值 d 以及一个时间段作为参数,即函数的使用语法如下:
DATE_ADD(d, INTERVAL val unit)
DATE_SUB(d, INTERVAL val unit)
操作符 + INTERVAL 和 - INTERVAL 可以实现类似功能:
d + INTERVAL val unit
d - INTERVAL val unit
其中 _unit_是时间间隔的单位,而 val 是时间间隔的数量。常见的单位有 SECOND ,MINUTE ,HOUR ,DAY ,MONTH 以及 YEAR 。
- 得到从今天往后三天的日期:
mysql> SELECT CURDATE(), DATE_ADD(CURDATE(), INTERVAL 3 DAY);
+------------+-------------------------------------+
| CURDATE() | DATE_ADD(CURDATE(), INTERVAL 3 DAY) |
+------------+-------------------------------------+
| 2022-07-03 | 2022-07-06 |
+------------+-------------------------------------+
1 row in set (0.00 sec)
- 得到从今天往后一周的日期:
mysql> SELECT CURDATE(), DATE_SUB(CURDATE(), INTERVAL 1 WEEK);
+------------+--------------------------------------+
| CURDATE() | DATE_SUB(CURDATE(), INTERVAL 1 WEEK) |
+------------+--------------------------------------+
| 2022-07-03 | 2022-06-26 |
+------------+--------------------------------------+
1 row in set (0.00 sec)
- 得到从现在往后 60 个小时的日期和时间:
mysql> SELECT NOW(), DATE_ADD(NOW(), INTERVAL 60 HOUR);
+---------------------+-----------------------------------+
| NOW() | DATE_ADD(NOW(), INTERVAL 60 HOUR) |
+---------------------+-----------------------------------+
| 2022-07-03 00:27:09 | 2022-07-05 12:27:09 |
+---------------------+-----------------------------------+
1 row in set (0.00 sec)
- 得到从现在往后 14.5 个小时的日期和时间:
mysql> SELECT NOW(), DATE_ADD(NOW(), INTERVAL '14:30' HOUR_MINUTE);
+---------------------+-----------------------------------------------+
| NOW() | DATE_ADD(NOW(), INTERVAL '14:30' HOUR_MINUTE) |
+---------------------+-----------------------------------------------+
| 2022-07-03 00:28:47 | 2022-07-03 14:58:47 |
+---------------------+-----------------------------------------------+
1 row in set (0.00 sec)
- 得到从现在往后 3 天 4 个小时的日期和时间:
mysql> SELECT NOW(), DATE_ADD(NOW(), INTERVAL '3 4' DAY_HOUR);
+---------------------+------------------------------------------+
| NOW() | DATE_ADD(NOW(), INTERVAL '3 4' DAY_HOUR) |
+---------------------+------------------------------------------+
| 2022-07-03 00:30:14 | 2022-07-06 04:30:14 |
+---------------------+------------------------------------------+
1 row in set (0.00 sec)
实际上,函数 DATE_ADD() 和 DATE_SUB() 是可互换使用的,例如,下面两个表达式实际上是等价的:
DATE_ADD(d, INTERVAL -3 MONTH)
DATE_SUB(d, INTERVAL 3 MONTH)
类似地,除了使用函数 DATE_ADD() 和 DATE_SUB() ,你也可以使用操作符 + INTERVAL 和 - INTERVAL 实现相同的功能:
mysql> SELECT CURDATE(), CURDATE() + INTERVAL 1 YEAR;
+------------+-----------------------------+
| CURDATE() | CURDATE() + INTERVAL 1 YEAR |
+------------+-----------------------------+
| 2022-07-03 | 2023-07-03 |
+------------+-----------------------------+
1 row in set (0.00 sec)
mysql> SELECT NOW(), NOW() - INTERVAL '1 12' DAY_HOUR;
+---------------------+----------------------------------+
| NOW() | NOW() - INTERVAL '1 12' DAY_HOUR |
+---------------------+----------------------------------+
| 2022-07-03 00:36:58 | 2022-07-01 12:36:58 |
+---------------------+----------------------------------+
1 row in set (0.00 sec)
函数 TIMESTAMPADD()也可以实现将一个时间段加到日期或日期+时间的值上,其使用的语法也和函数 DATE_ADD() 比较类似:
TIMESTAMPADD(unit,interval,d) = DATE_ADD(d,INTERVAL interval unit)
4. 计算某月的第一天、最后一天或天数
问题
给定一个日期,你希望确定该日期所在月份的第一天或最后一天日期,或者在该月份 n 个月之后的月份中第一天或最后一天的日期,以及某个月份中一共有多少天。
解决方案
为了得到某个日期所在月份第一天的日期值,需要使用使用日期平移计算。为了得到某个日期所在月份的最后一天的日期值,可以直接使用 LAST_DAY() 函数。为了得到某个月份的天数,可以先得到该月份最后一天的日期值,然后对其使用 DAYOFMONTH() 函数即可。
讨论
有时候,你可能有一个参考日期,并且你希望得到一个目标日期值,但是二者之间并没有固定的相对关系。例如,当前日期到本月第一天和最后一天的天数并非固定的天数。
为了得到给定日期值所在月份第一天的日期,可以将给定日期向前平移比 DAYOFMONTH() 少 1 的天数即可:
mysql> SELECT d, DATE_SUB(d, INTERVAL DAYOFMONTH(d) - 1 DAY) AS '1st of month'
-> FROM date_val;
+------------+--------------+
| d | 1st of month |
+------------+--------------+
| 1864-02-28 | 1864-02-01 |
| 1900-01-15 | 1900-01-01 |
| 1999-12-31 | 1999-12-01 |
| 2000-06-04 | 2000-06-01 |
| 2017-03-16 | 2017-03-01 |
+------------+--------------+
5 rows in set (0.00 sec)
更一般地,如果希望得到距离给定日期 n 个月之后的日期所在月份的第一天的日期值,可以使用下列 SQL 语句:
DATE_ADD(DATE_SUB(d,INTERVAL DAYOFMONTH(d)-1 DAY),INTERVAL n MONTH)
例如,针对给定日期,为了确定该日期前一个月和后一个月的第一天的日期,可以使用下列 SQL 语句:
mysql> SELECT d,
-> DATE_ADD(DATE_SUB(d, INTERVAL DAYOFMONTH(d) - 1 DAY), INTERVAL -1 MONTH)
-> AS '1st of previous month',
-> DATE_ADD(DATE_SUB(d, INTERVAL DAYOFMONTH(d) - 1 DAY), INTERVAL 1 MONTH)
-> AS '1st of following month'
-> FROM date_val;
+------------+-----------------------+------------------------+
| d | 1st of previous month | 1st of following month |
+------------+-----------------------+------------------------+
| 1864-02-28 | 1864-01-01 | 1864-03-01 |
| 1900-01-15 | 1899-12-01 | 1900-02-01 |
| 1999-12-31 | 1999-11-01 | 2000-01-01 |
| 2000-06-04 | 2000-05-01 | 2000-07-01 |
| 2017-03-16 | 2017-02-01 | 2017-04-01 |
+------------+-----------------------+------------------------+
5 rows in set (0.00 sec)
想要得到某给定日期所在月份最后一天的日期相对而言就简单很多,因为可以直接使用函数 LAST_DAY() :
mysql> SELECT d, LAST_DAY(d) AS 'last of month'
-> FROM date_val;
+------------+---------------+
| d | last of month |
+------------+---------------+
| 1864-02-28 | 1864-02-29 |
| 1900-01-15 | 1900-01-31 |
| 1999-12-31 | 1999-12-31 |
| 2000-06-04 | 2000-06-30 |
| 2017-03-16 | 2017-03-31 |
+------------+---------------+
5 rows in set (0.00 sec)
类似地,更一般地,如果希望得到距离给定日期 n 个月之后的日期所在月份的最后一天的日期值,可以使用下列 SQL 语句:
LAST_DAY(DATE_ADD(d, INTERVAL n MONTH))
例如,针对给定的日期,想要得到相对该日期一个月前和一个月后的日期所在月最后一天的日期,可以使用下列 SQL 语句:
mysql> SELECT d,
-> LAST_DAY(DATE_ADD(d, INTERVAL -1 MONTH))
-> AS 'last of previous month',
-> LAST_DAY(DATE_ADD(d, INTERVAL 1 MONTH))
-> AS 'last of following month'
-> FROM date_val;
+------------+------------------------+-------------------------+
| d | last of previous month | last of following month |
+------------+------------------------+-------------------------+
| 1864-02-28 | 1864-01-31 | 1864-03-31 |
| 1900-01-15 | 1899-12-31 | 1900-02-28 |
| 1999-12-31 | 1999-11-30 | 2000-01-31 |
| 2000-06-04 | 2000-05-31 | 2000-07-31 |
| 2017-03-16 | 2017-02-28 | 2017-04-30 |
+------------+------------------------+-------------------------+
5 rows in set (0.00 sec)
为了得到某个月份的天数,可以先使用 LAST_DAY() 函数,然后再使用 DAYOFMONTH() 函数:
mysql> SELECT d, DAYOFMONTH(LAST_DAY(d)) AS 'days in month' FROM date_val;
+------------+---------------+
| d | days in month |
+------------+---------------+
| 1864-02-28 | 29 |
| 1900-01-15 | 31 |
| 1999-12-31 | 31 |
| 2000-06-04 | 30 |
| 2017-03-16 | 31 |
+------------+---------------+
5 rows in set (0.00 sec)
5. 查找某日期的星期名
问题
你希望知道某给定日期的星期名。
解决方案
使用 DAYNAME() 函数。
讨论
想要知道某给定日期对应的星期名,可以使用 DAYNAME() 函数:
mysql> SELECT CURDATE(), DAYNAME(CURDATE());
+------------+--------------------+
| CURDATE() | DAYNAME(CURDATE()) |
+------------+--------------------+
| 2022-07-03 | Sunday |
+------------+--------------------+
1 row in set (0.00 sec)
DAYNAME() 函数通常会和其他日期相关的操作结合使用。例如,想要确定某月第一天的星期名,可以结合函数 DAYOFMONTH() :
mysql> SET @d = CURDATE();
Query OK, 0 rows affected (0.00 sec)
mysql> SET @first = DATE_SUB(@d, INTERVAL DAYOFMONTH(@d) - 1 DAY);
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT @d AS 'starting date',
-> @first AS '1st of month date',
-> DAYNAME(@first) AS '1st of month day';
+---------------+-------------------+------------------+
| starting date | 1st of month date | 1st of month day |
+---------------+-------------------+------------------+
| 2022-07-03 | 2022-07-01 | Friday |
+---------------+-------------------+------------------+
1 row in set (0.00 sec)
6. 查找某一周中任何一个工作日的日期
问题
你需要计算给定日期所在周的某个工作日对应的日期。假设你希望知道和 2014-07-09 在同一周的周二所对应的日期。
解决方案
见下列讨论部分。
讨论
由于给定了日期 2014-07-09 ,想要知道和该日期在同一周的周二对应的日期,那么你需要先知道 2014-07-09 这个日期对应的星期数。例如,如果 2014-07-09 对应周一,那么和该日期在同一周的周二对应的日期就是该日期向后平移一天,即 2014-07-10 ;如果 2014-07-09 对应周三,那么和该日期在同一周的周二对应的日期就是该日期向前平移一天,即 2014-07-08 。
针对以上需求,MySQL 提供了两个有用的函数。函数 DAYOFWEEK() 将周日视为一周的第一天,周六视为一周的最后一天,且从周日到周六依次对应数字 1 1 1 到 7 7 7 ;函数 WEEKDAY() 将周一视为一周的第一天,且从周一到周日依次对应数字 0 0 0 到 6 6 6 。
有了上述函数,那么求解上述问题可以按照这样的思路,即:
- 首先,将给定日期向前平移
DAYOFWEEK()天,这一步会得到一个日期,且该日期总是给定日期所在周前一周的周六; - 接着,将第 1 1 1 步中得到的日期向后平移一天则得到周日的日期,向后平移两天则得到周一的日期,依次类推。
上述步骤对应到 SQL 语句如下:
DATE_ADD(DATE_SUB(d, INTERVAL DAYOFWEEK(d) DAY), INTERVAL n DAY)
其中 d 表示给定的一个日期, n n n 的取值范围是从 1 1 1 到 7 7 7,分别可以得到和日期 d 在同一周的周日到周六对应的日期。实际上,上述表达式还可以进一步简化如下:
针对 date_val 表中的记录,可以得到和其中的日期在同一周的周日和周六(这里周日为一周的第一天,周六的最后一天)所对应的日期如下:
mysql> SELECT d, DAYNAME(d) AS day,
-> DATE_ADD(d, INTERVAL 1 - DAYOFWEEK(d) DAY) AS Sunday,
-> DATE_ADD(d, INTERVAL 7 - DAYOFWEEK(d) DAY) AS Saturday
-> FROM date_val;
+------------+----------+------------+------------+
| d | day | Sunday | Saturday |
+------------+----------+------------+------------+
| 1864-02-28 | Sunday | 1864-02-28 | 1864-03-05 |
| 1900-01-15 | Monday | 1900-01-14 | 1900-01-20 |
| 1999-12-31 | Friday | 1999-12-26 | 2000-01-01 |
| 2000-06-04 | Sunday | 2000-06-04 | 2000-06-10 |
| 2017-03-16 | Thursday | 2017-03-12 | 2017-03-18 |
+------------+----------+------------+------------+
5 rows in set (0.00 sec)
边栏推荐
- go map
- go 字符串操作
- Go array and slice
- Rocky basics 1
- APICloud Studio3 API管理与调试使用教程
- Hiengine: comparable to the local cloud native memory database engine
- Shu tianmeng map × Weiyan technology - Dream map database circle of friends + 1
- go map
- Difference between avc1 and H264
- Default parameters of function & multiple methods of function parameters
猜你喜欢

DataPipeline双料入选中国信通院2022数智化图谱、数据库发展报告

百日完成国产数据库opengausss的开源任务--openGuass极简版3.0.0安装教程

CloudCompare——点云切片

《2022年中国银行业RPA供应商实力矩阵分析》研究报告正式启动

简单上手的页面请求和解析案例

潘多拉 IOT 开发板学习(HAL 库)—— 实验7 窗口看门狗实验(学习笔记)

OpenHarmony应用开发之Navigation组件详解

Pandora IOT development board learning (HAL Library) - Experiment 7 window watchdog experiment (learning notes)

SAE international strategic investment geometry partner

Cloudcompare - point cloud slice
随机推荐
Hundred days to complete the open source task of the domestic database opengauss -- openguass minimalist version 3.0.0 installation tutorial
Flutter draws animation effects of wave movement, curves and line graphs
uni-app开发语音识别app,讲究的就是简单快速。
“百度杯”CTF比赛 九月场,Web:Upload
初次使用腾讯云,解决只能使用webshell连接,不能使用ssh连接。
CloudCompare——点云切片
Simple page request and parsing cases
[notes of in-depth study paper]uctransnet: rethink the jumping connection in u-net from the perspective of transformer channel
#yyds干货盘点# 解决名企真题:搬圆桌
[深度学习论文笔记]UCTransNet:从transformer的通道角度重新思考U-Net中的跳跃连接
It's too convenient. You can complete the code release and approval by nailing it!
MySQL giant pit: update updates should be judged with caution by affecting the number of rows!!!
JPA规范总结和整理
Flutter InkWell & Ink组件
数据泄露怎么办?'华生·K'7招消灭安全威胁
[深度学习论文笔记]使用多模态MR成像分割脑肿瘤的HNF-Netv2
Small case of function transfer parameters
Le rapport de recherche sur l'analyse matricielle de la Force des fournisseurs de RPA dans le secteur bancaire chinois en 2022 a été officiellement lancé.
The Research Report "2022 RPA supplier strength matrix analysis of China's banking industry" was officially launched
Cf:a. the third three number problem