简体   繁体   English

MySQL中日期之间的月差

[英]The difference in months between dates in MySQL

I'm looking to calculate the number of months between 2 date time fields.我正在寻找计算 2 个日期时间字段之间的月数。

Is there a better way than getting the unix timestamp and the dividing by 2 592 000 (seconds) and rounding up whithin MySQL?有没有比获取 unix 时间戳和除以 2 592 000(秒)并在 MySQL 中四舍五入更好的方法?

Month-difference between any given two dates:任何给定两个日期之间的月差:

I'm surprised this hasn't been mentioned yet:我很惊讶这还没有被提及:

Have a look at the TIMESTAMPDIFF() function in MySQL.看看 MySQL 中的TIMESTAMPDIFF()函数。

What this allows you to do is pass in two TIMESTAMP or DATETIME values (or even DATE as MySQL will auto-convert) as well as the unit of time you want to base your difference on.这允许您做的是传入两个TIMESTAMPDATETIME值(甚至是DATE因为 MySQL 将自动转换)以及您希望基于差异的时间单位。

You can specify MONTH as the unit in the first parameter:您可以在第一个参数中指定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

It basically gets the number of months elapsed from the first date in the parameter list.它基本上获取从参数列表中的第一个日期开始经过的月数。 This solution automatically compensates for the varying amount of days in each month (28,30,31) as well as taking into account leap years — you don't have to worry about any of that stuff.此解决方案会自动补偿每个月 (28,30,31) 中不同的天数,并考虑闰年——您不必担心任何这些事情。


Month-difference with precision:精确的月差:

It's a little more complicated if you want to introduce decimal precision in the number of months elapsed, but here is how you can do it:如果您想在经过的月数中引入十进制精度,这会稍微复杂一些,但您可以这样做:

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
  )

Where startdate and enddate are your date parameters, whether it be from two date columns in a table or as input parameters from a script:其中startdateenddate是您的日期参数,无论是来自表中的两个日期列还是作为脚本的输入参数:

Examples:例子:

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 calculates months between two dates. PERIOD_DIFF计算两个日期之间的月份。

For example, to calculate the difference between now() and a time column in your_table:例如,要计算 now() 和 your_table 中的时间列之间的差异:

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

I use also PERIOD_DIFF .我也使用PERIOD_DIFF To get the year and the month of the date, I use the function EXTRACT :要获取日期的年份和月份,我使用函数EXTRACT

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

The DATEDIFF function can give you the number of days between two dates. DATEDIFF函数可以为您提供两个日期之间的天数。 Which is more accurate, since... how do you define a month?哪个更准确,因为...你如何定义一个月? (28, 29, 30, or 31 days?) (28、29、30 或 31 天?)

As many of the answers here show, the 'right' answer depends on exactly what you need.正如这里的许多答案所显示的那样,“正确”的答案完全取决于您的需求。 In my case, I need to round to the closest whole number .就我而言,我需要四舍五入到最接近的整数

Consider these examples: 1st January -> 31st January: It's 0 whole months, and almost 1 month long.考虑以下示例: 1 月 1 日 -> 1 月 31 日:整整 0 个月,几乎有 1 个月之久。 1st January -> 1st February? 1 月 1 日 - > 2 月 1 日? It's 1 whole month, and exactly 1 month long.整整 1 个月,正好是 1 个月。

To get the number of whole (complete) months, use:要获得完整(完整)月数,请使用:

SELECT TIMESTAMPDIFF(MONTH, '2018-01-01', '2018-01-31');  => 0
SELECT TIMESTAMPDIFF(MONTH, '2018-01-01', '2018-02-01');  => 1

To get a rounded duration in months, you could use:要获得以月为单位的舍入持续时间,您可以使用:

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

This is accurate to +/- 5 days and for ranges over 1000 years.这精确到 +/- 5 天,范围超过 1000 年。 Zane's answer is obviously more accurate, but it's too verbose for my liking.赞恩的回答显然更准确,但我不喜欢它太冗长。

I prefer this way, because evryone will understand it clearly at the first glance:我更喜欢这种方式,因为每个人第一眼就会明白:

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

From the MySQL manual:从 MySQL 手册:

PERIOD_DIFF(P1,P2) PERIOD_DIFF(P1,P2)

Returns the number of months between periods P1 and P2.返回期间 P1 和 P2 之间的月数。 P1 and P2 should be in the format YYMM or YYYYMM. P1 和 P2 的格式应为 YYMM 或 YYYYMM。 Note that the period arguments P1 and P2 are not date values.请注意,期间参数 P1 和 P2 不是日期值。

