Die Funktion MinutesBetween aus DateUtils rechnet merkwürdig

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut

Die Funktion MinutesBetween aus DateUtils rechnet merkwürdig

Beitragvon willi4willi » 29. Jun 2017, 18:07 Die Funktion MinutesBetween aus DateUtils rechnet merkwürdig

Hallo allerseits!

Bei der Verwendung von MinutesBetween aus der unit DateUtils habe ich folgende Merkwürdigkeit:

Sie gibt mir zwischen dem "01.01.2017 00:00:00" und "01.01.2017 01:00:00" sind 59 Minuten zurück. Nehme ich aber den "01.01.2017 01:00:00" und "01.01.2017 02:00:00" sind es 60 Minuten.

Ich dachte immer, jede Stunde des Tages hat 60 Minuten.

Wenn ich einmal nur eine Zeitdifferenz nach dem Jahr 1900 berücksichtigen möchte und mir folgende Funktion schreibe:
Code: Alles auswählen
Function MinutesBetween(const ANow, AThen: TDateTime): Int64;
var Tage : double;
begin
  Tage:=AThen-ANow;
  Result:= TRUNC(Tage*1440);
end;


passiert das gleiche.

Habt ihr eine Idee, wie man die richtige Zeitdifferenz herausbekommt??
Viele Grüße

Willi4Willi

------------
willi4willi
 
Beiträge: 93
Registriert: 1. Nov 2008, 18:06
OS, Lazarus, FPC: Winux (L 1.2.xy FPC 2.6.z) | 
CPU-Target: xxBit
Nach oben

Beitragvon Mathias » 29. Jun 2017, 19:06 Re: Die Funktion MinutesBetween aus DateUtils rechnet merkwü

Folgender Code gibt bei mir wie erwartet 60min .
Code: Alles auswählen
procedure TForm1.Button1Click(Sender: TObject);
var
  a, b: TDateTime;
begin
  a := StrToDateTime('01-01-2017 00:00:00');
  b := StrToDateTime('01-01-2017 01:00:00');
 
  Caption := IntToStr(MinutesBetween(a, b));
end;



Hier ist noch die Funktion von FPC 3.1.1 , sieht diese bei dir gleich aus ?
Code: Alles auswählen
Function MonthsBetween(const ANow, AThen: TDateTime; AExact : Boolean = False): Integer;
 
var
  y, m, d: Word;
 
begin
  if AExact and (ANow >= -DateDelta) and (AThen >= -DateDelta) and
     (ANow <= MaxDateTime) and (AThen <= MaxDateTime) then
    begin
    PeriodBetween(ANow, AThen, y, m, d);
    Result := y*12 + m;
    end
  else
    Result:=Trunc((Abs(DateTimeDiff(ANow,AThen))+HalfMilliSecond)/ApproxDaysPerMonth);
end
Mit Lazarus sehe ich gün
Mit Java und C/C++ sehe ich rot
Mathias
 
Beiträge: 3189
Registriert: 2. Jan 2014, 17:21
Wohnort: Schweiz
OS, Lazarus, FPC: Linux (die neusten Trunc) | 
CPU-Target: 64Bit
Nach oben

Beitragvon wp_xyz » 29. Jun 2017, 19:13 Re: Die Funktion MinutesBetween aus DateUtils rechnet merkwü

Es gab dazu vor einiger Zeit einige Bugfixes - die sind im fpc-Trunk (und kommen evt in fpc 3.04), weshalb Mathias als notorischer trunc-User das richtige Ergebnis erhält.

Das Problem ist das alte Problem mit den Gleitkommazahlen. TDateTime ist intern nichts anderes als Double und hat daher wie jedes andere Gleitkommaformat nur eine bestimmte Anzahl von Nachkommastellen. Da die Einheiten von TDateTime ganze Tage sind, sind Minuten unendlich lange Dezimalzahlen (Divisor 3) und können nicht exakt durch eine Double-Zahl ausgedrückt werden. Durch die Subtraktion kommt nochmals ein Rundungsfehler dazu.

Schau dir das folgende Programm an. Als Differenz, in Minuten ausgedrückt, erhält man 59.999999996507540. Trunc() macht daraus 59. Zwei Stunden früher machen die Rundungsfehler daraus etwas wie 60.000000006984919, wo das Trunc() den richtigen Wert ergibt.

Die Lösung ist immer, die halbe Einheit, auf die man runden möchte, also 0.5, dazuzuaddieren, und erst dann trunc zu nehmen. Damit wird aus 59.999999996507540 der Wert 60.499999..., was nach Trunc() zu den korrekten 60 wird; und der Wert von zwei Stunden früher, 60.000000006984919, wird zu 60.500000..., was nach Trunc() immer noch 60 bleibt. Also:

Code: Alles auswählen
program MinutesBetween_Test;
 
uses
  SysUtils,
  DateUtils;
 
procedure Test(d1, d2: TDateTime);
begin
  WriteLn('d1: ', FormatDateTime('ddddd hh:nn:ss', d1), ' = ', d1);
  WriteLn('d2: ', FormatDateTime('ddddd hh:nn:ss', d2), ' = ', d2);
  WriteLn('MinutesBetween(d1, d2): ', MinutesBetween(d1, d2));
  WriteLn('Differenz der Double-Werte als Minuten: ', (d2 - d1)*60*24);
  WriteLn('Differenz nach Korrektur: ', (d2 - d1)*60*24 + 0.5);
  WriteLn('Differenz nach Korrektur (gerundet): ', trunc((d2 - d1)*60*24 + 0.5));
  WriteLn;
end;
 
begin
  Test(EncodeDateTime(2017, 1, 1, 0, 0, 0, 0), EncodeDateTime(2017, 1, 1, 1, 0, 0, 0));
  Test(EncodeDateTime(2016, 12, 31, 22, 0, 0, 0), EncodeDateTime(2016, 12, 31, 23, 0, 0, 0));
  ReadLn;
end.
wp_xyz
 
Beiträge: 2249
Registriert: 8. Apr 2011, 08:01

Beitragvon Mathias » 29. Jun 2017, 19:21 Re: Die Funktion MinutesBetween aus DateUtils rechnet merkwü

Somit hat willi4willi zufällig sehr dummer Werte erwischt. :wink:

Was noch interessant wäre, wen willi4willi anstelle von 2017 mal 2016 probieren würde.
Mit Lazarus sehe ich gün
Mit Java und C/C++ sehe ich rot
Mathias
 
Beiträge: 3189
Registriert: 2. Jan 2014, 17:21
Wohnort: Schweiz
OS, Lazarus, FPC: Linux (die neusten Trunc) | 
CPU-Target: 64Bit
Nach oben

Beitragvon wp_xyz » 29. Jun 2017, 19:26 Re: Die Funktion MinutesBetween aus DateUtils rechnet merkwü

Ist egal, Es wird immer wieder Werte geben, die falsch ausfallen. Man muss einfach bei der Anwendung von Trunc() wissen, dass das Ergebnis einen Hauch kleiner sein kann als der nächste Integer und damit falsch wird. Die vorgeschlagene Korrektur, 0.5 zu addieren, hilft dagegen.
wp_xyz
 
Beiträge: 2249
Registriert: 8. Apr 2011, 08:01

Beitragvon willi4willi » 29. Jun 2017, 20:39 Re: Die Funktion MinutesBetween aus DateUtils rechnet merkwü

Ja, das ist tatsächlich egal. Auch bei 2016 kommt ein falscher Wert. Das Problem ist die Uhrzeit.
Ich werde mir mal die neuste FPC-Version besorgen und es damit probieren.
Danke für die Tipps!
Viele Grüße

Willi4Willi

------------
willi4willi
 
Beiträge: 93
Registriert: 1. Nov 2008, 18:06
OS, Lazarus, FPC: Winux (L 1.2.xy FPC 2.6.z) | 
CPU-Target: xxBit
Nach oben

Beitragvon Mathias » 29. Jun 2017, 21:24 Re: Die Funktion MinutesBetween aus DateUtils rechnet merkwü

Was ich generell merkwürdig finde, wieso wird die Uhrzeit als Float-Typ verarbeitet und nicht als Integer ?
Ein Integer wäre mit irgendwie logischer, welcher einfach Mirkosekunden zählt.
Mit Lazarus sehe ich gün
Mit Java und C/C++ sehe ich rot
Mathias
 
Beiträge: 3189
Registriert: 2. Jan 2014, 17:21
Wohnort: Schweiz
OS, Lazarus, FPC: Linux (die neusten Trunc) | 
CPU-Target: 64Bit
Nach oben

Beitragvon willi4willi » 29. Jun 2017, 21:25 Re: Die Funktion MinutesBetween aus DateUtils rechnet merkwü

Habe es nun mit der FPC 3.1.1 übersetzt und - Wunderbar! Es funktioniert jetzt richtig.

Danke nochmals.
Viele Grüße

Willi4Willi

------------
willi4willi
 
Beiträge: 93
Registriert: 1. Nov 2008, 18:06
OS, Lazarus, FPC: Winux (L 1.2.xy FPC 2.6.z) | 
CPU-Target: xxBit
Nach oben

Beitragvon wp_xyz » 29. Jun 2017, 21:45 Re: Die Funktion MinutesBetween aus DateUtils rechnet merkwü

Mathias hat geschrieben:Was ich generell merkwürdig finde, wieso wird die Uhrzeit als Float-Typ verarbeitet und nicht als Integer ?
Ein Integer wäre mit irgendwie logischer, welcher einfach Mirkosekunden zählt.

Damit bräuchte man aber zwei verschiedene Variablen für Datum und Uhrzeit. TDateTime benutzt dagegen den ganzzahligen Anteil als Tage ab einem bestimmten Stichtag und den Gleitkommaanteil als Uhrzeit innerhalb dieses Tages: 0.0 = Mitternacht, 0.25 = 1/4 Tag = 6 Uhr, 0.5 = 1/2 Tag = 12 Uhr Mittag, etc. Damit kann man mit Datums/Zeit-Angaben rechnen wie mit anderen Zahlen.
wp_xyz
 
