Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Varios (https://www.clubdelphi.com/foros/forumdisplay.php?f=11)
-   -   Optimizar comparación de fechas (https://www.clubdelphi.com/foros/showthread.php?t=33922)

kuan-yiu 20-07-2006 19:26:51

Optimizar comparación de fechas
 
El título puede no ser del todo exacto, pero no se me ocurría otro mejor. Gracias de antemano a todos.
Es una cuestión algo tonta: me he planteado si lo estaré haciendo de un modo óptimo o por lo menos mínimo.

El problema:
Tengo que comparar 4 fechas más o menos lo que quiero saber es:
Cita:

fecha_creacion<=fecha_aprobacion<=fecha_emision_f_ant<=fecha_emision_f
Si esta comparación no se cumple no puedo dejar grabar los cambios.

Problema, los datos se obtienen de una consulta, están en DBEdits y algunos pueden llegar en blanco, y ese es el problema. Tengo un pequeño monstruo de if-else anidados para asegurarme de que no comparo cuando llegan en blanco y sí cuando tienen datos... que no me acaba de convencer.
Código Delphi [-]
function fechasValidas: Boolean;
begin
// fecha_creacion siempre tiene algún valor.
  if (fecha_aprobacion.Text<>'') then
  begin
  // fecha_aprobacion tiene algún valor
    result := StrToDate(fecha_creacion.Text)<=StrToDate(fecha_aprobacion.Text);
    if (result=FALSE) then
      exit;
    if (fecha_emision_f_ant.Text<>'') then
    begin
    // fecha_emision_f_ant tiene algún valor
      result := (StrToDate(fecha_aprobacion.Text)<=StrToDate(fecha_emision_f_ant.Text));
      if (result=FALSE) then
        exit;
      if (fecha_emision_f.Text<>'') then
        result := (StrToDate(fecha_emision_f_ant.Text)<=StrToDate(fecha_emision_f.Text));
    end
    else
    begin
    // fecha_emision_f_ant no tiene datos
      result := StrToDate(fecha_creacion.Text)<=StrToDate(fecha_aprobacion.Text);
      if (result=FALSE) then
        exit;
      if (fecha_emision_f.Text<>'') then
        result := (StrToDate(fecha_aprobacion.Text)<=StrToDate(fecha_emision_f.Text));
    end;
  end
  else
  // fecha_aprobacion no tiene datos, por lo que el resto tampoco pueden tenerlos.
    result := TRUE;
end;

Peculiaridades:
fecha_creacion: nunca llega en blanco.
fecha_aprobacion: si no existe las otras dos (fecha_emision_f_ant y fecha_emision_f) tampoco.


PD: Me ha costado escribir todo esto... creo que me voy a tomar una galleta para recuperarme :D .

delphi.com.ar 20-07-2006 23:47:49

Si quieres optimizar el código para menor uso de procesador, yo evitaría convertir un string a un TDate para cada comparación. O a lo sumo excluír en en las primeras líneas las comparaciones que serán mas comunes!

Por otro lado, líneas como esta:
Código Delphi [-]
   result := StrToDate(fecha_creacion.Text)<=StrToDate(fecha_aprobacion.Text);
   if (result=FALSE) then
     exit;
no es lo mismo que esto? :
Código Delphi [-]
   if not StrToDate(fecha_creacion.Text)<=StrToDate(fecha_aprobacion.Text) then
     exit;
Yo personalmente prefiero evitar los Exit intentando que la lógica te lleve a la salida :p

Saludos!

Ñuño Martínez 21-07-2006 00:46:47

A raíz del último comentario, una forma de optimizar podría ser:
Código Delphi [-]
FUNCTON Comprobar: BOOLEAN;
 BEGIN
   Result := FALSE;
   IF A = B THEN
   BEGIN
     IF B = C THEN
       Result := TRUE;
   END;
 END;

Como ves, Result sólo se asigna en dos puntos y la propia lógica "te lleva fuera". Además reduces en operaciones ya que no tienes que hacer:

Código Delphi [-]
      result := StrToDate(fecha_creacion.Text)<=StrToDate(fecha_aprobacion.Text);
       if (result=FALSE) then ...

Mick 21-07-2006 05:13:04

Yo seguramente haria algo como:

Código:

function fechasValidas: Boolean;
var
 f1,f2,f3,f4: TDateTime;
begin
    f1:= StrToDateDef(fecha_creacion.text      ,MaxDouble);
    f2:= StrToDateDef(fecha_aprovacion.text    ,MaxDouble);
    f3:= StrToDateDef(fecha_emision_f_ant.text,MaxDouble);
    f4:= StrToDateDef(fecha_emision_f.text      ,MaxDouble);
    Result:= (f1<=f2) and (f2<=f3) and (f3<=f4);
end;

Aunque se podria optimizar, creo que en este caso no valdria la pena ya
que el codigo quedaria mucho mas enrevesado, y salvo que haya que llamar
miles de veces por segundo a esa funcion, la velocidad de ejecucion sera suficiente.

Saludos

kuan-yiu 21-07-2006 09:30:32

Antes de nada gracias a todos por vuestros comentarios :)
Por partes:

delphi.com.ar
He puesto los exit porque antes lo tenía todo con if-else y era múchísimo más grande y lioso, a mi entender, y en un caso como este no creo que sea un mal uso, y eso que a mi tampoco me gusta emplearlos.
Creo que es buena idea lo de hacer las transformaciones una única vez, al principio y luego usar directamente esos valores.

Ñuño Martínez
En el caso que pones no estás considerando las fechas nulas, que siempre deben ignorarse de la lógica de las comparaciones, por lo que ese sistema no me sirve en este caso concreto.
Quizá usar result una única vez sea lo mejor (lo tenía así al principio y en su lugar usaba una variable temporal).

Mick
Igualmente no estás considerando las fechas nulas que darían falsos negativos.
Y creo que definitivamente voy a hacer las transformaciones a "TDate" al principio.

Lepe 21-07-2006 10:21:25

De todas las respuestas dadas, la que más me gusta es la de Mick, de hecho, yo haría algo así:
Código Delphi [-]
function fechasValidas: Boolean;
var
 creacion, aprobacion, emision_ant, emision_f: TDateTime;
begin
    creacion   := StrToDateDef(fecha_creacion.text       ,MaxDouble);
    aprobacion := StrToDateDef(fecha_aprovacion.text    ,MaxDouble);
    emision_ant:= StrToDateDef(fecha_emision_f_ant.text,MaxDouble);
    emision_f  := StrToDateDef(fecha_emision_f.text      ,MaxDouble);

    Result := aprobacion <> maxdouble;
    result := result and creacion <= aprobacion;

    if emision_ant <> maxdouble then
    begin

      result := result and aprobacion <= emision_ant;
      if emision_f <> maxdouble then
        result := result and emision_ant <= emision_f
    end;
    else
    begin
      result := result and creacion <= aprobacion;
      if emision_f <> maxdouble then
        result := result and aprobacion <= emision_F
    end;

end;
Si delphi tiene desactivada la directiva "Complete Boolean evaluation" (por defecto creo que sí en Tools --> enviroment), las comparaciones serían tremendamente efectivas.

El punto es usar Result una y otra vez y siempre como primera condición de comparación, si ya es false, Delphi detecta que está unida la comparación con un "and" y no evalúa la condición de la derecha del "and", directamente devuelve falso.

Creo que las comparaciones están correctas.

Saludos

kuan-yiu 21-07-2006 10:37:10

Que curioso, acabo de hacer una segunda versión de la función que toma detalles que todos me habéis indicado y que se parece mucho a la tuya Lepe :)
Código Delphi [-]
{ Compara las fechas para asegurar que no son incongruentes entre si. }
function TfEditPpto.fechasValidas: Boolean;
var
  fec1,fec2,fec3, fec4: TDateTime;
  condi: Boolean;
begin
// Primero se transforman todos los datos.
  fec1 := StrToDateDef(fecha_creacion.Text, 0);
  fec2 := StrToDateDef(fecha_aprobacion.Text, 0);
  fec3 := StrToDateDef(fecha_emision_f_ant.Text, 0);
  fec4 := StrToDateDef(fecha_emision_f.Text, 0);
// Determina si las fechas son válidas.
  condi := TRUE;
  if (fec2>0) then
    condi := condi and (fec1<=fec2);
  if (fec3>0) then
  begin
    condi := condi and (fec2<=fec3);
    if (fec4>0) then
      condi := condi and (fec3<=fec4);
  end
  else
    if (fec4>0) then
      condi := condi and (fec2<=fec4);
  result := condi;
end;
Esta ya me gusta más, ¡¡MUCHAS GRACIAS A TODOS!!

Lepe 21-07-2006 18:46:43

Si... pero en la mia uso variables más descriptivas y me ahorro la Booleana :p :D

Saludos

Mick 21-07-2006 19:09:58

El codigo que he puesto si tiene en cuenta las fechas nulas.
Precisamente por eso se usa un StrToIntDef con valor por defecto MaxDouble, simplemente esta hecho asi a proposito para ahorrarnos los IFs posteriores. Es decir el uso de MaxDouble en los STrToIntDef hace que no sea necesario poner ningun IF posterior de comprobacion. Porque esos IFS estan implicitos en la comparacion final:

Result:= (f1<=f2) and (f2<=f3) and (f3<=f4);

Si las fecha es nula el strtointdef da error y devuelve MaxDouble de modo que las comparaciones posteriores dan el resultado correcto (por lo menos el mismo que el codigo inicial que se ha puesto).

Saludos

kuan-yiu 26-07-2006 09:58:04

Que cosas... entonces "MaxDouble" es una constante definida en alguna unidad, ¿en cual? No la conocía.

delphi.com.ar 26-07-2006 19:55:01

Cita:

Empezado por kuan-yiu
Que cosas... entonces "MaxDouble" es una constante definida en alguna unidad, ¿en cual? No la conocía.

Unit Math! ;)

kuan-yiu 27-07-2006 10:11:02

Cita:

Empezado por delphi.com.ar
Unit Math! ;)

Gracias :)


La franja horaria es GMT +2. Ahora son las 13:45:10.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi