Ir al contenido


Foto

Obtener Dias, Meses y Años a partir de una cantidad de días


  • Por favor identifícate para responder
14 respuestas en este tema

#1 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.419 mensajes
  • LocationRepública Dominicana

Escrito 14 julio 2016 - 02:26

Amigos tengo el siguiente problema, mi sistema debe aportar información en días entre dos fechas, eso se obtiene desde la base de datos sin problemas, ahora necesitan pasar esa cantidad de dias a 1 año 2 meses y 4 dias por dar un ejemplo, si fuera entre dos fechas no tendría problemas con eso porque ya de por sí exiten funciones para ello, ¿alguien me aportar alguna idea para lograr eso?.

 

Saludos.


  • 0

#2 santiago14

santiago14

    Advanced Member

  • Miembros
  • PipPipPip
  • 334 mensajes
  • LocationCerrillos - Salta - Argentina

Escrito 14 julio 2016 - 02:31

A ver esto:


delphi
  1. {-------------------------------------------------------------------------------
  2.   Procedure: desagregarEdad
  3.   Author: Santiago14 (Santiago Russo).
  4.   DateTime: 09.11.2012 18:20:49
  5.   Arguments: dias:integer
  6.   Result: string
  7.   Comentario: Devuelve la edad en días; días y meses; en años y meses según corresponda
  8. -------------------------------------------------------------------------------}
  9. function desagregarEdad(dias:integer):string;
  10. var
  11. meses, dias1:integer;
  12. begin
  13. meses:=dias div 30;
  14. dias1:= dias mod 30;
  15. case meses of
  16. 0:
  17. begin
  18. result:=intToStr(dias) + ' Días';
  19. end;
  20. 1..24:
  21. begin
  22. result:=inTtoStr(meses) + ' Meses y ' + intToStr(dias1) + ' Días';
  23. end;
  24. else
  25. begin
  26. result:=intToStr(meses div 12) + ' Años y ' + inttostr(meses mod 12) + ' Meses';
  27. end;
  28. end;
  29. end;

Veamos si sirve...


  • 2

#3 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.419 mensajes
  • LocationRepública Dominicana

Escrito 14 julio 2016 - 02:55

Excelente Santiago, me ha funcionado perfectamente, muchas gracias..


  • 0

#4 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 14 julio 2016 - 05:15

El problema es que no todos los meses tienen 30 días ni todos los años 365 días. Si queremos un cálculo exacto como si mirásemos el cálculo en un calendario, hay que tener en cuenta años bisiestos y días por mes.

 

Para ello propongo esta función por la "cuenta de la vieja"


delphi
  1. function RestaFechas(D1, D2:TDate): String;
  2. var
  3.   i: integer;
  4.   dia0, mes0, ano0: WORD;
  5.   dia, mes, ano: WORD;
  6.   dias, meses, anos: WORD;
  7.   D: integer;
  8. begin
  9.   D:= Ceil(D2)-Ceil(D1);
  10.   dias:= 0;
  11.   meses:= 0;
  12.   anos:= 0;
  13.  
  14.   DecodeDate(D1, ano0, mes0, dia0);
  15.   DecodeDate(D1, ano, mes, dia);
  16.   for i:= 0 to D-1 do
  17.   begin
  18.    D1:= D1+1;
  19.    inc(dias);
  20.    DecodeDate(D1, ano, mes, dia);
  21.    if dia = dia0 then
  22.    begin
  23.      inc(meses);
  24.      dias:= 0;
  25.      if mes = mes0 then
  26.      begin
  27.        meses:= 0;
  28.        inc(anos);
  29.      end;
  30.    end;
  31.   end;
  32.   Result:= IntToStr(anos) + ' años, ' +  IntToStr(meses) + ' meses, y ' + IntToStr(dias) + ' dias';
  33. end;

Uso:


delphi
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. begin
  3. Label1.Caption:= RestaFechas(DateTimePicker1.DateTime, DateTimePicker2.DateTime);
  4. end;

Esta seria la diferencia de cálculo entre las dos funciones propuestas:

 

Archivo adjunto  fechas.jpg   20,83KB   3 descargas

 

 

 

Saludos.


  • 3

#5 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.419 mensajes
  • LocationRepública Dominicana

Escrito 15 julio 2016 - 07:35

