AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

StringToDateTime macht murx

Ein Thema von Sergej_Molotov · begonnen am 4. Sep 2012 · letzter Beitrag vom 4. Sep 2012
Antwort Antwort
Sergej_Molotov

Registriert seit: 18. Jul 2006
61 Beiträge
 
Delphi 11 Alexandria
 
#1

StringToDateTime macht murx

  Alt 4. Sep 2012, 15:01
Hallo Zusammen,

ich bin gerade über was gestolpert, was ich mir nicht so richtig erklären kann. Folgender Beispielcode:

Delphi-Quellcode:
procedure TDlg_Main.BtnTestClick(Sender: TObject);
var
  tmp : Tdatetime;
  aFormat : TFormatSettings;
begin
  tmp := StrToDateTime('21.12.1993 00:09:59'); //Liefert 21.12.1993 00:09:59 und OK

  GetLocaleFormatSettings(LOCALE_SYSTEM_DEFAULT, aFormat);
  aFormat.ShortDateFormat := 'yyyy-mm-dd';
  aFormat.ShortTimeFormat := 'hh:nn:ss';

  aFormat.DateSeparator := '-';
  aFormat.TimeSeparator := ':';

  tmp := StrToDateTime('1993-12-21 00:10:00', aFormat); // Liefert 21.12.1993 00:09:59 NICHT OK

  tmp := VarToDateTime('1993-12-21 00:10:00')// Liefert 21.12.1993 00:09:59 NICHT OK
end;
Bediene ich die Funktion falsch oder was ist hier los?
Thomas
  Mit Zitat antworten Zitat
Benutzerbild von Bummi
Bummi

Registriert seit: 15. Jun 2010
Ort: Augsburg Bayern Süddeutschland
3.470 Beiträge
 
Delphi XE3 Enterprise
 
#2

AW: StringToDateTime macht murx

  Alt 4. Sep 2012, 15:17
unter XE bei mir nicht nachvollziebar
Thomas Wassermann H₂♂
Das Problem steckt meistens zwischen den Ohren
DRY DRY KISS
H₂ (wenn bei meinen Snipplets nichts anderes angegeben ist Lizenz: WTFPL)
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.184 Beiträge
 
Delphi 12 Athens
 
#3

AW: StringToDateTime macht murx

  Alt 4. Sep 2012, 15:29
Bei mir XE2/XE3 funktioniert es.

Da du die RTL-Codes besitzt, kannst du aber mal StrToDateTime debuggen und schauen, ob du den Rechenfehler findest.


Was du aber "falsch" machst, ist das dir Delphi deine Variable wegoptimieren könnte, da du sie nie ausliest, sondern immer nur befüllst.
Delphi-Quellcode:
procedure TDlg_Main.BtnTestClick(Sender: TObject);
var
  tmp : Tdatetime;
  aFormat : TFormatSettings;
begin
  tmp := StrToDateTime('21.12.1993 00:09:59'); //Liefert 21.12.1993 00:09:59 und OK
  if temp = 0 then ; // billiger Hack, im Delphi daran zu hindern diese Variable wegoptimieren zu können (den ganze "nutzlose" IFs optimieren die immernoch nicht weg)

  ...

  tmp := StrToDateTime('1993-12-21 00:10:00', aFormat); // Liefert 21.12.1993 00:09:59 NICHT OK
  if temp = 0 then ;

  tmp := VarToDateTime('1993-12-21 00:10:00')// Liefert 21.12.1993 00:09:59 NICHT OK
  if temp = 0 then ;
end;
[add]
Statt dem IF mal folgendes verwenden, welches zusätzlich noch Millisekunden, Mikrosekunden und sinnloser Weise ein paar Nanosekunden anzeigt.
ShowMessage(FormatDateTime('dd.mm.yyyy hh:mm:ss.zzz', tmp) + StringReplace(Format('.%7.3n', [Frac(tmp * MSecsPerDay) * 1000]), ' ', '0', [rfReplaceAll]));
$2B or not $2B
  Mit Zitat antworten Zitat
Sergej_Molotov

Registriert seit: 18. Jul 2006
61 Beiträge
 
Delphi 11 Alexandria
 
#4

AW: StringToDateTime macht murx

  Alt 4. Sep 2012, 15:49
Delphi-Quellcode:
function TryStrToDateTime(const S: string; out Value: TDateTime): Boolean;
var
  Pos: Integer;
  NumberPos: Integer;
  BlankPos, LastBlankPos, OrigBlankPos: Integer;
  LDate, LTime: TDateTime;
  Stop: Boolean;
