Resolvendo problemas de intervalo de data com Records em Delphi

Record é um tipo de dado que lhe permite estruturar dados, a partir do Delphi 2006 Record se tornou quase uma classe, suportando:

  • Construtores
  • Sobreposição de operadores
  • Declaração de métodos não-virtuais
  • Métodos e propriedades estáticos

Podemos utilizar os Record de várias maneiras, no meu caso utilizo muito em parâmetros de métodos que devem representar chaves primárias (Primary Key), desta forma facilita a leitura e manutenção do código, é verdade que você não fica mudando as chaves primárias das tabelas do seu banco de dados o tempo todo, mas quando precisar o mudança no código será simples. Outro exemplo seria informar intervalo de dados.

Imagine que você precise gerar um conjunto de boletos de um determinado período, você iria declarar algo assim.

    procedure GerarBoleto( DataInicial, DataFinal : TDateTime );

Geralmente temos problema com periodos, porque os bancos de dados armazenam data e hora no mesmo campo, desta forma temos que na data inicial estar certo que a hora está acertada para ’00:00:00′ e para a data final teremos que estar certos de ter como hora final ’23:59:59′, existem outros aspectos e artifícios dependendo do banco de dados para contornar esta situação, mas isso depende de cada banco.

Record é uma excelente solução para estes casos, para isso representamos os parâmetros do método GerarBoleto como um Record e ele irá fazer todo o trabalho para evitar os problema de data e hora, assim como facilitar a leitura, entendimento e manutenção do código.

O método passaria a ser declarado assim:

    procedure GerarBoleto( periodo : TPeriodo );

A declaração do Record teria as propriedades DataInicial e DataFinal, estas por suas vez quando receberam valores terão os mesmo ajustados de acordo com os método Set.

Abaixo a declaração do Record e a seguir algumas explicações para o mesmo.

unit Perido;

interface

uses SysUtils, DateUtils;

type

  TPeriodo = Record
  private
    FDataFinal: TDateTime;
    FDataInicial: TDateTime;
    procedure SetDataFinal(const Value: TDateTime);
    procedure SetDataInicial(const Value: TDateTime);

  public
    property DataInicial: TDateTime read FDataInicial write SetDataInicial;
    property DataFinal: TDateTime read FDataFinal write SetDataFinal;

    Constructor Create(Di, Df: TDateTime);

    procedure SetIntervaloAnual( Anoi, Anof : Integer );
  end;

implementation

{ TPeriodo }

constructor TPeriodo.Create(Di, Df: TDateTime);
begin
  DataInicial := Di;
  DataFinal := Df
end;

procedure TPeriodo.SetDataFinal(const Value: TDateTime);
begin
  FDataFinal := EncodeDateTime(Yearof(Value), MonthOf(Value), Dayof(Value), 23, 59, 59, 1000);
end;

procedure TPeriodo.SetDataInicial(const Value: TDateTime);
begin
  FDataInicial := EncodeDateTime(Yearof(Value), MonthOf(Value), Dayof(Value), 0, 0, 0, 0);
end;

procedure TPeriodo.SetIntervaloAnual(Anoi, Anof: Integer);
begin
  DataInicial := EncodeDate(Anoi, 1, 1);
  DataFinal   := EncodeDate(Anof, 12, 31);
end;

end.
  • Toda e qualquer atribuição de valor para DataInicial e DataFinal serão ajustados de acordo com o horário inicial e final
  • Foi criado um método adicional SetIntervaloAnual, onde você pode informar o ano inicial e final para o período e o método irá gerar os intervalos.

Abaixo uma das formas onde podemos utilizar este Record, informando período inicial e final;

var
  periodo : TPeriodo;
begin
  periodo.DataInicial := EncodeDate( 2009, 1, 1);
  periodo.DataFinal   := Now;

  GerarBoleto(periodo);

Outra forma é passar o período utilizando o construtor do Record

var
  periodo : TPeriodo;
begin
  periodo.Create(EncodeDate( 2009, 1, 1), Now);
  GerarBoleto(periodo);

Outra forma seria utilizar o método SetIntervaloAnual, onde você apenas especifica o período anual.

var
  periodo : TPeriodo;
begin

  periodo.SetIntervaloAnual(2008, 2009);

  GerarBoleto(periodo);

Em todos os casos, os valores horários foram acertados.

Mas este record pode fazer muito mais por nós, porque não gerar a declaração where do SQL para as datas de acordo com os valores? Dois simples métodos pode resolver todos os problemas.

Field, é o nome do campo que representa a data em sua tabela.

function TPeriodo.DataDBFormat(const Value: TDateTime): String;
begin
  Result := FormatDateTime('mm/dd/yyyy hh:mm:ss', Value);
end;

function TPeriodo.GenerateSQL(Field: String): String;
Const
  sql: String = '%s between ''%s'' and ''%s'' ';
begin
  Result := Format(sql, [FieldI, DataDBFormat(DataInicial), DataDBFormat(DataFinal)]);
end;

Na prática usamos desta forma:

var
  periodo : TPeriodo;
begin

  periodo.SetIntervaloAnual(2008, 2009);

  ShowMessage( periodo.GenerateSQL('DATA_BOLETO'));

O resultado no ShowMessage será: DATA_BOLETO between ’01/01/2008 00:00:00′ and ’12/31/2009 23:59:59′

Espero que este post seja útil e lhe ajude no seu dia-a-dia. Download código fonte

Até o próximo post.

3 respostas
  1. Carlos Gonzaga
    Carlos Gonzaga says:

    Andreano, muito interesante esta aplicação de record! A maioria dos codigos que já vi, utilizam ainda a sintaxe anterior deixando os novos recursos do lado.

    Responder
  2. Wagner Freitas
    Wagner Freitas says:

    Bom dia ,
    Boa solução, aprecio este tipo de disposição.
    Mas existe um bug ….
    Quando é atribuído o valor da datafinal o parâmetro milissegundos não pode ser igual a 1000.
    Sendo assim com o código corrigido o parâmetro deve ser 999.

    Responder

Deixe uma resposta

Want to join the discussion?
Feel free to contribute!

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *


Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.