Excelente mi estimado, ya lo tengo en mi repositorio..


  • 0

#6 sir.dev.a.lot

sir.dev.a.lot

    Advanced Member

  • Miembros
  • PipPipPip
  • 545 mensajes
  • Location127.0.0.1

Escrito 16 julio 2016 - 03:19


delphi
  1. Program Obsolete;

 

function CalculateLapseDate(FechaI, FechaF : TDateTime) : String;
var
AAI, AAF, MMI, MMF, DDI, DDF: Word;
AA, MM, DD: Integer;
begin
DecodeDate(FechaI, AAI, MMI, DDI);
DecodeDate(FechaF, AAF, MMF, DDF);

AA := AAF - AAI;

if( MMI <= MMF ) then
MM := MMF - MMI
else
begin
MM := MMF + 12 - MMI;
AA := AA - 1;
end;

if( DDI <= DDF ) then
DD := DDF - DDI
else
begin
DD := DDF + MonthDays[IsLeapYear(AAF),MMF] - DDI;
MM := MM - 1;
end;

if MM < 0 then
begin
AA := AA - 1;
MM := 12 + MM;
end;

Mes:= MM;

If AA > 0 then
Result := Format('%.0d Años %.0d Meses y %.0d Dias', [AA, MM, DD])
else
Result := Format('%.0d Meses y %.0d Dias', [MM, DD])
end;

 

Solo un aporte mas, Aun que ya la version de @Escafandra, esta 100% Completa.

 

Nota: Version definitiva en el Mensaje de @Escafandra  

 

delphi
  1. function RestaFechas(D1, D2:TDate): String;


Editado por sir.dev.a.lot, 17 julio 2016 - 08:19 .

  • 2

#7 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 16 julio 2016 - 05:39

sir.dev.a.lot, muy buena función y te evitas contar los días.
 


Solo un aporte mas, Aun que ya la version de @Escafandra, esta 100% Completa.

En realidad la función que expuse tenía un bug que ya está corregido.
 
sir.dev.a.lot, tu función tiene un bug si fecha final es menor que fecha inicial y otro si el día final es menos que el inicial. Fáciles de solucionar.

En caso de fecha final menor que la inicial, mi función cuenta cero días y la de santiago14 es capaz de contar días negativos.

 

Archivo adjunto  fechas.jpg   25,87KB   0 descargasArchivo adjunto  fechas2.jpg   24,78KB   0 descargas
 
Saludos.


  • 0

#8 sir.dev.a.lot

sir.dev.a.lot

    Advanced Member

  • Miembros
  • PipPipPip
  • 545 mensajes
  • Location127.0.0.1

Escrito 16 julio 2016 - 07:16

@Escafandra, Bien!, segun vamos mostrando codigo, se van depurando mas y mas las funciones.

 

Aunque ya su version es la definitiva. (y)

 

No me habia percatado de esos 2 bugs, Gracias por encontrarlos.

 

Saludos!


  • 0

#9 tiquinho

tiquinho

    Member

  • Miembros
  • PipPip
  • 36 mensajes

Escrito 18 agosto 2022 - 04:03

El problema es que no todos los meses tienen 30 días ni todos los años 365 días. Si queremos un cálculo exacto como si mirásemos el cálculo en un calendario, hay que tener en cuenta años bisiestos y días por mes.

 

Para ello propongo esta función por la "cuenta de la vieja"


delphi
  1. function RestaFechas(D1, D2:TDate): String;
  2. var
  3.   i: integer;
  4.   dia0, mes0, ano0: WORD;
  5.   dia, mes, ano: WORD;
  6.   dias, meses, anos: WORD;
  7.   D: integer;
  8. begin
  9.   D:= Ceil(D2)-Ceil(D1);
  10.   dias:= 0;
  11.   meses:= 0;
  12.   anos:= 0;
  13.  
  14.   DecodeDate(D1, ano0, mes0, dia0);
  15.   DecodeDate(D1, ano, mes, dia);
  16.   for i:= 0 to D-1 do
  17.   begin
  18.    D1:= D1+1;
  19.    inc(dias);
  20.    DecodeDate(D1, ano, mes, dia);
  21.    if dia = dia0 then
  22.    begin
  23.      inc(meses);
  24.      dias:= 0;
  25.      if mes = mes0 then
  26.      begin
  27.        meses:= 0;
  28.        inc(anos);
  29.      end;
  30.    end;
  31.   end;
  32.   Result:= IntToStr(anos) + ' años, ' +  IntToStr(meses) + ' meses, y ' + IntToStr(dias) + ' dias';
  33. end;

