Czy w delphi jest taka funkcja?

0

Ogarnąłem parę razy artykuł o Obliczeniach na datach i czasie i nie mogę znaleźć takiej która zwróciłaby ilość lat, miesięcy, dni, godzin, minut i sekund pomiędzy dwoma datami. Więc czy faktycznie, w tak bogatym zbiorze delphi, jest taka funkcja czy muszę ją samemu napisać?

0

jest, nazywa się odejmowanie.

0

facepalm...

data1-data2
0

Nie ma. Musisz sam napisać. Najpierw rozkładasz wartość datę na rok/mies/dzień itd. (od tego są funkcje - > DateUtils) a później odejmujesz kolejne składniki. Tylko i tak jest to problematyczne, gdyż nie możesz miesiąca na dni przeliczyć. Będzie ci potrzebne sprawdzanie ile dni ma dany miesiąc.

0

Nie ma. Musisz sam napisać.

No jasne, że nie ma:

DateUtils

Jak nie znasz się, to chociaż czytaj to co inni napisali :X

0

Nie ma takiej funkcji o jaką autor pyta. Jeśli jest, to wskaż ją.

DaySpan,HourSpan itd. Czy na pewno nie ma? Są.

0

Te funkcje na niewiele się zdadzą. Widać nie rozumiesz sedna problemu.

0

DecodeDateTime

0

W module DataUtils Znalazłem funcję IncDays, IncHours etc, przerobiłem je trochę. Do tego napisałem funkcję która zwraca poprawną odmianę słów dzień, godzina, minuta, sekunda.

type
  TMyLabel = class(TLabel)
  public
    procedure Add(const S: String);
    procedure Clear;
  end;
  TFormatType = (fDays, fHours, fMinutes, fSeconds);

var
  MainForm: TMainForm;
  Labela: TMyLabel;
function GetDateString(I: Integer; Format: TFormatType): String;
var Licznik, Last: Integer; S: String;
const Tab: array [TFormatType, 1..3] of String =
          (('dzień',   'dni',     'dni'),
           ('godzina', 'godziny', 'godzin'),
           ('minuta',  'minuty',  'minut'),
           ('sekunda', 'sekundy', 'sekund'));
begin

  S := IntToStr(I);
  Last := StrToInt(S[Length(S)]);

  Licznik := 3;
  if (Last >= 2) and (Last <= 4) then Licznik := 2;
  if I = 1 then Licznik := 1;

  Result := Tab[Format, Licznik];
end;
procedure TMyLabel.Add(const S: String);
begin
  Caption := Caption + S;
end;

procedure TMyLabel.Clear;
begin
  Caption := '';
end;
function DecDay(const AValue: TDateTime;
  const ANumberOfDays: Integer): TDateTime;
begin
  Result := AValue - ANumberOfDays;
end;

function DecHour(const AValue: TDateTime;
  const ANumberOfHours: Int64): TDateTime;
begin
  Result := ((AValue * HoursPerDay) - ANumberOfHours) / HoursPerDay;
end;

function DecMinute(const AValue: TDateTime;
  const ANumberOfMinutes: Int64): TDateTime;
begin
  Result := ((AValue * MinsPerDay) - ANumberOfMinutes) / MinsPerDay;
end;

function DecSecond(const AValue: TDateTime;
  const ANumberOfSeconds: Int64): TDateTime;
begin
  Result := ((AValue * SecsPerDay) - ANumberOfSeconds) / SecsPerDay;
end;
procedure DoAllThis;
var Data: TDateTime; Licznik: Integer;  {tą procedurę regularnie wywołuje Timer}
begin
  Data := EncodeDateTime(2012, 07, 01, 0, 0, 0, 0);

  Labela.Clear;

  Labela.Add('Do wakacji zostało ');

  Licznik := DaysBetween(Data, Now);
  Labela.Add(IntToStr(Licznik) + ' ' + GetDateString(Licznik, fDays) + ', ');
  Data := DecDay(Data, Licznik);

  Licznik := HoursBetween(Data, Now);
  Labela.Add(IntToStr(Licznik) + ' ' + GetDateString(Licznik, fHours) + ', ');
  Data := DecHour(Data, Licznik);

  Licznik := MinutesBetween(Data, Now);
  Labela.Add(IntToStr(Licznik) + ' ' + GetDateString(Licznik, fMinutes) + ' i ');
  Data := DecMinute(Data, Licznik);

  Licznik := SecondsBetween(Data, Now);
  Labela.Add(IntToStr(Licznik) + ' ' + GetDateString(Licznik, fSeconds));

end;

Umieszczam tutaj, bo może komuś się przyda.

1

Mam teraz więcej czasu więc wytłumaczę wszystko i dam przykładowy kod.

Zadanie wydaje się być proste ale tak naprawdę diabeł tkwi w środku. Spowodowane jest to pokręconą arytmetyką narzuconą sposobem w jakim reprezentujemy datę. Po pierwsze trzeba się liczyć z tym iż dostaniemy wyniki dziwne. Dla przykładu:
28.02.2011 - 31.03.2011 da w wyniku 1 miesiąc i 3 dni, gdyż pierwszy miesiąc mija 28.03.2011 a do 31.03.2011 zostają jeszcze 3 dni
28.02.2011 - 1.05.2011 da w wyniku 1 miesiąc i 1 dzień, gdyż do końca lutego jest 1 dzień i później mamy cały pełny miesiąc. Czyli mniej niż poprzednio!

Tradycyjna arytmetyka niestety zawodzi. Nie możemy sobie po kolei wywołać (Days/Monts/Yers)Between i zwyczajnie skumulować wynik. Trzeba trochę pokombinować. Tak więc przejdźmy może do implementacji (uwaga, dawno nie pisałem w tym języku, poniższego kodu nie kompilowałem):

procedure SwapDateTime(var A, B: TDateTime);
var
  tmp: TDateTime;
begin
  tmp := B;
  B := A;
  A := tmp;
end;

procedure GetDateTimeSpan(DateTimeFrom, DateTimeTo: TDateTime; var Years, Months, Days, Hours, Minutes, Seconds: Word);
var
  TimeDiff : TDateTime;
  MonthDiff: Integer;
  MSec: Word;
const
  MONTHS_IN_YEAR = 12;
  MAX_DAYS_DIFF = 30; // największy możliwy wynik ilości dni
begin
  { w zasadzie tylko jeden kierunek ma sens więc ustawmy datę mniejszą jako pierwszą }
  if DateTimeFrom > DateTimeTo then SwapDateTime(DateTimeFrom, DateTimeTo);

  { sporą robotę odwala funkcja MonthsBetween, obliczamy lata i miesiące ZANIM zaczniemy
    modyfikować daty na potrzeby algorytmu gdyż ona zwróci od razu prawidłowy wynik }
  MonthDiff := MonthsBetween(DateTimeFrom, DateTimeTo);
  Years := MonthDiff div MONTHS_IN_YEAR; // na szczęście rok to zawsze 12 miesięcy
  Months := MonthDiff mod MONTHS_IN_YEAR;

  { Aby dotrzeć do ilości dni musimy przetworzyć czas dnia, obliczmy więc ich różnicę. Jeśli czas dnia
    końcowego nie osiągnął jeszcze czasu dnia początkowego, wtedy liczmy czas do końca dnia
    początkowego + czas od początku dnia końcowego. Obydwa przypadki obejmie następująca konstrukcja. }
  TimeDiff := TimeOf(DateTimeTo - DateTimeFrom);
  { obliczony czas zamieniamy na składniki }
  DecodeTime(TimeDiff, Hours, Minutes, Seconds, MSec);

  { TU UWAGA: licząc dni możemy powiedzieć, że jeśli czas dnia "DateTimeTo" jeszcze nie osiągnął
    czasu dnia "DateTimeFrom" to data końcowa znajduje się dzień wcześniej, gdyż odpowiednia
    godzina dnia nie została jeszcze osiągnięta. Dlatego odejmujemy jeden dzień od daty końcowej
    i nie bierzemy już czasu dnia pod uwagę }
  if TimeOf(DateTimeTo) < TimeOf(DateTimeFrom) then DateTimeTo := DecDay(DateTimeTo);
  { Teraz kolejne bardzo pomocne funkcje - DayOfTheMonth i DaysInMonth,
     UWAGA: przed ich użyciem musieliśmy wziąć pod uwagę czas dnia, teraz go możemy
     zignorować. Inaczej na nie wiele by się te funkcje zdały.
     Obliczamy różnicę dnia miesiąca. }
  Days := DayOfTheMonth(DateTimeTo) - DayOfTheMonth(DateTimeFrom);
  { W przypadku gdy dni następują po sobie nie ma problemu, dostaniemy wynik z przedziału 0..30;
    w przeciwnym razie wynik będzie "ujemny" t.j. wartość będzie po prostu poza zakresem.
    Będzie tak, gdy dzień miesiąca daty początkowej nie osiągnie jeszcze dnia miesiąca daty
    końcowej. Wtedy, podobnie jak przy czasie dnia, obliczamy ilość dni do końca miesiąca
    początkowego + ilość dni od początku miesiąca końcowego. Zrobimy to za jednym zamachem
    dodając do wyniku ilość dni miesiąca początkowego. Zauważ, że nie musimy tego
    dodanego miesiąca uwzględniać już w wyniku ilości miesięcy, funkcja MonthsBetween na prawdę
    sporo odwaliła. }
  if not Days in [0..MAX_DAYS_DIFF] then Inc(Days, DaysInMonth(DateTimeFrom));
end;
0

Procedura pochodzi ze strony: http://www.delphipages.com/forum/showthread.php?t=141632

 procedure DateDif(Date1, Date2: TDateTime; var Y, M, D: word);
    var
      d1, d2 : TDateTime;
    begin
      Y := 0;
      M := 0;
      D := 0;
      if Trunc(Date1) < Trunc(Date2) then begin
        d1 := Trunc(Date1);
        d2 := Trunc(Date2);
      end else begin
        d1 := Trunc(Date2);
        d2 := Trunc(Date1);
      end;

      while Trunc(IncMonth(d1)) <= Trunc(d2) do begin
        d1 := IncMonth(d1);
        Inc(M);
      end;

      while Trunc(d1) +1 <= Trunc(d2) do begin
        d1 := d1 +1;
        Inc(D);
      end;

      Y := M div 12;
      M := M -(Y *12);
    end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Y, M, D : word;
begin
  DateDif(DateTimePicker1.DateTime, DateTimePicker2.DateTime, Y, M, D);
  YearsEdit.Text   := IntToStr(Y);
  MonthsEdit.Text  := IntToStr(M);
  DaysEdit.Text    := IntToStr(D);
end;

1 użytkowników online, w tym zalogowanych: 0, gości: 1