Beiträge: 2249
Registriert: 8. Apr 2011, 08:01

Beitragvon m.fuchs » 29. Jun 2017, 22:15 Re: Die Funktion MinutesBetween aus DateUtils rechnet merkwü

wp_xyz hat geschrieben:
Mathias hat geschrieben:Was ich generell merkwürdig finde, wieso wird die Uhrzeit als Float-Typ verarbeitet und nicht als Integer?

Damit bräuchte man aber zwei verschiedene Variablen für Datum und Uhrzeit.


Nicht unbedingt: Unixtime bzw. Ticks.
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de
m.fuchs
 
Beiträge: 1670
Registriert: 22. Sep 2006, 18:32
Wohnort: Berlin
OS, Lazarus, FPC: Winux (L 1.6, FPC 3.0) | 
CPU-Target: x86, x64, arm
Nach oben

Beitragvon Mathias » 29. Jun 2017, 22:22 Re: Die Funktion MinutesBetween aus DateUtils rechnet merkwü

Nicht unbedingt: Unixtime bzw. Ticks.

Ich wusste doch, das es sowas gibt. Als ich Anfangs mit Lazarus und Now etwas machen wollte, wurde ich nicht schlau, bis ich merkte, das es ein Float-Typ ist. :roll:
Mit Lazarus sehe ich gün
Mit Java und C/C++ sehe ich rot
Mathias
 
Beiträge: 3189
Registriert: 2. Jan 2014, 17:21
Wohnort: Schweiz
OS, Lazarus, FPC: Linux (die neusten Trunc) | 
CPU-Target: 64Bit
Nach oben

Beitragvon wp_xyz » 29. Jun 2017, 22:51 Re: Die Funktion MinutesBetween aus DateUtils rechnet merkwü

m.fuchs hat geschrieben:Nicht unbedingt: Unixtime bzw. Ticks.

Natürlich. Meine Antwort bezog sich darauf, dass Mathias von "Uhrzeit" gesprochen hat.

Ticks kannte ich noch nicht. Aber das demonstriert, wie groß Int64 sein kann: 10000 Jahre mit 100 Nanosekunden Auflösung, und dann ist noch ein Faktor 3 Reserve!
wp_xyz
 
Beiträge: 2249
Registriert: 8. Apr 2011, 08:01

Beitragvon marcov » 30. Jun 2017, 11:20 Re: Die Funktion MinutesBetween aus DateUtils rechnet merkwü

m.fuchs hat geschrieben:
wp_xyz hat geschrieben:
Mathias hat geschrieben:Was ich generell merkwürdig finde, wieso wird die Uhrzeit als Float-Typ verarbeitet und nicht als Integer?

Damit bräuchte man aber zwei verschiedene Variablen für Datum und Uhrzeit.


Nicht unbedingt: Unixtime bzw. Ticks.


Unixtime hat aber eine feste 1s Präzision, was etwas ungenau ist für viele Applikationen.
marcov
 
Beiträge: 999
Registriert: 5. Aug 2008, 08:37
Wohnort: Eindhoven (Niederlande)
OS, Lazarus, FPC: Windows ,Linux,FreeBSD,Dos (L trunk FPC trunk) | 
CPU-Target: 32/64,PPC(+64), ARM
Nach oben

Beitragvon Timm Thaler » 1. Jul 2017, 09:28 Re: Die Funktion MinutesBetween aus DateUtils rechnet merkwü

Dafür ist eine Sekunde auch eine Sekunde...

Das heisst aber auch, dass mit DateTime die üblichen Probleme mit Float-Vergleichen auftreten. Etwas wie if Now = wert then sollte man vermeiden, weil es sein kann, dass Now nie gleich wert ist.
Timm Thaler
 
Beiträge: 430
Registriert: 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.6 FPC3.0.0, Raspbian Jessie Laz1.6 FPC3.0.0 | 
CPU-Target: Raspberry Pi 3
Nach oben

Beitragvon Mathias » 1. Jul 2017, 19:48 Re: Die Funktion MinutesBetween aus DateUtils rechnet merkwü

Etwas wie if Now = wert then sollte man vermeiden, weil es sein kann, dass Now nie gleich wert ist.

Bei Float-Vergleichen mache ich immer ein <= oder >= .
Bei Ganzzahlen dies auch ein Vorteil sein.
Mit Lazarus sehe ich gün
Mit Java und C/C++ sehe ich rot
Mathias
 
Beiträge: 3189
Registriert: 2. Jan 2014, 17:21
Wohnort: Schweiz
OS, Lazarus, FPC: Linux (die neusten Trunc) | 
CPU-Target: 64Bit
Nach oben

» Weitere Beiträge siehe nächste Seite »
Nächste

Zurück zu Freepascal



Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 2 Gäste

porpoises-institution
accuracy-worried