Uso:


delphi
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. begin
  3. Label1.Caption:= RestaFechas(DateTimePicker1.DateTime, DateTimePicker2.DateTime);
  4. end;

Esta seria la diferencia de cálculo entre las dos funciones propuestas:

 

attachicon.giffechas.jpg

 

 

 

Saludos.

 

Buenas tardes escafandra

 

Estoy validando este rango de fechas 30/09/2021 y 29/03/2022

 

Para lo que necesito, me debería salir 0 años, 5 meses y 28 días, pero me sale 0 años, 4 meses y 58 días. 

 

Esto sucede por que la condición if (dia = dia0) then no se va a cumplir para el mes 2, ya que este tiene 28 días o 29 si es bisiesto. En el caso que los días sean 29(sino es bisiesto), 30 o 31.

 

Hice un ajuste y algunas pruebas y me ha funcionado, pero no se si este bien hecho, me gustaría una retroalimentación.

 

Gracias.


delphi
  1. D1 := FechaI;
  2. D2 := Fechaf;
  3. D:= Ceil(D2)-Ceil(D1);
  4. dias:= 0;
  5. meses:= 0;
  6. anos:= 0;
  7.  
  8. DecodeDate(D1, ano0, mes0, dia0);
  9. DecodeDate(D2, ano, mes, dia);
  10. for i:= 0 to D-1 do
  11. begin
  12. D1:= D1+1;
  13. inc(dias);
  14. DecodeDate(D1, ano, mes, dia);
  15. if (dia = dia0) or ((mes=3) and (dias=dia0)) then
  16. begin
  17. inc(meses);
  18. dias:= 0;
  19. if mes = mes0 then
  20. begin
  21. meses:= 0;
  22. inc(anos);
  23. end;
  24. end;
  25. end;


  • 1

#10 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 19 agosto 2022 - 11:02

Efectivamente es un bug lo que describes y puede ocurrir cuando la fecha de inicio es mayor de 28. Afecta no sólo a febrero sino a meses de 30 días si se inicia en un día 31.
La idea que propones no es correcta puesto que la variable días lleva la cuenta del resto de días antes de cumplir un nuevo mes. No nos dice cuantos días tiene un mes. En un principio pensé a bote pronto en corregir el problema con DaysInMonth pero tampoco es correcto. Cuando tenga un poco de tiempo lo miro más afondo.
 

Saludos.
  • 2

#11 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 21 agosto 2022 - 05:29

He pensado algo sobre el asunto y no es trivial. Si quisiésemos contar días, horas o minutos sería un asunto trivial pero si queremos contar meses se debe partir de un consenso.
¿Cuántos meses pasan entre dos fechas? La respuesta puede ser los meses naturales que se ven pasar en el calendario o meses hipotéticos. Me explico con un ejemplo basado en un test de 6 preguntas:

1.- ¿Cuántos meses pasan desde el 28-1-2022 hasta el 28-2-2022?
a) Un mes y cero días.
b) Cero meses y 31 días.

2.- ¿Cuántos meses pasan desde el 27-1-2022 hasta el 1-3-2022?
a) Un mes y 2 días.
b) Un mes y 5 días.

3.- ¿Cuántos meses pasan desde el 28-1-2022 hasta el 1-3-2022?
a) Un mes y 1 día.
b) Un mes y 4 días.

4.- ¿Cuántos meses pasan desde el 29-1-2022 hasta el 1-3-2022?
a) Un mes y cero días.
b) Un mes y 3 días

5.- ¿Cuántos meses pasan desde el 30-1-2022 hasta el 1-3-2022?
a) Un mes y cero días
b) Un mes y 2 días

6.- ¿Cuántos meses pasan desde el 31-1-2022 hasta el 1-3-2022?
a) Un mes y cero días
b) Un mes y 1 días



Las repuestas tipificadas como a) son correctas si contamos como un més desde un día de inicio y sumamos un día a la fecha hasta encontrar el mismo día de inicio o saltar de mes por su máximo número de días. Se trata de una cuenta de meses virtuales similar a como contamos los años de edad de una persona:

Edad de una persona nacida el 21-8-2021 a fecha de 21.8.2022:
a) Un año
b) Cero años y 12 meses, puesto que no vi pasar un año natural completo por el calendario.



Volviendo al problema original. Si contamos como meses naturales, la cuenta es muy fácil, contamos los meses naturales del calendario y le sumamos los días desde el día cero al final de su mes sumando la cuenta días hasta el día fin. Con esta cuenta concluiríamos como en el caso 1) que desde el 28-1-2022 hasta el 28-2-2022 van cero meses y 31 días, cosa con la que no estarán de acuerdo algunos.

Pero las respuestas a también nos hacen caer en incongruencias como el caso 4) y 5) que dan la misma respuesta para el caso virtual a). Entonces en este caso podemos añadir a los días restantes la diferencia entre el día de inicio y el tope de días de ese mes siempre que sea mayor a 28...

2.- ¿Cuántos meses pasan desde el 27-1-2022 hasta el 1-3-2022?
a) Un mes y 2 día.
b) Un mes y 5 días.

3.- ¿Cuántos meses pasan desde el 28-1-2022 hasta el 1-3-2022?
a) Un mes y 1 día.
b) Un mes y 4 días.

4.- ¿Cuántos meses pasan desde el 29-1-2022 hasta el 1-3-2022?
a) Un mes y 2 días.
b) Un mes y 3 días.

5.- ¿Cuántos meses pasan desde el 30-1-2022 hasta el 1-3-2022?
a) Un mes y 1 día.
b) Un mes y 2 días.

Tenemos de nuevo incongruencias, la intuición nos dice que no puede ser 2) = 4) ni 3) = 5)

Una vez más podemos cambiar de idea y considerar para el mapa meses virtuales que el caso 3 es correcto, puesto que de 28-1 al 28-2 pasa un mes y un día. Entonces a partir de ahí debemos considerar que en el caso 4 restamos un día, resultando un mes y cero días, y al caso 5 le restamos 2 días. Pero ahora el caso 5 sería un mes menos 1 día, luego cero meses lo que no es justo lo que pasa.

En resumen, sin un consenso claro de como medir los meses virtuales, tenemos que aceptar incongruencias en los días que resten de la cuenta. La medición en meses reales, tampoco parece la opción que gustaría.

Espero comentarios, ideas y correcciones.



Saludos.
  • 1

#12 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.446 mensajes
  • LocationMéxico

Escrito 22 agosto 2022 - 07:57

buen día amigo escafandra.

 

Esto me recuerda a la pregunta:

 

¿Cuántos cumpleaños ah pasado una persona que nació un 29 de Febrero?

 

Creo que aquí lo que se requiere es de una regla de negocio como se hace en temas laborales donde un mes cuenta con 30 días, es decir el promedio entre los días del año y los12 meses.

 

Saludos


  • 1

#13 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 22 agosto 2022 - 09:29

Efectivamente hay que consensuar una regla. Aunque considerar meses de 30 días nos lleva a asegurar a los padres de un bebé nacido el 1 de febrero que no cumple un mes el 1 de marzo. Lo incongruente es tener una cuenta que pretende una precisión de días y sin embargo tener un error de meses.

Es por eso que quizás hay que admitir las incongruencias en días de los casos 4, 5 y 6 donde mostraban la misma respuesta.

Es ahí donde quizás se encuentre un consenso donde se cuenten bien los meses y se asuma cierta imperfección en la cuenta de días.



Asumiendo que un mes se cuenta desde un día hasta que se repita o hasta que se obligue por finalizar sus días, el código que propongo es así:
 

