Bitsin bu Tarih/Saat işkencesi(TDateTime tipleri ile oynamak..)

// 23 Mayıs 2009 // Delphi, Programlama

Hemen hemen hepimiz yazdığımız projelerde tarih/saat verilerini kullanır, bunlar üzerinde çeşitli maniplasyonlar yaparız. Kimi zaman belli bir tarihe bir değer eklemek , kimi zamanda çıkartmak isteriz. Kimi zaman muhasebe/finansman uygulamalarında vade gün ve tarihlerini hesaplarken tarih/saat hesaplama rutinlerine ihtiyaç duyarız. Bazen de daha spesifik tarih/saat hesaplarına ihtiyacımız olur, artık yıl ile başımızı derde sokarız.

Tabii ki her modern programlama dilinin bu sorunları aşmak için hazır fonksiyon/kütüphaneleri vardır. Delphi’nin de var. Ancak bizim makalemize konu olacak gibi değil ;)
Genellikle, kullanıcılarımıza tarih ve saat verilerini girebilmeleri için, veri biçimlendirme denetimleri kullanırız. Bunların başında sanıyorum ki hepimizin kullandığı TDateTimePicker gelir. Ancak TDateTimePicker bileşeni bile tarih saat girmek için aslında meşakkatli bir bileşendir. Bu bileşenle uğraşmak istemeyenler TMaskEdit’in tarih saat formatındaki maskelerini kullanarak giriş işleminin yapılmasını sağlamak isterler. Ancak bu bileşen TDateTimePicker’dan bile daha yorucudur. Bu bileşenlerin birbirlerine göre güzel tarafları bulunmakla birlikte her ikisi de tam manası ile kusursuz tarih/saat girişleri için yeterlidir denilemez. TDateTimePicker’ın hem tarih hem saat gösterme ve değiştirmesine yarayan format harici bir property’si yokken, Format kullanıldığında da çeşitli garipliklerle karşılaşırız. TMaskEdit’in ise geçersiz tarih formatı hatalarından hem size hemde kullanıcılara eminim gına gelmiştir.

Herhangi bir tarihe 07 ay 25 gün 06 saat 05 dk 48 saniye eklemek istesek, bahsedilen bileşenler ile bunu yapmamız son derece zor olacaktır. Evet tüm bu zorlukları yaşadığınızı, yada siz yaşamasanız bile yazdığınız programın kullanıcılarının yaşadıklarını biliyorum. Belki artık onlar daha iyi bir alternatife rastlamadıkları için bu durumu kanıksamış olabilirler ama biz şimdi tarih/saat girişlerindeki kalıplaşmış yapıyı kırmaya hazırlanıyoruz.. Nasıl mı ?

Önce biraz hayalinizde canlandırmanızı istiyorum. Herhangi bir TDateTime verisini tutan bir veribilinçli denetimde aşağıdaki şekilde girişler yapabildiğinizi düşünün:

+5g -> Mevcut tarihten 5 gün sonrası
+5a -> Mevcut tarihten 5 ay sonrası
+5y -> Mevcut tarihten 5 yıl sonrası
+5s -> Mevcut tarihten 5 saat sonrası
+5dk-> Mevcut tarihten 5 dakika sonrası
+5sn-> Mevcut tarihten 5 saniye sonrası

+5g* -> Bugünden 5 gün sonrası
+5a* -> Bugünden 5 ay sonrası
+5y* -> Bugünden 5 yıl sonrası
+5s* -> Bugünden 5 saat sonrası
+5dk* -> Bugünden 5 dakika sonrası
+5sn* -> Bugünden 5 saniye sonrası

bugün-> Bugünün tarihi
bugun-> Bugünün tarihi
şimdi -> Bugünün tarihi
simdi -> Bugünün tarihi
today -> Bugünün tarihi
now -> Bugünün tarihi
yarın -> Yarının tarihi
yarin -> Yarının tarihi
tomorrow -> Yarının tarihi
dün -> Dün
dun -> Dün
yesterday -> Dün
23052009 -> 23/05/2009
230509 -> 23/05/2009
23/05 -> 23/05/2009
23.05 -> 23/05/2009
23.05/2009 -> 23/05/2009
23/05.2009 -> 23/05/2009
23.05/09 -> 23/05/2009
23/05.09 -> 23/05/2009

Tabii yukarıda + olarak verdiğim örneklerin – olarak da çalışacağını söylemeye gerek duymuyorum.Sanırım yazdığımız programların kullanıcıları bu durumdan son derece memnun olacaklardır. Ayrıca belki bazı hesaplamalarda kodsal anlamda bizimde işimize yarayacaktır. Bu kadar kêlamın ardından arzu ederseniz bu sihirli metodu sizlerin beğenisine sunayım:

function ConvertToDateTime(const AOldValue, AValue : String) : TDateTime;
type
TSeperator = record
  AlphaSection  : String;
  NumSection    : Integer;
  UseCurrentDate: Boolean;
end;

function Parse(const Value : String) : TSeperator;
var
  iCounter  : Integer;
  sNum,
  sValue    : String;

  TAlpha    : set of Char; //'a'..'z';
  TNums     : set of Char; //'0'..'9';
begin
  sNum := '';
  sValue := LowerCase(Value);

  TAlpha := ['a'..'z'];
  TNums  := ['0'..'9'];

  Result.AlphaSection := '';
  Result.NumSection := 0;
  Result.UseCurrentDate := false;

  for iCounter := 1 to Length(sValue) do
  begin
    if sValue[iCounter] in TAlpha then Result.AlphaSection := Result.AlphaSection + sValue[iCounter];
    if sValue[iCounter] in TNums  then sNum := sNum + sValue[iCounter];
    if sValue[iCounter] = '*' then Result.UseCurrentDate := true;
  end;

  if sNum <> '' then
    Result.NumSection := StrToIntDef(sNum, 0);
end;

  var
  sVal        : String;
  iMultiplier : Integer;
  OldDate     : TDateTime;
  Separator   : TSeperator;
begin
  Result := 0;
  iMultiplier := 1;

  OldDate := Now;

  if Trim(AOldValue) <> '' then
    OldDate := StrToDateTimeDef(Trim(AOldValue), Now);

  sVal := Trim(AValue);
  sVal := StringReplace(sVal, 'Ü', 'ü', [rfReplaceAll]);
  sVal := StringReplace(sVal, 'Ş', 'ş', [rfReplaceAll]);
  sVal := StringReplace(sVal, 'İ', 'i', [rfReplaceAll]);
  sVal := LowerCase(sVal);

  sVal := StringReplace(sVal, '.', DateSeparator, [rfReplaceAll]);
  sVal := StringReplace(sVal, ',', DateSeparator, [rfReplaceAll]);
  sVal := StringReplace(sVal, '/', DateSeparator, [rfReplaceAll]);

  if Pos(DateSeparator, sVal) <> 0 then
  begin
    try
      Result := StrToDate(sVal);
      Exit;
    except
    end;
  end;

  if Length(sVal) = 0 then Exit;

  if  (sVal = 'bugün') or
      (sVal = 'bugun') or
      (sVal = 'şimdi') or
      (sVal = 'simdi') or
      (sVal = 'today') or
      (sVal = 'now') then
  begin
    Result := Now;
    Exit;
  end;

  if  (sVal = 'yarın') or
      (sVal = 'yarin') or
      (sVal = 'tomorrow') then
  begin
    Result := Now + 1;
    Exit;
  end;

  if  (sVal = 'dün') or
      (sVal = 'dun') or
      (sVal = 'yesterday') then
  begin
    Result := Now - 1;
    Exit;
  end;

  if sVal[1] = '-' then iMultiplier := -1;

  if (sVal[1] in ['-', '+']) and (Length(sVal) >= 3) then
  begin
    Delete(sVal, 1, 1); // -, +

    Separator := Parse(sVal);

    if Separator.AlphaSection = '' then Separator.AlphaSection := 'g';

    if Separator.AlphaSection = 'g' then // Gün
    begin
      if Separator.UseCurrentDate
      then Result := IncDay(Now, iMultiplier * Separator.NumSection)
      else Result := IncDay(OldDate, iMultiplier * Separator.NumSection);

      Exit;
    end;

    if Separator.AlphaSection = 'a' then // Ay
    begin
      if Separator.UseCurrentDate
      then Result := IncMonth(Now, iMultiplier * Separator.NumSection)
      else Result := IncMonth(OldDate, iMultiplier * Separator.NumSection);

      Exit;
    end;

    if Separator.AlphaSection = 'y' then // Yıl
    begin
      if Separator.UseCurrentDate
      then Result := IncYear(Now, iMultiplier * Separator.NumSection)
      else Result := IncYear(OldDate, iMultiplier * Separator.NumSection);

      Exit;
    end;

    if Separator.AlphaSection = 's' then // Saat
    begin
      if Separator.UseCurrentDate
      then Result := IncHour(Now, iMultiplier * Separator.NumSection)
      else Result := IncHour(OldDate, iMultiplier * Separator.NumSection);

      Exit;
    end;

    if Separator.AlphaSection = 'dk' then // Dakika
    begin
      if Separator.UseCurrentDate
      then Result := IncMinute(Now, iMultiplier * Separator.NumSection)
      else Result := IncMinute(OldDate, iMultiplier * Separator.NumSection);

      Exit;
    end;

    if Separator.AlphaSection = 'sn' then // Saniye
    begin
      if Separator.UseCurrentDate
      then Result := IncSecond(Now, iMultiplier * Separator.NumSection)
      else Result := IncSecond(OldDate, iMultiplier * Separator.NumSection);

      Exit;
    end;
  end;

  if (Length(sVal) mod 2) <> 0 then sVal := '0' + sVal;

  if ( Length(sVal) = 6 ) or ( Length(sVal) = 8 ) then // 23052009 , 230509 gibi tarih girişleri
  begin
    Insert( DateSeparator, sVal, 5 );
    Insert( DateSeparator, sVal, 3 );

    TryStrToDate(sVal, Result);
    Exit;
  end;

  raise Exception.Create('Tarih/Saat dönüşümlerinde hata.!');
end;

Şimdi yeni bir uygulama açıyoruz ve formumuzun üzerine bir grid atıyoruz. Veritabanı bağlantılarını ayarladıktan sonra içinde tarihsel bir veri ihtiva eden bir sql sonucunu yada bir tabloyu TDataSet türevli bir nesneye atıyoruz. Ardından tarih alanımızı o nesneye ekliyoruz.(TDataSet türevi component üzerinde Fields Editor/Add All Fields).
Ardından TDateTimeField türündeki field’ın olaylarından OnSetText‘e aşağıdaki gibi bir kod yazacağız;

procedure TForm1.ClientDataSet1SaleDateSetText(Sender: TField;
const Text: String);
begin
  try
    Sender.AsDateTime := ConvertToDateTime(Sender.AsString, Text);
  except
    Sender.AsString := Text;
  end;
end;

Şimdi programı çalıştıralım ve grid yada bir TDBEdit üzerinden denemelerimizi yapalım. İyi eğlenceler ;)

Saygılar, sevgiler..

“Bitsin bu Tarih/Saat işkencesi(TDateTime tipleri ile oynamak..)” için 2 Yorum

  1. nurullah celik diyor ki:

    bir proğramaın sattini belli bie saat arasında nasıl gösteririm 17:00-19:00

  2. Knosegogy diyor ki:

    ilginc bilgiler icin tesekkurler

Yorum Yazın