Entwickler-Ecke

Grafische Benutzeroberflächen (VCL & FireMonkey) - Problem mit tDateTimePicker


midan23 - So 08.08.04 19:48
Titel: Problem mit tDateTimePicker
In einem meiner Programme habe ich mehrere tDateTime-Komponenten um Ein- und Ausschaltzeiten eingeben zu können. Dabei soll die Ausschaltzeit immer grösser sein als die Einschaltzeit.
Und genau das funktioniert nicht ... Hier ein Beispiel :

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
object Test: TTest
  Left = 217
  Top = 106
  Width = 153
  Height = 94
  Caption = 'Test'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object lEin: TLabel
    Left = 8
    Top = 8
    Width = 73
    Height = 21
    Alignment = taCenter
    AutoSize = False
    Caption = 'Einschaltzeit'
    Layout = tlCenter
  end
  object lAus: TLabel
    Left = 8
    Top = 40
    Width = 73
    Height = 21
    Alignment = taCenter
    AutoSize = False
    Caption = 'Ausschaltzeit'
    Layout = tlCenter
  end
  object tpEin: TDateTimePicker
    Left = 88
    Top = 8
    Width = 49
    Height = 21
    CalAlignment = dtaLeft
    Date = 38207
    Format = 'HH:mm'
    Time = 38207
    DateFormat = dfShort
    DateMode = dmComboBox
    Kind = dtkTime
    ParseInput = False
    TabOrder = 0
    OnChange = tpChange
  end
  object tpAus: TDateTimePicker
    Left = 88
    Top = 40
    Width = 49
    Height = 21
    CalAlignment = dtaLeft
    Date = 38207.0006944444
    Format = 'HH:mm'
    Time = 38207.0006944444
    DateFormat = dfShort
    DateMode = dmComboBox
    Kind = dtkTime
    ParseInput = False
    TabOrder = 1
    OnChange = tpChange
  end
end


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ComCtrls, StdCtrls;

type
  TTest = class(TForm)
    lEin: TLabel;
    lAus: TLabel;
    tpEin: TDateTimePicker;
    tpAus: TDateTimePicker;
    procedure tpChange(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Test: TTest;

implementation

{$R *.dfm}

uses
   dateUtils;

procedure TTest.tpChange(Sender: TObject);
begin
  if tpAus.Time <= tpEin.Time then
    tpAus.Time := incMinute(tpEin.Time);
end;

end.

Weiss jemand, woran das liegen könnte und wie man das Problem beheben könnte ?


midan23 - Mo 09.08.04 08:08

Mittlerweile hab ich nachgeforscht ...
tDateTimePicker verwendet zum speichern des Datums und der Uhrzeit eine Variable vom Typ tDateTime, welcher als double definiert ist.
Der Teil vor dem Komma ist die Anzahl Tage seit dem 30.12.1899, während der Teil nach dem Komma die Uhrzeit enthält (sozusagen als Bruchteil des Tages).

Das Problem mit dem ich seit ein paar Tgaen kämpfe ist also einfach nur ein Rundungsfehler, der bei Fliesskomazahlen unvermeidbar ist ...

Ich hab im Internet nachgeforscht, scheinbar gibt es keine brauchbare Alternative zur Zeiteingabe ... das schreit doch schon fast nach einer neuen Komponente ...


Klabautermann - Mo 09.08.04 08:15

Wie gering sind denn deine Zeituntereschiede? tDateTime kommt noch problemlos mit dem Millisekundenbereich zurecht, Probleme solltest du erst bekommen, wenn du drunter liegst.

Ich kann mir nicht vorstellen, dass es dadran liegt, wie genau äußert sich denn das Problem?

Gruß
Klabautermann


midan23 - Mo 09.08.04 08:59

Wenn man in meinem Programm die Minuten der Startzeit erhöht, bekommt man folgende Werte :
Start - Ende
00:00 - 00:01
00:01 - 00:02
00:02 - 00:02 *
00:03 - 00:04
00:04 - 00:05
00:05 - 00:05 *
00:06 - 00:07
00:07 - 00:08
00:08 - 00:09
00:09 - 00:09 *

Alle mit * markierten Zeilen sollten eigentlich nicht vorkommen ...
Wenn man darüber nachdenkt, gibt es nur vier mögliche Fehlerquellen :
1) Ein Fehler in meinem Sourcecode (Der Fehler ist mit meinem Beispiel-Programm oben nachvollziehbar ...)
2) Rundungsfehler bei Fiesskommazahlen
3) Fehler in "incMinute"
4) Fehler in tDateTimePicker (eher unwahrscheinlich)


Klabautermann - Mo 09.08.04 10:29

Hallo,

ja du hast recht, der Effekt liegt an einem Rundungsfehler oder daran, das der DateTime Picker den Minutenwertanders erhöht als IncMinute.

Ich konnte das Problem nicht rekonstruieren, wenn ich selbstgeschriebene Funktion IncAnotherMinute verwende. ICh hoffe diese ist ein akzeptabler Workaround.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
function IncAnotherMinute(aTime: tTime): tTime;
  var
    aMinute, ahour, aSecond, amSecond : Word;
begin
  DecodeTime(aTime, aHour, aMinute, aSecond, amSecond);
  if (aMinute = 59then begin
    aMinute := 0;
    if aHour = 23 then
      aHour := 0
    else
      inc(aHour);
  end
  else
    inc(aMinute);
  Result := EncodeTime(aHour, aMinute, aSecond, amSecond);
end// IncAnotherMinute


Gruß
Klabautermann


midan23 - Mo 09.08.04 12:03

Funktioniert ! wie eine Eins ! Danke ...
Ich dachte schon, ich müsste dafür eine eigene Komponente entwerfen ...

Hatte mir sogar schon konkrete Gedanken darüber gemacht :
- Speichern der Zeit in getrennten Variablen für Stunden, Minuten und Sekunden
- Möglichkeit die Zeit als "Sekunden seit Mitternacht" zu setzten und zu erhalten
- Erhöhen bzw. erniedrigen um x Stunden, Minuten oder Sekunden unter berücksichtigung von Minimal- bzw Maximal-Werten
- Funktion um die Zeit als Zeichenkette zu erhalten, wobei zwischen 12 und 24-Stundenformat gewählt werden kann. Man kann die Zeichenkette auch mit oder ohne Sekunden erhalten.

Würde ich mir alles zutrauen ... bis auf eins :
Wie kann ich sicherstellen, das der Benutzer eine gültige Zeit eingibt ?

Hab schon versucht, eine eigene Komponente von tCustomEdit bzw tCustomMaskEdit abzuleiten aber die Eingabe auf gültige Werte einzuschränken ist mir noch nicht gelungen ...


Klabautermann - Mo 09.08.04 12:18

Hi,

wenn es mit der Funktion nicht geklappt hätte, währe mein nächster Schritt gewesen die IF abfrage ein wenig unpräziser zu machen. Denn diese hat ja nciht gegriffen, da du ja sonst mindesten 59,x Sekunden differenz haben müstest, du hast aber scheinbar -0,x Sekunden differenz. Eine IF abfrage dieser Art:


Delphi-Quelltext
1:
if (tpAus.Time <= tpEin.Time + NeViertelSekundeOderSo) then                    


sollte das Problem genau so lösen.

Gruß
Klabautermann

PS: Wenn dein Problem gelöst ist, kannst du den Status (des ersten Postings) entsprechend setzen.


Uli Schoch - Mo 09.08.04 15:45

Hi
Mach doch einfach


Quelltext
1:
2:
 if tpAus.Time <= tpEin.Time then  
    tpAus.Time := tpEin.Time + 1/24/60;


So addierst du eine Minute = 1 Tag / 24 h pro Tag / 60 Minute pro Stunde

Gruss
Uli


Klabautermann - Mo 09.08.04 16:43

Hi,
Uli Schoch hat folgendes geschrieben:
So addierst du eine Minute = 1 Tag / 24 h pro Tag / 60 Minute pro Stunde

was wieder zu rundungsfehlern führen kann, welche das Problem ja erst verursachten.

Gruß
Klabautermann


midan23 - Mo 09.08.04 18:55

Die Funktion löst das Problem in diesem Fall, aber bei Fliesskommazahlen kann man Rundungsfehler nie ganz ausschliessen ...

Ich denke, ich nehme die Funktion als vorläufige Lösung und entwickle eine Komponente ... da gibt es dann keine Rundungsfehler mehr ...

Weiterer Vorteil : Es wird meine erste Komponente ... also für mich eine gute Gelegenheit was neues zu lernen ...