我正在寻找计算 2 个日期时间字段之间的月数。
有没有比获取 unix 时间戳和除以 2 592 000(秒)并在 MySQL 中四舍五入更好的方法?
我正在寻找计算 2 个日期时间字段之间的月数。
有没有比获取 unix 时间戳和除以 2 592 000(秒)并在 MySQL 中四舍五入更好的方法?
我很惊讶这还没有被提及:
看看 MySQL 中的TIMESTAMPDIFF()函数。
这允许您做的是传递两个TIMESTAMP
或DATETIME
值(甚至DATE
MySQL 将自动转换)以及您想要基于差异的时间单位。
您可以MONTH
在第一个参数中指定为单位:
SELECT TIMESTAMPDIFF(MONTH, '2012-05-05', '2012-06-04')
-- Outputs: 0
SELECT TIMESTAMPDIFF(MONTH, '2012-05-05', '2012-06-05')
-- Outputs: 1
SELECT TIMESTAMPDIFF(MONTH, '2012-05-05', '2012-06-15')
-- Outputs: 1
SELECT TIMESTAMPDIFF(MONTH, '2012-05-05', '2012-12-16')
-- Outputs: 7
它基本上获取从参数列表中的第一个日期开始经过的月数。该解决方案会自动补偿每个月 (28,30,31) 的不同天数,并考虑闰年——您不必担心任何这些事情。
如果您想在经过的月数中引入小数精度,这会稍微复杂一些,但您可以这样做:
SELECT
TIMESTAMPDIFF(MONTH, startdate, enddate) +
DATEDIFF(
enddate,
startdate + INTERVAL
TIMESTAMPDIFF(MONTH, startdate, enddate)
MONTH
) /
DATEDIFF(
startdate + INTERVAL
TIMESTAMPDIFF(MONTH, startdate, enddate) + 1
MONTH,
startdate + INTERVAL
TIMESTAMPDIFF(MONTH, startdate, enddate)
MONTH
)
您的日期参数在哪里startdate
以及enddate
是来自表中的两个日期列还是来自脚本的输入参数:
例子:
With startdate = '2012-05-05' AND enddate = '2012-05-27':
-- Outputs: 0.7097
With startdate = '2012-05-05' AND enddate = '2012-06-13':
-- Outputs: 1.2667
With startdate = '2012-02-27' AND enddate = '2012-06-02':
-- Outputs: 3.1935
PERIOD_DIFF计算两个日期之间的月份。
例如,要计算 now() 和 your_table 中时间列之间的差异:
select period_diff(date_format(now(), '%Y%m'), date_format(time, '%Y%m')) as months from your_table;
我也使用PERIOD_DIFF。要获取日期的年份和月份,我使用函数 EXTRACT:
SELECT PERIOD_DIFF(EXTRACT(YEAR_MONTH FROM NOW()), EXTRACT(YEAR_MONTH FROM time)) AS months FROM your_table;
DATEDIFF函数可以为您提供两个日期之间的天数。哪个更准确,因为...你如何定义一个月?(28、29、30 还是 31 天?)
正如这里的许多答案所示,“正确”答案取决于您的需求。就我而言,我需要四舍五入到最接近的整数。
考虑以下示例: 1 月 1 日 -> 1 月 31 日:整整 0 个月,将近 1 个月。1 月 1 日 -> 2 月 1 日?整整1个月,正好1个月。
要获取整个(完整)月数,请使用:
SELECT TIMESTAMPDIFF(MONTH, '2018-01-01', '2018-01-31'); => 0
SELECT TIMESTAMPDIFF(MONTH, '2018-01-01', '2018-02-01'); => 1
要获得以月为单位的四舍五入持续时间,您可以使用:
SELECT ROUND(TIMESTAMPDIFF(DAY, '2018-01-01', '2018-01-31')*12/365.24); => 1
SELECT ROUND(TIMESTAMPDIFF(DAY, '2018-01-01', '2018-01-31')*12/365.24); => 1
这精确到 +/- 5 天,范围超过 1000 年。Zane 的回答显然更准确,但对我来说太冗长了。
我更喜欢这种方式,因为每个人第一眼都会清楚地理解它:
SELECT
12 * (YEAR(to) - YEAR(from)) + (MONTH(to) - MONTH(from)) AS months
FROM
tab;
来自 MySQL 手册:
PERIOD_DIFF(P1,P2)
返回周期 P1 和 P2 之间的月数。P1 和 P2 的格式应为 YYMM 或 YYYYMM。请注意,句点参数 P1 和 P2 不是日期值。
mysql> SELECT PERIOD_DIFF(200802,200703); -> 11
所以有可能做这样的事情:
Select period_diff(concat(year(d1),if(month(d1)<10,'0',''),month(d1)), concat(year(d2),if(month(d2)<10,'0',''),month(d2))) as months from your_table;
其中 d1 和 d2 是日期表达式。
我必须使用 if() 语句来确保月份是一个两位数,例如 02 而不是 2。
有没有更好的办法?是的。不要使用 MySQL 时间戳。除了它们占用 36 个字节之外,它们使用起来一点也不方便。对于所有日期/时间值,我建议使用 Julian Date 和 Seconds from 午夜。这些可以组合形成一个 UnixDateTime。如果将其存储在 DWORD(无符号 4 字节整数)中,则可以将一直到 2106 的日期存储为自 epoc 以来的秒数,01/01/1970 DWORD max val = 4,294,967,295 - DWORD 可以保存 136 年的秒数
朱利安日期在进行日期计算时非常好用 UNIXDateTime 值在进行日期/时间计算时很好用有,但我想要一目了然的指示。
用一门好的语言可以很快地转换为 Julian 并返回。使用指针我将它降低到大约 900 个 Clks(这当然也是从 STRING 到 INTEGER 的转换)
当您进入使用日期/时间信息的严肃应用程序(例如金融市场)时,朱利安日期是事实上的。
查询将类似于:
select period_diff(date_format(now(),"%Y%m"),date_format(created,"%Y%m")) from customers where..
给出自在客户记录上创建日期戳以来的日历月数,让 MySQL 在内部进行月份选择。
DROP FUNCTION IF EXISTS `calcula_edad` $$
CREATE DEFINER=`root`@`localhost` FUNCTION `calcula_edad`(pFecha1 date, pFecha2 date, pTipo char(1)) RETURNS int(11)
Begin
Declare vMeses int;
Declare vEdad int;
Set vMeses = period_diff( date_format( pFecha1, '%Y%m' ), date_format( pFecha2, '%Y%m' ) ) ;
/* Si el dia de la fecha1 es menor al dia de fecha2, restar 1 mes */
if day(pFecha1) < day(pFecha2) then
Set vMeses = VMeses - 1;
end if;
if pTipo='A' then
Set vEdad = vMeses div 12 ;
else
Set vEdad = vMeses ;
end if ;
Return vEdad;
End
select calcula_edad(curdate(),born_date,'M') -- for number of months between 2 dates
执行此代码,它将创建一个函数 datedeifference,它将为您提供日期格式 yyyy-mm-dd 的差异。
DELIMITER $$
CREATE FUNCTION datedifference(date1 DATE, date2 DATE) RETURNS DATE
NO SQL
BEGIN
DECLARE dif DATE;
IF DATEDIFF(date1, DATE(CONCAT(YEAR(date1),'-', MONTH(date1), '-', DAY(date2)))) < 0 THEN
SET dif=DATE_FORMAT(
CONCAT(
PERIOD_DIFF(date_format(date1, '%y%m'),date_format(date2, '%y%m'))DIV 12 ,
'-',
PERIOD_DIFF(date_format(date1, '%y%m'),date_format(date2, '%y%m'))% 12 ,
'-',
DATEDIFF(date1, DATE(CONCAT(YEAR(date1),'-', MONTH(DATE_SUB(date1, INTERVAL 1 MONTH)), '-', DAY(date2))))),
'%Y-%m-%d');
ELSEIF DATEDIFF(date1, DATE(CONCAT(YEAR(date1),'-', MONTH(date1), '-', DAY(date2)))) < DAY(LAST_DAY(DATE_SUB(date1, INTERVAL 1 MONTH))) THEN
SET dif=DATE_FORMAT(
CONCAT(
PERIOD_DIFF(date_format(date1, '%y%m'),date_format(date2, '%y%m'))DIV 12 ,
'-',
PERIOD_DIFF(date_format(date1, '%y%m'),date_format(date2, '%y%m'))% 12 ,
'-',
DATEDIFF(date1, DATE(CONCAT(YEAR(date1),'-', MONTH(date1), '-', DAY(date2))))),
'%Y-%m-%d');
ELSE
SET dif=DATE_FORMAT(
CONCAT(
PERIOD_DIFF(date_format(date1, '%y%m'),date_format(date2, '%y%m'))DIV 12 ,
'-',
PERIOD_DIFF(date_format(date1, '%y%m'),date_format(date2, '%y%m'))% 12 ,
'-',
DATEDIFF(date1, DATE(CONCAT(YEAR(date1),'-', MONTH(date1), '-', DAY(date2))))),
'%Y-%m-%d');
END IF;
RETURN dif;
END $$
DELIMITER;
这取决于您希望如何定义月数。回答这个问题:“月份有什么不同:2008 年 2 月 15 日 - 2009 年 3 月 12 日”。它是由明确的 # 天数定义的,这取决于闰年 - 它是什么月份,或者上个月的同一天 = 1 个月。
天数的计算:
2 月 15 日 -> 29 日(闰年)= 2008 年 3 月 1 日 + 365 = 2009 年 3 月 1 日。3 月 1 日 -> 3 月 12 日 = 12 天。14 + 365 + 12 = 391 天。总计 = 391 天 /(每月平均天数 = 30)= 13.03333
月份的计算:
2008 年 2 月 15 日 - 2009 年 2 月 15 日 = 2 月 12 日 -> 3 月 12 日 = 不到 1 个月 总计 = 12 个月,如果 2 月 15 日 - 3 月 12 日被视为“过去一个月”,则为 13
SELECT *
FROM emp_salaryrevise_view
WHERE curr_year Between '2008' AND '2009'
AND MNTH Between '12' AND '1'
我需要精确的月差。尽管 Zane Bien 的解决方案方向正确,但他的第二个和第三个示例给出了不准确的结果。二月的一天除以二月的天数不等于五月的一天除以五月的天数。所以第二个例子应该输出 ((31-5+1)/31 + 13/30 = ) 1.3043 和第三个例子 ((29-27+1)/29 + 2/30 + 3 = ) 3.1701。
我结束了以下查询:
SELECT
'2012-02-27' AS startdate,
'2012-06-02' AS enddate,
TIMESTAMPDIFF(DAY, (SELECT startdate), (SELECT enddate)) AS days,
IF(MONTH((SELECT startdate)) = MONTH((SELECT enddate)), 0, (TIMESTAMPDIFF(DAY, (SELECT startdate), LAST_DAY((SELECT startdate)) + INTERVAL 1 DAY)) / DAY(LAST_DAY((SELECT startdate)))) AS period1,
TIMESTAMPDIFF(MONTH, LAST_DAY((SELECT startdate)) + INTERVAL 1 DAY, LAST_DAY((SELECT enddate))) AS period2,
IF(MONTH((SELECT startdate)) = MONTH((SELECT enddate)), (SELECT days), DAY((SELECT enddate))) / DAY(LAST_DAY((SELECT enddate))) AS period3,
(SELECT period1) + (SELECT period2) + (SELECT period3) AS months
您可以通过这种方式获得年、月和日:
SELECT
username
,date_of_birth
,DATE_FORMAT(CURDATE(), '%Y') - DATE_FORMAT(date_of_birth, '%Y') - (DATE_FORMAT(CURDATE(), '00-%m-%d') < DATE_FORMAT(date_of_birth, '00-%m-%d')) AS years
,PERIOD_DIFF( DATE_FORMAT(CURDATE(), '%Y%m') , DATE_FORMAT(date_of_birth, '%Y%m') ) AS months
,DATEDIFF(CURDATE(),date_of_birth) AS days
FROM users
你也可以试试这个:
select MONTH(NOW())-MONTH(table_date) as 'Total Month Difference' from table_name;
或者
select MONTH(Newer_date)-MONTH(Older_date) as 'Total Month Difference' from table_Name;
给出开始日期为 ins_frm 和结束日期为 ins_to 的简单答案
SELECT convert(TIMESTAMPDIFF(year, ins_frm, ins_to),UNSIGNED) as yrs,
mod(TIMESTAMPDIFF(MONTH, ins_frm, ins_to),12) mnths
FROM table_name
享受 :)))
试试这个
SELECT YEAR(end_date)*12 + MONTH(end_date) - (YEAR(start_date)*12 + MONTH(start_date))
虽然这是一个老话题,但它显示在谷歌的顶部,我没有看到与 Mysql 相关的新问题来计算几个月的差异。我需要一个非常精确的计算,包括月份的分数。
这是为了计算订阅费,例如每月 8 欧元。然后 2 月的 1 天与其他月份相比确实有不同的价格。所以需要计算月份的分数,这里分数的精度是基于秒的。
它的作用是在 @from 和 @to 日期之间计算时将计算分成 3 个部分:
例如从 '2021-09-29 12:00:00' 到 '2021-11-07 00:00:00':
所以结果是1.25个月。
set @from = '2021-09-29 12:00:00';
set @to = '2021-11-07 00:00:00';
select
/* part 1 */ (unix_timestamp(last_day(@from)) + 86400 - unix_timestamp(@from)) / 86400 / day(last_day(@from))
/* part 2 */ + PERIOD_DIFF(EXTRACT(YEAR_MONTH FROM @to), EXTRACT(YEAR_MONTH FROM @from)) - 1 +
/* part 3 */ 1 - (unix_timestamp(last_day(@to)) + 86400 - unix_timestamp(@to)) / 86400 / day(last_day(@to))
month_fraction;
完全相同的计算,但现在基于不使用 mysql 变量的人的字段,并且更容易接管您自己的字段:
select
/* part 1 */ (unix_timestamp(last_day(periodStart)) + 86400 - unix_timestamp(periodStart)) / 86400 / day(last_day(periodStart))
/* part 2 */ + PERIOD_DIFF(EXTRACT(YEAR_MONTH FROM periodTill), EXTRACT(YEAR_MONTH FROM periodStart)) - 1 +
/* part 3 */ 1 - (unix_timestamp(last_day(periodTill)) + 86400 - unix_timestamp(periodTill)) / 86400 / day(last_day(periodTill))
month_fraction
from (select '2021-09-29 12:00:00' periodStart, '2021-11-07 00:00:00' periodTill) period
对于我使用的速度优化unix_timestamp
,它应该执行得很快,因为它能够使用数学计算。以unix_timestamp
秒为单位返回一个数字。86400 是一天中的秒数。
这个查询对我有用:)
SELECT * FROM tbl_purchase_receipt
WHERE purchase_date BETWEEN '2008-09-09' AND '2009-09-09'
它只需要两个日期并检索它们之间的值。