MySQL中日期之间的月份差异


Answers:


16

DATEDIFF功能可以给你的天数两个日期之间。哪个更准确,因为...您如何定义一个月?(28、29、30或31天?)


68
PERIODDIFF可以准确计算月数。
Max Caceres,2009年

10
关于日期,“更准确”是令人难以置信的主观。例如,如果您所在的企业的月度周期需要知道两个日期之间有多少个月,那么比上述天数更重要。一个月多长时间?如果我需要一个月算,我不能简单地除以30
托马斯·潘恩

6
这不是正确的答案;您应该在下面将麦克斯的答案标记为正确。
Graham Charles

1
如果您需要整月数,正确计算accounting年和一个月中不同的天数,Max的答案是不正确的。PERIODDIFF只需采用YYYYMM值,因此,除非您在传递YYYYMM值之前自行计算天数,否则您要计算的只是月数,如果少于整数月,则四舍五入到最接近的月数。请参阅下面的Zane答案。
rgvcorley 2014年

2
为什么对PERIODDIFF评论有这么多赞成?Perioddiff不采用日数值,因此,如果您要计算的月份数少于整数,则需要四舍五入到最接近的月份
rgvcorley14年

218

给定两个日期之间的月份差异:

令我惊讶的是还没有提到:

看看MySQL中的TIMESTAMPDIFF()函数。

这允许您做的是传递两个TIMESTAMP或多个DATETIME值(甚至DATEMySQL会自动转换)以及要基于差值的时间单位。

您可以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)中变化的天数,并考虑到leap年-您不必担心这些事情。


月精度差异:

如果要在经过的月数中引入十进制精度,则要稍微复杂一点,但是可以通过以下方法实现:

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

8
这是正确的答案,PERIODDIFF对所选答案的评论的所有赞誉都非常令人
误解

5
谢谢,在我的情况下,我需要包含日期,因此我将所有“ enddate”替换为“ date_add(enddate,interval 1 day)”。然后2014年3
Sven Tore 2014年

2
非常感谢.....特别thanx的“精确度月差异:”。您是mysql的明星
Vidhi

1
同意 这是正确的答案。正是我想要的!谢谢。
乔恩·沃特

104

PERIOD_DIFF计算两个日期之间的月份。

例如,要计算now()与your_table中的time列之间的差:

select period_diff(date_format(now(), '%Y%m'), date_format(time, '%Y%m')) as months from your_table;

1
知道在11个月零1天(即12个月)时会做什么,但是如果更改为30个月,那么它仍然是11天?
Darryl Hein

1
上面的示例将11个月零1天返回11个月。PERIOD_DIFF对30天与31天的月份没有任何意义。
Max Caceres,2009年

26

我也使用PERIODDIFF。要获取日期的年和月,我使用函数 EXTRACT

  SELECT PERIOD_DIFF(EXTRACT(YEAR_MONTH FROM NOW()), EXTRACT(YEAR_MONTH FROM time)) AS months FROM your_table;

2
难以置信!该方法使用的时间减少了50%DATE_FORMAT,谢谢!+1
大卫·罗德里格斯

1
在这里获取unix时间戳 SELECT PERIOD_DIFF(EXTRACT(YEAR_MONTH FROM NOW()), EXTRACT(YEAR_MONTH FROM FROM_UNIXTIME(time, "%Y%m%d"))) AS months FROM your_table;
Smolla'3

2
这是不正确的,PERIOD_DIFF不采用日值,因此,如果少于整数月份,则需要计算四舍五入到最接近月份的月份数。您应该编辑答案以使其清楚。
rgvcorley14年

12

正如这里的许多答案所示,“正确”的答案完全取决于您的需求。就我而言,我需要四舍五入到最接近的整数

考虑以下示例: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 TIMESTAMPDIFF(MONTH,'2018-01-31','2018-02-04'); 间隔0个月... SELECT TIMESTAMPDIFF(MONTH,'2018-01-31','2018-03-01'); 给您1 ...如果5天为0,29天为1?
斯科特,

@Scott,一月和三月之间有一个整月(称为二月)。TIMESTAMPDIFF仅给出两个日期之间的完整月份数。如果您希望任何一个月的期间等于一个月,请使用我提供的四舍五入方法。
IanS

8

从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。


8

我更喜欢这种方式,因为evryone乍一看会很清楚:

SELECT
    12 * (YEAR(to) - YEAR(from)) + (MONTH(to) - MONTH(from)) AS months
FROM
    tab;

斯坦尼斯拉夫(Stanislav)-如果您想按月数了解数据,则此方法完全正确。我有一些报告按月显示销售情况(1月/ 2月/ 3月等),但是有太多数据(按季度/年/过去年等显示图表),因此我无法按任何类别进行分组。我需要提取原始数据并遍历它-当它是1/31时,许多计算将“下个月”放到3月,而我们人类知道是2月。如果它是2/4,则有一些计算会说1/31是“本月”,而1/28是“上个月”
Scott

6

有没有更好的办法?是。不要使用MySQL时间戳。除了占用36个字节的事实外,使用它们根本不方便。我建议从午夜开始使用Julian日期和秒作为所有日期/时间值。这些可以组合在一起形成UnixDateTime。如果将其存储在DWORD(无符号4字节整数)中,则自epoc(自01/01/1970)以来,直到2106为止的日期都可以存储为秒。DWORD max val = 4,294,967,295-DWORD可以保留136年的秒

在进行日期计算时,Julian Dates非常适合使用UNIXDateTime值在进行日期/时间计算时非常适合使用,两者都不好看,因此我在需要一列但不会进行大量计算的列时使用Timestamps与,但我想一目了然。

可以使用一种良好的语言很快地转换为Julian并返回。使用指针,我将其降低到大约900个Clks(当然,这也是从STRING到INTEGER的转换)

当您进入使用日期/时间信息的严肃应用程序(例如金融市场)时,朱利安日期实际上就是事实。


您从哪里获得有关它们占用36个字节的事实的信息?MySQL手册规定TIMESTAMP列的存储要求为4个字节dev.mysql.com/doc/refman/5.1/en/storage-requirements.html
poncha

立即开始计划Y2106错误!;-)
ErichBSchulz

5

查询将类似于:

select period_diff(date_format(now(),"%Y%m"),date_format(created,"%Y%m")) from customers where..

给出自客户记录上创建日期戳以来的多个日历月,让MySQL在内部进行月份选择。


2
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

2

执行此代码,它将创建一个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;

1
您是否认为可以清理一下代码并对其进行注释,以便使其更容易理解?谢谢
Darryl Hein

1

这取决于您希望如何定义月数。回答以下问题:“月份之间有何不同:2008年2月15日至2009年3月12日”。它是由明确的天数(取决于leap年)定义的,即leap年是哪个月,或者上个月的同一天= 1个月。

天数的计算

2月15日-> 29(le年)= 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月15日-> 3月12日=少于1个月总计= 12个月,如果2月15日-3月12日被视为“过去一个月”,则为13个月



1

我需要精确的月差。尽管赞恩·比恩(Zane Bien)的解决方案朝着正确的方向发展,但他的第二个和第三个示例给出的结果并不准确。2月的天数除以2月的天数等于5月的天数除以5月的天数。因此,第二个示例应输出((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

1

PERIOD_DIFF()函数

一种方式是MySQL PERIOD_DIFF()返回两个周期之间的差。期间应采用相同的格式,即YYYYMM或YYMM。要注意的是,周期不是日期值。

码:

SELECT PERIOD_DIFF(200905,200811);

在此处输入图片说明


0

这样可以得到几年,几个月和几天:

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

0

您也可以尝试以下操作:

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;

0

给定简单的答案,开始日期为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

请享用 :)))


0

试试这个

SELECT YEAR(end_date)*12 + MONTH(end_date) - (YEAR(start_date)*12 + MONTH(start_date))

欢迎使用Stack Overflow!确保检查问题的日期以及现有答案是否具有相同的解决方案。
lehiester19年

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.