UTC-Zeit in lokale Zeit bei Verwendung von TDatasource

Rund um die LCL und andere Komponenten
Antworten
Benutzeravatar
willi4willi
Lazarusforum e. V.
Beiträge: 170
Registriert: Sa 1. Nov 2008, 18:06
OS, Lazarus, FPC: Lazarus 3.8 FPC 3.2.2 x86_64-win64-win32/win64 x86_64-linux-gtk2
CPU-Target: i386, win64, arm

UTC-Zeit in lokale Zeit bei Verwendung von TDatasource

Beitrag von willi4willi »

Hallo,
am 30. 3. ist wieder die Umstellung auf Sommerzeit - für mich immer ein Problem.

Auf einem zentralen Datenbankserver gibt es Server-Programme, die rund um die Uhr laufen und von verschiedenen PCs greifen die Clients auf diese Daten zu.
Die DateTime-Felder haben im Moment die lokale Zeit. Das ist auch in Ordnung, solange nicht auf Sommerzeit hin und hergeschalten wird, denn dann fehlt eine Stunde oder es ist eine zuviel.
Ein weiteres Problem ist der Zugriff auf die Datenbank, wenn z.B. aus der Ferne z.B. England oder Russland darauf zugeriffen werden soll, denn diese haben ja eine andere lokale Zeit.

Darum meine Idee, dass die Serverprogramme grundsätzlich nur die UTC-Zeit in die Datenbank schreiben und die Clients beim Anzeigen von DateTime diese in die lokale Zeit vorher wandeln.

Also anstatt von:

Code: Alles auswählen

  Edit1.Text:=DateTimeToStr(Query1.FieldByName('MeinZeitFeld').AsDateTime);
verwende ich:

Code: Alles auswählen

  Edit1.Text:=DateTimeToStr(UTC2local(Query1.FieldByName('MeinZeitFeld').AsDateTime));
Doch wie mache ich das, wenn ich eine TDataSource-Komponente und TDBEdit verwende? Gibt es da schon eine Eigenschaft, die man setzen muss um diese Umwandlung vorzunehmen? Oder kennt jeman einen einfachen Trick?

Wenn man ein Formular mit über 50 Felder hat, dann ist es müßig ohne die Data-Controls zu arbeiten.

Viele Grüße

Willi4Willi
 

Viele Grüße

Willi4Willi

------------

wp_xyz
Beiträge: 5142
Registriert: Fr 8. Apr 2011, 09:01

Re: UTC-Zeit in lokale Zeit bei Verwendung von TDatasource

Beitrag von wp_xyz »

TField hat m.E. Ereignisse OnGetText und OnSetText, die aufgerufen werden, wenn das Feld aus der Datenbank ausgelesen/reingeschrieben wird. Da könntest du doch in dem einen Fall die Stunde dazuzählen, und im anderen Fall abziehen. Damit wären alle Controls, die mit Query1 verknüpft sind, bedient.

Benutzeravatar
willi4willi
Lazarusforum e. V.
Beiträge: 170
Registriert: Sa 1. Nov 2008, 18:06
OS, Lazarus, FPC: Lazarus 3.8 FPC 3.2.2 x86_64-win64-win32/win64 x86_64-linux-gtk2
CPU-Target: i386, win64, arm

Re: UTC-Zeit in lokale Zeit bei Verwendung von TDatasource

Beitrag von willi4willi »

Danke! So hatte ich das auch gemacht.

Das müsste ich dann aber mit jedem TDateTimeField tun:

Code: Alles auswählen

 
 
procedure TForm1.FeldGetText(Sender: TField; var aText: string; DisplayText: Boolean);
begin
  if sender.DataType=ftDateTime then
  aText := DateTimeToStr(UTC2Local(Sender.DataSet.FieldByName('TTAktLeseZeit').AsDateTime));
end;
 
procedure TForm1.FeldSetText(Sender: TField; const aText: string);
begin
  if sender.DataType=ftDateTime then Sender.AsDateTime :=  Local2UTC(StrToDateTime(aText));
end;
 
 
Einfach eine eigene Ableitung davon zu verweden, schlug fehl.

Code: Alles auswählen

 
 
Type
 
{ TBJDateTimeField }
 
 TBJDateTimeField =  class(TDateTimeField)
   procedure FeldGetText(Sender: TField; var aText: string; MyDisplayText: Boolean);
   procedure FeldSetText(Sender: TField; const aText: string);
      private
      public
        constructor Create;
        destructor Destroy; override;
      end;
 
implementation
 
uses localtime;
{ TBJDateTimeField }
 
constructor TBJDateTimeField.Create;
begin
  inherited;
  self.OnGetText:=@FeldGetText;
  self.OnSetText:=@FeldSetText;