begin
  Result := True;
  Pos := 1;
  LTime := 0;

  // jump over all the non-numeric characters before the date data
  ScanToNumber(S, Pos);

  // date data scanned; searched for the time data
  if ScanDate(S, Pos, LDate) then
  begin
    // search for time data; search for the first number in the time data
    NumberPos := Pos;
    ScanToNumber(S, NumberPos);

    // the first number of the time data was found
    if NumberPos < Length(S) then
    begin
      // search between the end of date and the start of time for AM and PM
      // strings; if found, then ScanTime from this position where it is found
      BlankPos := Pos - 1;
      LastBlankPos := BlankPos;
      Stop := False;
      while (not Stop) and (BlankPos < NumberPos) do
      begin
        // blank was found; scan for AM/PM strings that may follow the blank
        if (BlankPos > 0) and (BlankPos < NumberPos) then
        begin
          Inc(BlankPos); // start after the blank
          OrigBlankPos := BlankPos; // keep BlankPos because ScanString modifies it
          Stop := ScanString(S, BlankPos, TimeAMString) or
                  ScanString(S, BlankPos, 'AM') or
                  ScanString(S, BlankPos, TimePMString) or
                  ScanString(S, BlankPos, 'PM');

          // ScanString jumps over the AM/PM string; if found, then it is needed
          // by ScanTime to correctly scan the time
          BlankPos := OrigBlankPos;
        end
        // no more blanks found; end the loop
        else
          Stop := True;

        // search of the next blank if no AM/PM string has been found
        if not Stop then
        begin
          LastBlankPos := BlankPos;
          BlankPos := PosEx(' ', S, LastBlankPos);
        end;
      end;

      // loop was forcely stopped; check if AM/PM has been found
      if Stop then
        // AM/PM has been found; check if it is before or after the time data
        if BlankPos > 0 then
          if BlankPos < NumberPos then // AM/PM is before the time number
            Pos := BlankPos
          else
            Pos := NumberPos // AM/PM is after the time number
        else
          Pos := NumberPos
      // the blank found is after the the first number in time data
      else
        Pos := NumberPos;

      // get the time data
      Result := ScanTime(S, Pos, LTime);

      // time data scanned with no errors
      if Result then
        if LDate >= 0 then
          Value := LDate + LTime // Hier wird 21.12.1993 + 30.12.1899 00:10:00 = 21.12.1993 00:09:59
        else
          Value := LDate - LTime
    end
    // no time data; return only date data
    else
      Value := LDate;
  end
  // could not scan date data; try to scan time data
  else
    Result := TryStrToTime(S, Value)
end;
Sieht der Source bei euch auch so aus???
Thomas
  Mit Zitat antworten Zitat
Sergej_Molotov

Registriert seit: 18. Jul 2006
61 Beiträge
 
Delphi 11 Alexandria
 
#5

AW: StringToDateTime macht murx

  Alt 4. Sep 2012, 16:03
Um er kurz zu machen, kann man bei mir mit foldendem Code auf eine Falschberechnung auslösen:

Delphi-Quellcode:
var
  tmp : TDateTime;
  tmp2: TDateTime;
begin
  tmp := StrToDateTime('21.12.1993');
  tmp2 := StrToDateTime('30.12.1899 00:10:00');
  if (tmp= 0) or (tmp2 = 0) then;
  tmp := tmp + tmp2; // 21.12.1993 00:09:59
end

Ich habe das ganze jetzt mal mit nem ShowMessage(DateTimeToStr(tmp)) ausgegeben lassen und siehe da, der Debugger zeigt mir einen falschen Wert an. Die Ausgabe des Dialogs ist korrekt.

Fazit: Traue nie einem Debugger. Danke für eure Unterstützung
Thomas

Geändert von Sergej_Molotov ( 4. Sep 2012 um 16:13 Uhr)
  Mit Zitat antworten Zitat
Furtbichler
(Gast)

n/a Beiträge
 
#6

AW: StringToDateTime macht murx

  Alt 4. Sep 2012, 19:25
'korrekt' ist so ne Sache, denn die TDateTime-Werte sind Floating-Point und Du hast es hier mit einem klassischen FP-Rundungsproblem zu tun.

Die Zeit '10:00:00' wird als 10/24 = 5/6 repräsentiert, was eine rationale Zahl mit unendlicher Periode ist und somit nicht 100% korrekt als floating point darzustellen ist. Hinzu kommt noch, das viele einfache Dezimalzahlen auch nicht sauber als FP darzustellen sind. Blöde Sache.

Der TDateTime-Wert ist also garantiert nicht exakt '10 Uhr morgens', sondern irgendwie ein paar Nanomilliwasweissichsekunden geringer. Der Debugger wird nun so blöd programmiert sein, und irgend eine Frickelformatierung verwenden (jedenfalls wohl nicht 'DateTimeToStr') und deshalb etwas anderes anzeigen. Aber auch die DateTimeToStr-Variant zeigt nicht genau den richtigen Wert an, sondern rundet (aka schummelt) auch ein wenig. Nur eben so, das Du zufrieden bist. Und darauf kommts ja an
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.184 Beiträge
 
Delphi 12 Athens
 
#7

AW: StringToDateTime macht murx

  Alt 4. Sep 2012, 20:42
Fazit: Time-zu-String-Funktionen runden vorwiegend ab.

Es wäre ja fatal, wenn man nur "runden" würde, denn dann würde die Anzeige garnicht mehr stimmen.

z.B. mit der "extrem" drastischen Variante ab 0,5 Stunden die Stundenanzeige aufrunden, denn dann würde aus 4:35 plötzlich 5 Uhr werden.

Auch wäre es fahrlässig, wenn man z.B. im StringToDateTime 'ne halbe Millisekunde aufrechnen würde, damit die Anzeigen stimmen, denn wenn man mit Zeiten rechnet, dann könnte sich diese "zusätzliche" Zeit sehr schnell aufschaukeln.

Was man aber machen könnte, wenn man z.B. 'ne halbe oder 10-tel Millisekunde nur für die Anzeige aufrechnet, womit das Abrundungsverhalten sich dort nicht derart negativ zeigt.

Bruchteil der Millisekunde deswegen, weil TDateTime auf eine Genauigkeit von einer Millisekunde ausgelegt ist.
$2B or not $2B

Geändert von himitsu ( 4. Sep 2012 um 21:06 Uhr)
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 21:12 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz