Odliczanie do daty w innym formacie.

0

Witam,
chciałbym zrobić odliczanie do konkretnej daty podane w godzinach minutach i sekundach. Mam podana date w takim formacie:
Fri Oct 25 8:00
W jaki sposób zrobić odliczanie od teraz do tej konkretnej daty? Mógłby mi ktoś pomóc?
pozdrawiam!

3

Temat ewidentnie pasuje do Newbie, ale nie przenoszę. Bo później i tak tamten dział zniknie. A poziom wiedzy potrzebnej do rozwiązania "problemu" autora wątku, śmiało można określić jako podstawowy. I tak później go zgodnie z planami oznaczymy gdy będzie to już dostępne dla Moderatorów/Adminów. Co do pobrania danych z tak podanego ciągu, o jaki pytasz to są również do tego odpowiednie funkcje formatujące. Więcej informacji znajdziesz w google na wielu stronach. Jak rownież w pomocy do IDE. Trzeba tylko chcieć poszukać samemu.

uses
  DateUtils;

var
  EndDate : TDateTime;

procedure TForm1.FormCreate(Sender : TObject);
begin
  Timer1.Enabled := False;
  Timer1.Interval := 1000;
end;

procedure TForm1.Button1Click(Sender : TObject);
begin
  EndDate := EncodeDateTime(2013, 10, 25, 8, 0, 0, 0);
  Timer1.Enabled := True;
end;

procedure TForm1.Timer1Timer(Sender : TObject);
var
  ADate : TDateTime;
begin
  ADate := IncSecond(Now, -1);
  Caption := DateTimeToStr(ADate);
  if ADate >= EndDate then
  begin
    Timer1.Enabled := False;
    ShowMessage('A teraz jazda do kursów i uczyć się podstaw! ;)');
  end;
end;
3

Ale jemu chodzi o to że ma to podane jako string a chyba w Delphi nie da się z takiej daty w "normalny sposób" czyli za pomocą formatowania uzyskać TDateTime (przez to że są nazwy miesięcy i dni tygodnia).
Trzeba się pobawić trochę na piechotę np tak:

procedure TForm1.Button1Click(Sender: TObject);
const
  SMN: array [1..12] of string = ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
                                  'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');
var
  DateTime: TDateTime;
  fs: TFormatSettings;
  i: Integer;
  MyDateTime: string;
begin
  MyDateTime:= 'Fri Oct 25 8:00';
  Delete(MyDateTime, 1, 4); //dzień tygodnia niepotrzebny
  for i:= Low(SMN) to High(SMN) do
  begin
    MyDateTime:= StringReplace(MyDateTime, SMN[i] + ' ', Format('%0.2d/', [i]), [rfReplaceAll]);
  end;
  //tu MyDateTime = 10/25 8:00
  fs.TimeSeparator:= ':';  //ustawiam własne formatowanie daty
  fs.DateSeparator:= '/';
  fs.ShortDateFormat:= 'mm/dd';
  fs.ShortTimeFormat:= 'h:nn';
  DateTime:= StrToDateTime(MyDateTime, fs); //tu masz datę w formacie TDateTime i możesz dodawać odejmować
  ShowMessage(DateTimeToStr(DateTime));
end;
0

dzieki wielkie Panowie :) nikt nie powiedzial, ze nie jestem Newbie :D

0

@kAzek: no ok, myślałem że większym problemem jest odliczanie do daty. Przecież takie rzeczy jak wyłuskanie poszczegółnych rzeczy z tak prostego stringu, toż to proste, jak budowa cepa niemalżę. Oczywiście dałeś idealny przykład. Bo powinno się robić takie rzeczy w oparciu o tablice i jak najprościej. Ale jeśli mamy ludzi uczyć użycia StringReplace, StrToDateTime czy też nawet Pos / Copy lub Delete to bysmy musieli założyć żłobek dla programistów i wibjać jakimś młotem oczywiste oczywiśtości do głów ;) Wtedy google musiano by zamknąć ;)

@karpov: no ok, Newbie czy nie. Korzystnie z Google to żaden wstyd. Teraz zatwierdź rozwiąznie bardziej pomocne. I tutaj niedoskonalość tego, że można zatwierdzić tylko jedno ;) A @kAzek ma ich procentowo więcej, ale ja się nie obrażę ;P

0
kAzek napisał(a)

Ale jemu chodzi o to że ma to podane jako string a chyba w Delphi nie da się z takiej daty w "normalny sposób" czyli za pomocą formatowania uzyskać TDateTime (przez to że są nazwy miesięcy i dni tygodnia).

Ale nic nie stoi na przeszkodzie, aby sobie taką funkcję zaimplementować :]

W Lazarusie w module DateUtils jest co prawda funkcja ScanDateTime, jednak nie jest przystosowana do rozpoznawania dni tygodnia/miesiąca - konwertuje tylko jeśli elementy daty/czasu są cyframi/liczbami;

Jutro pobawię się nieco i spróbuje napisać taki algorytm (razem z obsługą nazw miesięcy) - do południa postaram się go udostępnić.

0

Dzieki wszystkim za pomoc:) Mam jeszcze pytanie. Skorzystalem z Waszych rozwiazan i majac juz 2 daty w formacie TDateTime uzywam DaysBetween() no i zwraca mi wynik np. 5. A ja chcialem uzyskac wynik rowniez w godzinach, minutach i sekundach zeby moc zrobic odliczanie do tej daty. Istnieje jakas funkcja oprocz DayBetween, ktora by to robila?

1

jest DateUtils tutaj masz spis wszystkich funkcji znajdujących się w tym module (szkoda, że po przeprowadzce gdzieś "przyjaźniejszy" spis funkcji zaginął) np. MinuteSpan czy MinutesBetween

0
DaysBetween - zwraca czy liczbę zmiennoprzecinkową w dniach.
Trunc(Frac(DaysBetween)*24) - to będą godziny
Trunc(Frac(DaysBetween*24)*60) - to będą minuty
Trunc(Frac(DaysBetween*24*60)*60) - to będą sekundy
0

Dzięki wszystkim za pomoc:) Problem rozwiązany: wyswietlanie godzin, minut i sekund rozwiazalem w taki sposob:

  godz:= HoursBetween(Now, EndDate);
  godzin.caption:=inttostr(godz mod 60);
  min:=MinutesBetween(Now, EndDate);
  minut.caption:=inttostr(min mod 60);
  sec := SecondsBetween(Now, EndDate);
  sekund.caption:=inttostr(sec mod 60);
4

Trochę późno piszę, ale skoro obiecałem to lepiej późno niż wcale; Wczoraj miałem dłuższą chwilę i napisałem zestaw funkcji do konwersji daty/czasu do łańcucha i z powrotem, ale że dostawca Internetu miał awarię - dopiero teraz mogę napisać;


Całość pisana w Lazarusie, ale zmodyfikowałem kod, tak by działał także pod Delphi7, w którym nie obsługiwany jest cplusplusowaty operator += oraz brak możliwości inicjowania zmienych lokalnych w procedurach/funkcjach;

Nie są one tak wypasione jak FormatDateTime, ale są w pełni odwracalne; Źródła:

Delphi7 - http://4programmers.net/Pastebin/2497
Lazarus - http://4programmers.net/Pastebin/2498

Funkcje InternalDateTimeToStrConv i InternalStrToDateTimeConv są funkcjami wewnętrznymi, które dokonują właściwej kunwersji; Funkcje DateTimeToStrConv i StrToDateTimeConv są gotowe do użytku, a przeładowane dlatego, że różnią się liczbą argumentów - można podać lub nie własny rekord typu TFormatSettings;

Obsługiwane formaty:

Format Opis
Data

D | dzień bez zera wiodącego
DD | dzień z zerem wiodącym
DDD | krótka nazwa dnia (pobrana z TFormatSettings.ShortDayNames)
DDDD | długa nazwa dnia (pobrana z TFormatSettings.LongDayNames)
M | miesiąc bez zera wiodącego
MM | miesiąc z zerem wiodącym
MMM | krótka nazwa miesiąca (pobrana z TFormatSettings.ShortMonthNames)
MMMM | długa nazwa miesiąca (pobrana z TFormatSettings.LongMonthNames)
YYYY | rok w formacie czterocyfrowym
Czas

H | godzina bez zera wiodącego
HH | godzina z zerem wiodącym
N | minuta bez zera wiodącego
NN | minuta z zerem wiodącym
S | sekunda bez zera wiodącego
SS | sekunda z zerem wiodącym
Z | milisekunda w formacie jednocyfrowym
ZZZ | milisekunda w formacie trzycyfrowym
AM/PM | flaga oznaczająca zegar 12-godzinny


Plusy:

  • obsługuje poprawnie liczbowe formaty,
  • obsługuje poprawnie skrócone i pełne nazwy dni i miesięcy,
  • poprawnie konwertuje łańcuch na datę/czas jeśli w łańcuchu jest tylko skrócona/pełna nazwa miesiąca bez jej liczbowego odpowiednika (np. gdy łańcuch zawiera jedynie 22 październik 2013 - miesiąc zostanie poprawnie odczytany),
  • poprawnie konwertuje godzinę na format 12-godzinnego zegara,
  • jest zupełnie odwracalna (pod warunkiem użycia jedynie ww. w tabelce formatów),
  • poprawnie konwertuje date/czas na podstawie własnego typu TFormatSettings w przeciwieństwie do RTLowskich funkcji;

Minusy:

  • brak obsługi dwucyfrowego formatu roku (nie chciało mi się już bawić z TwoDigitYearCenturyWindow),
  • brak obsługi czasów wschodnioazjatyckich (cokolwiek to znaczy),
  • każdy format musi być oddzielony od kolejnego co najmniej jednym zanakiem "nieformatowalnym" (nie może być np. HHNN, trzeba je oddzielić, np. spacją HH NN lub separatorem HH:NN) - to chyba normalne jest, bo RTLowskie funkcje najprawdopodobniej także nie poradzą sobie z połączonymi formatami (nie sprawdzałem),
  • flaga AM/PM nie może stać na początku łańcucha (może być przed formatami czasu, ale nie na początku łańcucha);

Oczywiście można te funkcje wzbogacić o obsługę dodatkowych formatów (pozostawiłem casey jakby ktoś chciał sobie coś dodać);


Przykład użycia:

const
  MASK_STRING = AnsiString('D DD DDD DDDD M MM MMM MMMM YYYY H HH N NN S SS Z ZZZ AM/PM');
var
  dtValue: TDateTime;
  strValue: AnsiString;
begin
  dtValue := EncodeDateTime(2013, 10, 21, 21, 14, 52, 98);

  { RTL date time to string }
  strValue := FormatDateTime(MASK_STRING, dtValue);
  WriteLn('"', strValue, '"');
  { date time to string }
  strValue := DateTimeToStrConv(dtValue, MASK_STRING);
  WriteLn('"', strValue, '"');
  { string to date time & date time to string }
  dtValue := StrToDateTimeConv(strValue, MASK_STRING);
  strValue := DateTimeToStrConv(dtValue, MASK_STRING);
  WriteLn('"', strValue, '"');
  { finally }
  ReadLn;
end.

W pierwszej linijce wyświetlony zostanie rezultat RTLowskiej funkcji FormatDateTime, w drugiej rezultat funkcji DateTimeToStrConv, a w trzeciej StrToDateTimeConv (podwójna konwersja własnymi funkcjami, żeby znów uzyskać łańcuch gotowy do wyświetlenia) - wartości w drugiej i trzeciej linii to rezultaty działania własnych funkcji, więc one musza być takie same żeby mówić o odwracalności;

Output (Lazarus):

"21 21 Pn poniedzia│ek 10 10 pač pačdziernik 2013 9 09 14 14 52 52 98 098 PM"
"21 21 Pn poniedzia│ek 10 10 pač pačdziernik 2013 9 09 14 14 52 52 98 098 PM"
"21 21 Pn poniedzia│ek 10 10 pač pačdziernik 2013 9 09 14 14 52 52 98 098 PM"

Output (Delphi7):

"21 21 Pn poniedzia│ek 10 10 pač pačdziernik 2013 21 09 14 14 52 52 98 098 PM"
"21 21 Pn poniedzia│ek 10 10 pač pačdziernik 2013 9 09 14 14 52 52 98 098 PM"
"21 21 Pn poniedzia│ek 10 10 pač pačdziernik 2013 9 09 14 14 52 52 98 098 PM"

W przypadku Delphi7 funkcja FormatDateTime z RTL Delphi7 nie poradziła sobie najlepiej;


Obie funkcje (DateTimeToStrConv i StrToDatTimeConv) testowałem na domyślnym TFormatSettings, jak i na własnym i w obu przypadkach po modyfikacji rekordu działa bezbłędnie (czego o FormatDateTime powiedzieć już nie można);

Nie wiem dokładnie dlaczego funkcja ScanDateTime z Lazarusa nie potrafi odwrócić łańcucha, jeśli zawiera np. nazwę dnia czy miesiąca, w każdym razie moja funkcja konwersji łańcucha na TDateTime do porównywania łańcuchów nazw miesięcy wykorzystuje funkcję CompareStr, która robi to jak widać poprawnie;

Kod wklejam do Pastebin bo jest długi; Być może przyda Ci się @karpov, a jeśli nie, to może kiedyś kiedyś ktoś trafi do tego wątku z Google. Enjoy :]

0

wow super praca:) wielkie dzięki, na pewno wypróbuje to rozwiązanie :)

0

Jeśli masz źródła RTL z Delphi (ja niestety nie mam) to możesz sobie zobaczyć jak wygląda oryginalna wersja funkcji FormatDateTime i nieco rozbudować mój kod, ale najważniejsze rzeczy są zaimplementowane; No i jest w pełni odwracalna, a to najważniejsze :]


EDIT:

Nie piszę nowego postu, jednak z racji tej, że poprawiłem oba algorytmy konwersji muszę stwierdzić, że znalazłem kilka niedociągnięć w kodzie, przez co dawał mniejsze możliwości;

Dla zainteresowanych (a raczej tych, którym algorytmy się spodobały i zaplusowali tamten post, czyli @karpov, @olesio, @kAzek i @babubabu) przedstawiam poprawiony kod wszystkich funkcji, zarówno z Lazarusa jak i Delphi7:

Aby wartość AM/PM mogła stać na początku łańcucha należało w funkcji InternalStrToDateTimeConv poprawić GetFormatInfo i bez względu na zawartość zmiennej chrFormat przypisać do zmiennej chrFormatSep zawartość wskaźnika pchrMaskCurr;

Drugim problemem był warunek obsługujący format A - w starej wersji od razu była sprawdzana i poprawiana godzina, co było błędem, bo jeśli wartość AM/PM była przed formatem godziny - zmienna intHour zawsze była ustawiona na 0 (po wywołaniu procedury InitDateTimeComponents), dlatego też nie działało to prawidłowo;

Rozwiązaniem okazało się dodanie jeszcze dwóch zmiennych - bool12HourClock i boolIsAMHour, które to zostają ustawione w obsłudze formatu A; Ta pierwsza zawsze na True (bo skoro ten format został napotkany to czas na pewno jest podany jako 12-godzinny), a ta druga w zależności od tego czy zmienna strFormatVal jest taka sama jak ASettings.TimeAMString czy ASettings.TimePMString; Jeśli to pierwsze to zostaje ustawiona na True, jeśli nie to na False;

Ostatnim krokiem było obsłużenie zmiennych bool12HourClock i boolIsAMHour już po wyjściu z pętli - wtedy już zmienna intHour posiada przekonwertowaną wartość z łańcucha AValue;

Do listy argumentów dodałem także ADefault: TDateTime po to, aby w razie błędu konwersji zwrócić wartość domyślną; Dlatego też właściwa konwersja dokonywana jest funkcją TryEncodeDateTime i jeśli wystąpi błąd konwersji (funkcja zwróci False) - do rezultatu funkcji zostanie wpisana wartość spod argumentu ADefault;

Dodatkowo jeśli którykolwiek z argumentów AValue lub AMask jest pusty - także zostaje zwrócona wartość domyślna;

Obie przeładowane funkcje konwersji z łańcucha do TDateTime zostały także przystosowane do obsługi wartości domyślnej;


Podsumowując - dzięki tym zabiegom po wywołaniu poniższego kodu:

const
  MASK_STRING = AnsiString('D DD DDD DDDD M MM AM/PM MMM MMMM YYYY H HH N NN S SS Z ZZZ');
var
  dtValue: TDateTime;
  strValue: AnsiString;
begin
  dtValue := EncodeDateTime(2013, 10, 21, 21, 14, 52, 98);

  { RTL date time to string }
  strValue := FormatDateTime(MASK_STRING, dtValue);
  WriteLn('"', strValue, '"');
  { date time to string }
  strValue := DateTimeToStrConv(dtValue, MASK_STRING);
  WriteLn('"', strValue, '"');
  { string to date time & date time to string }
  dtValue := StrToDateTimeConv(strValue, MASK_STRING, 0);
  strValue := DateTimeToStrConv(dtValue, MASK_STRING);
  WriteLn('"', strValue, '"');
  { finally }
  ReadLn;
end.

na ekranie zobaczymy:

"21 21 Pn poniedzia│ek 10 10 PM pač pačdziernik 2013 9 09 14 14 52 52 98 098"
"21 21 Pn poniedzia│ek 10 10 PM pač pačdziernik 2013 9 09 14 14 52 52 98 098"
"21 21 Pn poniedzia│ek 10 10 PM pač pačdziernik 2013 9 09 14 14 52 52 98 098"

czyli jak widać funkcje działają poprawnie - są w pełni odwracalne, a o to w sumie chodziło.

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