delphi
  1. function RestaFechas(D1, D2:TDate): String;
  2. var
  3. i: integer;
  4. dia0, mes0, ano0: WORD;
  5. dia, mes, ano: WORD;
  6. dias, meses, anos, top: WORD;
  7. D: integer;
  8. cambio: boolean;
  9. begin
  10. D:= Ceil(D2)-Ceil(D1);
  11. dias:= 0;
  12. meses:= 0;
  13. anos:= 0;
  14. cambio:= false;
  15. DecodeDate(D1, ano0, mes0, dia0);
  16. DecodeDate(D1, ano, mes, dia);
  17. for i:= 0 to D-1 do
  18. begin
  19. D1:= D1+1;
  20. inc(dias);
  21. DecodeDate(D1, ano, mes, dia);
  22. top:= DaysInMonth(D1);
  23. if (dia = dia0) or cambio then
  24. begin
  25. inc(meses);
  26. dias:= 0;
  27. if mes = mes0 then
  28. begin
  29. meses:= 0;
  30. inc(anos);
  31. end;
  32. end;
  33. cambio:= (dia0 > top) and (dia = top);// and (dia0 <> 31);
  34. end;
  35. Result:= IntToStr(anos) + ' años, ' + IntToStr(meses) + ' meses y ' + IntToStr(dias) + ' dias';
  36. end;

Saludos.
  • 2

#14 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.446 mensajes
  • LocationMéxico

Escrito 22 agosto 2022 - 11:09

...
Es por eso que quizás hay que admitir las incongruencias en días de los casos 4, 5 y 6 donde mostraban la misma respuesta.

Es ahí donde quizás se encuentre un consenso donde se cuenten bien los meses y se asuma cierta imperfección en la cuenta de días.

Asumiendo que un mes se cuenta desde un día hasta que se repita o hasta que se obligue por finalizar sus días, el código que propongo es así:
...

Ciertamente, el enfoque es lo que es necesario definir y a partir de ello crear la regla.

Muy buen punto amigo.

Saludos
  • 1

#15 tiquinho

tiquinho

    Member

  • Miembros
  • PipPip
  • 36 mensajes

Escrito 23 agosto 2022 - 08:41

Efectivamente hay que consensuar una regla. Aunque considerar meses de 30 días nos lleva a asegurar a los padres de un bebé nacido el 1 de febrero que no cumple un mes el 1 de marzo. Lo incongruente es tener una cuenta que pretende una precisión de días y sin embargo tener un error de meses.

Es por eso que quizás hay que admitir las incongruencias en días de los casos 4, 5 y 6 donde mostraban la misma respuesta.

Es ahí donde quizás se encuentre un consenso donde se cuenten bien los meses y se asuma cierta imperfección en la cuenta de días.



Asumiendo que un mes se cuenta desde un día hasta que se repita o hasta que se obligue por finalizar sus días, el código que propongo es así:
 


delphi
  1. function RestaFechas(D1, D2:TDate): String;
  2. var
  3. i: integer;
  4. dia0, mes0, ano0: WORD;
  5. dia, mes, ano: WORD;
  6. dias, meses, anos, top: WORD;
  7. D: integer;
  8. cambio: boolean;
  9. begin
  10. D:= Ceil(D2)-Ceil(D1);
  11. dias:= 0;
  12. meses:= 0;
  13. anos:= 0;
  14. cambio:= false;
  15. DecodeDate(D1, ano0, mes0, dia0);
  16. DecodeDate(D1, ano, mes, dia);
  17. for i:= 0 to D-1 do
  18. begin
  19. D1:= D1+1;
  20. inc(dias);
  21. DecodeDate(D1, ano, mes, dia);
  22. top:= DaysInMonth(D1);
  23. if (dia = dia0) or cambio then
  24. begin
  25. inc(meses);
  26. dias:= 0;
  27. if mes = mes0 then
  28. begin
  29. meses:= 0;
  30. inc(anos);
  31. end;
  32. end;
  33. cambio:= (dia0 > top) and (dia = top);// and (dia0 <> 31);
  34. end;
  35. Result:= IntToStr(anos) + ' años, ' + IntToStr(meses) + ' meses y ' + IntToStr(dias) + ' dias';
  36. end;

Saludos.

 

 

Buenos días escafranda

 

Realmente muchísimas gracias por tomarte el tiempo para el análisis, explicación y ajuste del código. 

Lo he probado y ha sido bastante acertado los cálculos.

 

Es claro que en algunos casos toca llegar a un consenso, ya que por ejemplo, para estas fechas:

 

31/01/2022 al 01/03/2022 

 

Bien podría ser un mes o 29 días, pero en términos generales, el código realiza su cometido con gran fiabilidad.

 

Nuevamente muchas gracias.


  • 1




IP.Board spam blocked by CleanTalk.