mysql> SELECT PERIOD_DIFF(200802,200703); mysql> SELECT PERIOD_DIFF(200802,200703); -> 11 -> 11

So it may be possible to do something like this:所以有可能做这样的事情:

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;

Where d1 and d2 are the date expressions.其中 d1 和 d2 是日期表达式。

I had to use the if() statements to make sure that the months was a two digit number like 02 rather than 2.我必须使用 if() 语句来确保月份是两位数,例如 02 而不是 2。

Is there a better way?有没有更好的办法? yes.是的。 Do not use MySQL Timestamps.不要使用 MySQL 时间戳。 Apart from the fact that they occupy 36 Bytes, they are not at all convenient to work with.除了它们占用 36 字节这一事实之外,使用它们一点也不方便。 I would reccomend using Julian Date and Seconds from midnight for all date/time values.对于所有日期/时间值,我建议从午夜开始使用 Julian Date 和 Seconds。 These can be combined to form a UnixDateTime.这些可以组合起来形成一个 UnixDateTime。 If this is stored in a DWORD (unsigned 4 Byte Integer) then dates all the way up to 2106 can be stored as seconds since epoc, 01/01/1970 DWORD max val = 4,294,967,295 - A DWORD can hold 136 years of Seconds如果将其存储在 DWORD(无符号 4 字节整数)中,则可以将一直到 2106 的日期存储为自 epoc 以来的秒数,01/01/1970 DWORD max val = 4,294,967,295 - DWORD 可以保存 136 年的秒数

Julian Dates are very nice to work with when making date calculations UNIXDateTime values are good to work with when making Date/Time calculations Neither are good to look at, so I use the Timestamps when I need a column that I will not be doing much calculation with, but I want an at-a-glance indication. Julian 日期在进行日期计算时非常适合使用 UNIXDateTime 值在进行日期/时间计算时很适合使用 两者都不好看,所以当我需要一个我不会做太多计算的列时,我使用时间戳与,但我想要一目了然的指示。

Converting to Julian and back can be done very quickly in a good language.转换为 Julian 并返回可以用一门好的语言非常快速地完成。 Using pointers I have it down to about 900 Clks (This is also a conversion from a STRING to an INTEGER of course)使用指针,我将其降低到大约 900 个时钟(当然,这也是从 STRING 到 INTEGER 的转换)

When you get into serious applications that use Date/Time information like for example the financial markets, Julian dates are de-facto.当您进入使用日期/时间信息的严肃应用程序(例如金融市场)时,儒略日期是事实上的。

The Query will be like:查询将类似于:

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

Gives a number of calendar months since the created datestamp on a customer record, letting MySQL do the month selection internally.给出自在客户记录上创建日期戳以来的多个日历月,让 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

Execute this code and it will create a function datedeifference which will give you the difference in date format yyyy-mm-dd.执行此代码,它将创建一个函数 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;

This depends on how you want the # of months to be defined.这取决于您希望如何定义月份数。 Answer this questions: 'What is difference in months: Feb 15, 2008 - Mar 12, 2009'.回答以下问题:“月份有什么不同:2008 年 2 月 15 日 - 2009 年 3 月 12 日”。 Is it defined by clear cut # of days which depends on leap years- what month it is, or same day of previous month = 1 month.它是否由明确的天数定义,这取决于闰年 - 它是什么月份,或上个月的同一天 = 1 个月。

A calculation for Days :天的计算

Feb 15 -> 29 (leap year) = 14 Mar 1, 2008 + 365 = Mar 1, 2009. Mar 1 -> Mar 12 = 12 days. Feb 15 -> 29 (leap year) = 14 Mar 1, 2008 + 365 = Mar 1, 2009. Mar 1 -> Mar 12 = 12 天。 14 + 365 + 12 = 391 days. 14 + 365 + 12 = 391 天。 Total = 391 days / (avg days in month = 30) = 13.03333总计 = 391 天 /(每月平均天数 = 30)= 13.03333

A calculation of months :月份的计算

Feb 15 2008 - Feb 15 2009 = 12 Feb 15 -> Mar 12 = less than 1 month Total = 12 months, or 13 if feb 15 - mar 12 is considered 'the past month' 2008 年 2 月 15 日 - 2009 年 2 月 15 日 = 15 年 2 月 12 日 -> 3 月 12 日 = 不到 1 个月总计 = 12 个月,如果 2 月 15 日 - 3 月 12 日被视为“过去一个月”,则为 13

