Search code examples
databasesqlitedelphifiredac

Reading DateTime from Database using Delphi and FireDAC


I'm failing dismally to read a datetime from an SQLite database using Delphi 10.3 and FireDAC. As the simplest example, I create a sample database using sqlite as follows:

.open Test.db
CREATE TABLE "TABLE1" ("Name"VarChar(16), "Time" datetime); 
INSERT INTO Table1 (Name,Time) VALUES("Fred",time('now'));

Then

select * from Table1

gives Fred|16:52:57 as expected.

If I generate a Delphi program with an FDConnection1 and FDQuery1 linked to a datasource and DBgrid it will read "Fred" but not the time. The value returned by FDquery1 asstring is '' and asfloat is 0. If I try the FireDAC explorer tool to look at the database it also fails to read the time value but I notice it does read datetimes from some of the example databases so it clearly can work.

Can anyone tell me what I'm missing. Thanks


Solution

  • Trying to construct a SQL statement as a string in Delphi code can be a bit error prone. However, you should find that the following code executes correctly

    procedure TForm2.btnInsertRowClick(Sender: TObject);
    const
      sInsertRow = ' INSERT INTO Table1 (Name,time) VALUES(''Fred'',datetime(''now''))';
    begin
      FDConnection1.ExecSql(sInsertRow);
    end;
    

    Btw, in general it would be better to use a parameterised INSERT statement than a literal one like the above, but it's not really practical here as you are inserting the now value returned by Sqlite rather than the Delphi now function.

    **Update: ** The code below is from a minimal project which creates your example table, inserts a row into it and then presents it in db-aware controls (DBGrid, DBEdit) for editing. It all works exactly as it ought to. In particular, any changes to the row data made through those controls are retained when tha app is next run. Note that the SQL which creates the table specifies a primary key on the Name column/field: this is necessary for the FDQuery1 to generate the UPDATE statements needed to save changes back to the on-disk table.

    The code in the btnSelectClick handler shows how to set the time field's DisplayFormat and EditMask properties so that it only displays the time part of the stored DateTime data.

    type
      TForm2 = class(TForm)
        FDConnection1: TFDConnection;
        FDQuery1: TFDQuery;
        DBGrid1: TDBGrid;
        DBNavigator1: TDBNavigator;
        DataSource1: TDataSource;
        DBEdit1: TDBEdit;
        btnCreateTable: TButton;
        btnInsertRow: TButton;
        btnSelect: TButton;
      [...]
      public
    
    [...]
    
    const
      sCreateTable = 'CREATE TABLE ''TABLE1'' (''Name'' VarChar(16) primary key, ''Time'' datetime)';
      sInsertRow = ' INSERT INTO Table1 (Name,time) VALUES(''Fred'',datetime(''now''))';
      sSelect = 'select * from table1';
    
    procedure TForm2.btnCreateTableClick(Sender: TObject);
    begin
      FDConnection1.Connected := True;
      FDConnection1.ExecSql(sCreateTable);
    end;
    
    procedure TForm2.btnInsertRowClick(Sender: TObject);
    const
    begin
      FDConnection1.ExecSql(sInsertRow);
    end;
    
    procedure TForm2.btnSelectClick(Sender: TObject);
    var
      AField : TDateTimeField;
    begin
      FDQuery1.SQL.Text := sSelect;
      FDQuery1.Open;
    
      //   The following shows how to  control how the time field is formatted for display
      //   in gui controls using the field's DisplayFormat
      //   and how to set up its EditMask for editing
      AField := FDQuery1.FieldByName('time') as TDateTimeField;
      AField.DisplayFormat := 'hh:nn:ss';
      AField.EditMask := '!90:00:00;1;_';
    end;
    end.
    

    .DFM file

    object Form2: TForm2
    [...]
      object FDConnection1: TFDConnection
        Params.Strings = (
          'Database=D:\Delphi\Code\FireDAC\db1.sqlite'
          'DriverID=SQLite')
        LoginPrompt = False
      end
      object FDQuery1: TFDQuery
        Connection = FDConnection1
      end
      object DataSource1: TDataSource
        DataSet = FDQuery1
      end
      object DBGrid1: TDBGrid
        DataSource = DataSource1
      end
      object DBNavigator1: TDBNavigator
        DataSource = DataSource1
      end
      object DBEdit1: TDBEdit
        DataField = 'Time'
        DataSource = DataSource1
      end
    end