end;
 
 
Muss ich dann eine Ableitung von der gesamten Komponente TQuery machen? Denn Query.Fields.add() erwartet den Typ TField.

Viele Grüße
 

Viele Grüße

Willi4Willi

------------

wp_xyz
Beiträge: 5142
Registriert: Fr 8. Apr 2011, 09:01

Re: UTC-Zeit in lokale Zeit bei Verwendung von TDatasource

Beitrag von wp_xyz »

Bei dem zweiten Ansatz, eine speziellen TDateTimeField-Nachfahren zu schreiben, der die universelle Zeit immer in die lokale Zeit umwandelt, wäre sicher die beste Lösung. Du müsstest die Quellen von TDataset oder Nachfahren durchsuchen, ob du in die Erzeugung der Felder eingreifen kannst. Dann würdest du von der von dir verwendeten Query-Klasse eine neue Klasse ableiten, in der die entsprechende Methode überschrieben wird. Genau kann ich's nicht sagen - habe schon länger nichts mehr mit Datasets gemacht.

Bei dem ersten Ansatz gäbe es noch die Möglichkeit den OnGetText/OnSetText nur einmal zu schreiben, und jedesmal bei den Feldern einzuhängen. Damit diese allgemeinen Methoden von jedem Formular aus aufgerufen werden können, würde ich diese in einer zentralen Unit implementieren, am besten gleich als Methoden des DataModules, das bei DB-Anwendungen eh' vorhanden ist:

Code: Alles auswählen

 
// --- im Datamodule ---
 
type
  TMyDatamodule = class(TDatamodule)
  // ...
  public
    procedure GetTextHandler(Sender: TField; var aText: string; DisplayText: Boolean);
    procedure SetTextHandler(Sender: TField; const aText: string);
    //...
  end;
  // Diese Methoden sind jeweils eine "Procedure ... of class", d.h. als Eventhandler verwendbar.
  // Ihre Implementierung muss so allgemein sein, dass nichts über die Feldnamen vorausgesetzt wird.
 
  procedure TMyDatamodule.GetTextHandler(Sender: TField; var aText: String; DisplayText: Boolean);
  begin
    if sender.DataType=ftDateTime then
      aText := DateTimeToStr(UTC2Local(Sender.AsDateTime));
  end;
 
  procedure TMyDatamodule.SetTextHandler(Sender: TField; const aText: string);
  begin
    if sender.DataType=ftDateTime then 
      Sender.AsDateTime := Local2UTC(StrToDateTime(aText));
  end;
 
var
  MyDatamodule: TMyDatamodule;
 
// --- in jedem Formular, das die DateTime-Felder verwendet, z.B. Form1 ---
 
  procedure TForm1.FormCreate(Sender: TObject);
  begin
    with Query1.FieldByName('TTActLeseZeit') do begin
      OnGetText := @MyDatamodule.GetTextHandler;
      OnSetText := @MyDatamodule.SetTextHandler;
    end;
    with Query1.FieldByName('Eine_andere_Zeit') do begin
      OnGetText := @MyDatamodule.GetTextHandler;
      OnSetText := @MyDatamodule.SetTextHandler;
    end;
  end;
 

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: UTC-Zeit in lokale Zeit bei Verwendung von TDatasource

Beitrag von mschnell »

IMHO:

Eigentlich sollten irgendwie gespeicherte Daten (z.B. in einer Datenbank) immer in UTC gehalten und erst bei der Präsentation für den User so umgerechnet werden, wie die aktuelle Verwendung der Daten es erfordert. Dann kann am wenigsten Unsinn passieren.

-Michael

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: UTC-Zeit in lokale Zeit bei Verwendung von TDatasource

Beitrag von mse »

wp_xyz hat geschrieben:Bei dem zweiten Ansatz, eine speziellen TDateTimeField-Nachfahren zu schreiben, der die universelle Zeit immer in die lokale Zeit umwandelt, wäre sicher die beste Lösung. Du müsstest die Quellen von TDataset oder Nachfahren durchsuchen, ob du in die Erzeugung der Felder eingreifen kannst.
Diesen Ansatz verfolgt "tmsedatetimefield", dort gibt es die property "options" mit den set-Elementen "dtfo_utc" und "dtfo_local" zur Bestimmung ob die Werte in der Datenbank als UTC oder Lokalzeit vorliegen.
Weiter haben die "t*datetimedit"-widgets die property "options" mit den set-Elementen "dteo_showlocal" und "dteo_showutc" zur Umrechnung in die gewünschte Anzeigeform. Im Speicher werden die tdatetime-Werte grundsätzlich in UTC gehalten.

Antworten