La diferencia en meses entre fechas en MySQL

85

Estoy buscando calcular el número de meses entre 2 campos de fecha y hora.

¿Hay una mejor manera que obtener la marca de tiempo de Unix y dividir por 2 592 000 (segundos) y redondear dentro de MySQL?

Darryl Hein
fuente

Respuestas:

16

La función DATEDIFF puede darle el número de días entre dos fechas. ¿Cuál es más acertado, ya que ... cómo se define un mes? (¿28, 29, 30 o 31 días?)

Plataforma improvisada
fuente
68
PERIODDIFF puede calcular el número de meses con precisión.
Max Caceres
10
"Más preciso" es increíblemente subjetivo cuando se trata de fechas. Si, por ejemplo, está en una empresa que tiene ciclos mensuales, la necesidad de saber cuántos meses hay entre dos fechas puede ser más importante que la cantidad de días por la misma razón que expuso anteriormente. ¿Cuánto dura un mes? Si necesito contar un mes, no puedo simplemente dividir entre 30.
Thomas Paine
6
Ésta no es la respuesta correcta; debe marcar la respuesta de Max como correcta, a continuación.
Graham Charles
1
La respuesta de Max no es correcta si desea el número de meses completos, teniendo en cuenta correctamente los años bisiestos y el número diferente de días en un mes. PERIODDIFFsimplemente toma un valor YYYYMM, por lo que a menos que usted mismo tenga en cuenta los días antes de pasar los valores YYYYMM, todo lo que está calculando es el número de meses, redondeado al mes más cercano si hay menos de un número entero de meses. Vea la respuesta de Zane a continuación.
rgvcorley
2
¿Por qué hay tantos votos a favor del PERIODDIFFcomentario? Perioddiff no toma valores de día, por lo que está calculando el número de meses redondeado al mes más cercano si hay menos de un número entero de meses
rgvcorley
218

Diferencia de mes entre dos fechas determinadas:

Me sorprende que esto no se haya mencionado todavía:

Eche un vistazo al TIMESTAMPDIFF () en MySQL.

Lo que esto le permite hacer es pasar en dos TIMESTAMPoDATETIME valores (o incluso DATEcuando MySQL se convertirá automáticamente), así como la unidad de tiempo en la que desea basar su diferencia.

Puede especificar MONTHcomo unidad en el primer parámetro:

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

Básicamente, obtiene el número de meses transcurridos desde la primera fecha en la lista de parámetros. Esta solución compensa automáticamente la cantidad variable de días en cada mes (28, 30, 31) y también tiene en cuenta los años bisiestos; no tiene que preocuparse por nada de eso.


Diferencia de mes con precisión:

Es un poco más complicado si desea introducir precisión decimal en el número de meses transcurridos, pero así es como puede hacerlo:

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
  )

Donde startdateyenddate están sus parámetros de fecha, ya sea de dos columnas de fecha en una tabla o como parámetros de entrada de un script:

Ejemplos:

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
Zane Bien
fuente
8
Esta es la respuesta correcta, todos los votos a favor del PERIODDIFFcomentario sobre la respuesta seleccionada son muy importantes
rgvcorley
5
Gracias, en mi caso necesitaba fechas inclusivas, así que reemplazo todo "enddate" con "date_add (enddate, interval 1 day)". Luego, 2014-03-01 a 2014-05-31 dará 3.00 en lugar de 2.97
Sven Tore
2
Muchas gracias ... agradecimiento especial por "Diferencia de mes con precisión:". Eres la estrella de mysql
Vidhi
1
Convenido. Esta es la respuesta correcta. ¡Exactamente lo que estaba buscando! Gracias.
Jon Vote el
104

PERIOD_DIFF calcula los meses entre dos fechas.

Por ejemplo, para calcular la diferencia entre now () y una columna de tiempo en your_table:

select period_diff(date_format(now(), '%Y%m'), date_format(time, '%Y%m')) as months from your_table;
Max Cáceres
fuente
1
¿Alguna idea de lo que hace cuando podrían ser 11 meses y 1 día, es decir, 12 meses, pero si cambia a meses de 30 días, todavía serían 11?
Darryl Hein
1
11 meses y 1 día devolverían 11 meses con el ejemplo anterior. PERIOD_DIFF no tiene sentido de meses de 30 frente a 31 días.
Max Caceres
26

También utilizo PERIODDIFF . Para obtener el año y el mes de la fecha, uso la función EXTRACCIÓN :

  SELECT PERIOD_DIFF(EXTRACT(YEAR_MONTH FROM NOW()), EXTRACT(YEAR_MONTH FROM time)) AS months FROM your_table;
Smolla
fuente
2
¡Increíble! Utiliza un 50% menos de tiempo que el DATE_FORMATmétodo, ¡gracias! +1
David Rodrigues
1
Aquí para una marca de tiempo de 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
2
Esto es incorrecto, PERIOD_DIFFno toma valores de día, por lo que está calculando el número de meses redondeado al mes más cercano si hay menos de un número entero de meses. Debes editar tu respuesta para que quede claro.
rgvcorley
12

Como muestran muchas de las respuestas aquí, la respuesta "correcta" depende exactamente de lo que necesita. En mi caso, necesito redondear al número entero más cercano .

Considere estos ejemplos: 1 de enero -> 31 de enero: son 0 meses completos y casi 1 mes. 1 de enero -> 1 de febrero? Es 1 mes completo y exactamente 1 mes.

Para obtener la cantidad de meses enteros (completos) , use:

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

Para obtener una duración redondeada en meses, puede usar:

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

Esto tiene una precisión de +/- 5 días y para rangos de más de 1000 años. La respuesta de Zane es obviamente más precisa, pero es demasiado detallada para mi gusto.

IanS
fuente
No estoy de acuerdo ... SELECT TIMESTAMPDIFF (MONTH, '2018-01-31', '2018-02-04'); le da 0 meses de diferencia ... SELECT TIMESTAMPDIFF (MONTH, '2018-01-31', '2018-03-01'); le da 1 ... si 5 días es 0, 29 días es 1?
Scott
@Scott, hay un mes completo (llamado febrero) entre enero y marzo. TIMESTAMPDIFF solo da el número de meses completos entre las fechas. Si desea que cualquier período de un mes sea igual a un mes, use la versión redondeada que le he dado.
IanS
8

Del manual de MySQL:

PERIOD_DIFF (P1, P2)

Devuelve el número de meses entre los períodos P1 y P2. P1 y P2 deben tener el formato YYMM o YYYYMM. Tenga en cuenta que los argumentos de período P1 y P2 no son valores de fecha.

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

Entonces, puede ser posible hacer algo como esto:

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;

Donde d1 y d2 son las expresiones de fecha.

Tuve que usar las declaraciones if () para asegurarme de que los meses fueran un número de dos dígitos como 02 en lugar de 2.

Vincent Ramdhanie
fuente
8

Prefiero esta forma, porque todos lo entenderán claramente a primera vista:

SELECT
    12 * (YEAR(to) - YEAR(from)) + (MONTH(to) - MONTH(from)) AS months
FROM
    tab;
Stanislav Basovník
fuente
Stanislav: este método es exactamente correcto si desea conocer los datos POR NÚMERO DE MES. Tengo algunos informes que muestran las ventas por mes (enero / febrero / marzo, etc.) pero hay tantos datos (gráficos por trimestre / año / años anteriores, etc.) que no puedo agrupar por nada. Necesito extraer los datos sin procesar y recorrerlos: cuando es 1/31, muchos de los cálculos ponen "el próximo mes" en marzo, cuando los humanos sabemos que es febrero. SI es 2/4, algunos cálculos dicen que 1/31 es "este mes" pero 1/28 fue "el mes pasado"
Scott
6

¿Existe una forma mejor? si. No use marcas de tiempo de MySQL. Aparte del hecho de que ocupan 36 Bytes, no es nada cómodo trabajar con ellos. Recomendaría usar la fecha juliana y los segundos desde la medianoche para todos los valores de fecha / hora. Estos se pueden combinar para formar un UnixDateTime. Si esto se almacena en un DWORD (entero de 4 bytes sin signo), las fechas hasta 2106 se pueden almacenar como segundos desde epoc, 01/01/1970 DWORD max val = 4,294,967,295 - Un DWORD puede contener 136 años de segundos

Es muy agradable trabajar con las fechas julianas cuando se realizan cálculos de fecha. Los valores de UNIXDateTime son buenos para trabajar cuando se realizan cálculos de fecha / hora. Ninguno de los dos es bueno para mirar, así que utilizo las marcas de tiempo cuando necesito una columna en la que no haré muchos cálculos con, pero quiero una indicación de un vistazo.

La conversión a Julian y viceversa se puede hacer muy rápidamente en un buen idioma. Usando punteros, lo reduzco a aproximadamente 900 Clks (esto también es una conversión de STRING a INTEGER, por supuesto)

Cuando ingresa a aplicaciones serias que usan información de fecha / hora como, por ejemplo, los mercados financieros, las fechas julianas son de facto.

Mike comerciante
fuente
¿De dónde sacaste la información sobre el hecho de que ocupan 36 bytes ? El manual de MySQL indica que el requisito de almacenamiento para la columna TIMESTAMP es de 4 bytes . dev.mysql.com/doc/refman/5.1/en/storage-requirements.html
poncha
¡Empiece a planificar el error Y2106 ahora! ;-)
ErichBSchulz
5

La consulta será como:

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

Da un número de meses calendario desde la fecha de creación en el registro de un cliente, lo que permite que MySQL haga la selección del mes internamente.

Shubham Verma
fuente
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
Rama
fuente
2

Ejecute este código y creará una función datedeifference que le dará la diferencia en el formato de fecha aaaa-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;
enorme
fuente
1
¿Crees que podrías limpiar y comentar un poco tu código para que sea un poco más comprensible? Gracias
Darryl Hein
1

Esto depende de cómo desee que se defina el número de meses. Responda estas preguntas: '¿Cuál es la diferencia en meses: 15 de febrero de 2008 - 12 de marzo de 2009?'. ¿Está definido por un número claro de días que depende de los años bisiestos, qué mes es, o el mismo día del mes anterior = 1 mes?

Un cálculo de días :

15 de febrero -> 29 (año bisiesto) = 14 1 de marzo de 2008 + 365 = 1 de marzo de 2009. 1 de marzo -> 12 de marzo = 12 días. 14 + 365 + 12 = 391 días. Total = 391 días / (promedio de días en el mes = 30) = 13.03333

Un cálculo de meses :

15 de febrero de 2008 - 15 de febrero de 2009 = 12 15 de febrero -> 12 de marzo = menos de 1 mes Total = 12 meses, o 13 si del 15 de febrero al 12 de marzo se considera 'el último mes'

Klathzazt
fuente
1
SELECT * 
FROM emp_salaryrevise_view 
WHERE curr_year Between '2008' AND '2009' 
    AND MNTH Between '12' AND '1'
Darryl Hein
fuente
1

Necesitaba un mes de diferencia con precisión. Aunque la solución de Zane Bien va en la dirección correcta, su segundo y tercer ejemplo dan resultados inexactos. Un día de febrero dividido por el número de días de febrero no es igual a un día de mayo dividido por el número de días de mayo. Entonces, el segundo ejemplo debería dar como resultado ((31-5 + 1) / 31 + 13/30 =) 1.3043 y el tercer ejemplo ((29-27 + 1) / 29 + 2/30 + 3 =) 3.1701.

Terminé con la siguiente consulta:

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
Michel
fuente
1

Función PERIOD_DIFF ()

Una de las formas es MySQL PERIOD_DIFF () devuelve la diferencia entre dos períodos. Los períodos deben tener el mismo formato, es decir, AAAAMM o AAMM. Cabe señalar que los períodos no son valores de fecha.

Código:

SELECT PERIOD_DIFF(200905,200811);

ingrese la descripción de la imagen aquí

Amitesh Bharti
fuente
0

Puede obtener años, meses y días de esta manera:

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
Artur Kędzior
fuente
0

También puedes probar esto:

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

O

select MONTH(Newer_date)-MONTH(Older_date) as 'Total Month Difference' from table_Name;
Shubham Verma
fuente
0

Respuesta simple dada la fecha de inicio como ins_frm y la fecha de finalización como 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

Disfruta :)))

Mo Kassab
fuente
0

Prueba esto

SELECT YEAR(end_date)*12 + MONTH(end_date) - (YEAR(start_date)*12 + MONTH(start_date))
Maceo
fuente
¡Bienvenido a Stack Overflow! Asegúrese de verificar la fecha de las preguntas y si las respuestas existentes tienen la misma solución.
lehiester
-1

Esta consulta funcionó para mí :)

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

Simplemente toma dos fechas y recupera los valores entre ellas.

Khaleel Rshid
fuente
respuesta totalmente incorrecta, quiere contar los meses
Shane Rowatt