I needed month-difference with precision.我需要精确的月差。 Although Zane Bien's solution is in the right direction, his second and third examples give inaccurate results.尽管 Zane Bien 的解决方案方向正确,但他的第二个和第三个示例给出了不准确的结果。 A day in February divided by the number of days in February is not equal to a day in May divided by the number of days in May.二月的一天除以二月的天数不等于五月的一天除以五月的天数。 So the second example should output ((31-5+1)/31 + 13/30 = ) 1.3043 and the third example ((29-27+1)/29 + 2/30 + 3 = ) 3.1701.所以第二个例子应该输出 ((31-5+1)/31 + 13/30 = ) 1.3043 和第三个例子 ((29-27+1)/29 + 2/30 + 3 = ) 3.1701。

I ended up with the following query:我最终得到了以下查询:

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 * 
FROM emp_salaryrevise_view 
WHERE curr_year Between '2008' AND '2009' 
    AND MNTH Between '12' AND '1'

PERIOD_DIFF() function PERIOD_DIFF() 函数

One of the way is MySQL PERIOD_DIFF() returns the difference between two periods.一种方法是 MySQL PERIOD_DIFF() 返回两个时期之间的差异。 Periods should be in the same format ie YYYYMM or YYMM.句点应采用相同的格式,即 YYYYMM 或 YYMM。 It is to be noted that periods are not date values.需要注意的是,期间不是日期值。

Code:代码:

SELECT PERIOD_DIFF(200905,200811);

在此处输入图片说明

You can get years, months and days this way:您可以通过这种方式获得年、月和日:

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

You can also try this:你也可以试试这个:

select MONTH(NOW())-MONTH(table_date) as 'Total Month Difference' from table_name;

OR或者

select MONTH(Newer_date)-MONTH(Older_date) as 'Total Month Difference' from table_Name;

Simple answer given start date as ins_frm and end date as ins_to给出开始日期为 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

Enjoy :)))享受 :)))

试试这个

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

Although it's an old topic it shows on top in google and I don't see newer questions related to Mysql to calculate the difference in months.虽然这是一个老话题,但它显示在谷歌的顶部,我没有看到与 Mysql 相关的新问题来计算月数的差异。 And I needed a very precise calculation including the fraction of the month.我需要一个非常精确的计算,包括一个月的分数。

This for the purpose to calculate a subscription fee eg 8 euro per month.这是为了计算订阅费,例如每月 8 欧元。 Then 1 day in februari does have a different price compared to other months.然后,与其他月份相比,2 月的 1 天确实有不同的价格。 So the fraction of months needs to be calculated and here the precision of the fraction is based on seconds.所以需要计算月份的分数,这里分数的精度基于秒。

What it does is to split the calculation into 3 parts when calculation between @from and @to dates:它的作用是在@from 和@to 日期之间进行计算时将计算拆分为 3 部分:

  1. fraction of the calendar month between @from and the end of the @from calendar month @from 和 @from 日历月结束之间的日历月的一部分
  2. number of whole calendar months between @from and @to @from 和 @to 之间的整个日历月数
  3. fraction of the calendar month between start of the calendar month and @to日历月开始和@to 之间的日历月的一部分

Eg from '2021-09-29 12:00:00' to '2021-11-07 00:00:00':例如从“2021-09-29 12:00:00”到“2021-11-07 00:00:00”:

  1. The 1.5 days at the end of september 2021. September does have 30 days so the fraction is 0.05 month (1.5/30). 2021 年 9 月结束时的 1.5 天。9 月确实有 30 天,所以分数是 0.05 个月 (1.5/30)。
  2. the whole month oktober 2021 so 1 full month 2021 年 10 月的整个月,所以 1 个月
  3. The 6 full days at the begin of november 2021. November does have 30 days so the faction is 0.2 month (6/30). 2021 年 11 月开始的 6 天。11 月确实有 30 天,所以派系是 0.2 个月(6/30)。

So the outcome is 1.25 month.所以结果是 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;

Exactly the same calculation but now based on fields for people not using mysql variables and easier to take over your own fields:完全相同的计算,但现在基于不使用 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

For speed optimization I've used unix_timestamp which should perform fast as it is able to use mathematic calculation.对于速度优化,我使用了unix_timestamp ,它应该执行得很快,因为它能够使用数学计算。 The unix_timestamp returns a number in seconds. unix_timestamp以秒为单位返回一个数字。 The 86400 is the number of seconds in a day. 86400 是一天中的秒数。

This query worked for me:)这个查询对我有用:)

SELECT * FROM tbl_purchase_receipt
WHERE purchase_date BETWEEN '2008-09-09' AND '2009-09-09'

It simply take two dates and retrieves the values between them.它只需要两个日期并检索它们之间的值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM