UPDATE: Issue was solved by @whosrdaddy. See comments below this question.
I am trying to resolve the following peculiar case: In a friend's Lazarus project, he tries to query an entry in SQLite. The asString()-method (in the procedure for displaying appointments) returns the proper date on Windows 64 Bit. On a 32 Bit operating system, however, only the first two digits are displayed ('16' instead of '28.02.2016'). What could be the reason?
This is the source code for initialising the form:
// Initialise Form
procedure TForm1.FormCreate(Sender: TObject);
begin
SQLite3Connection1.DatabaseName:='Kalender.sqlite';
SQLTransaction1.Database:=SQLite3Connection1;
SQLQuery1.Transaction:=SQLTransaction1;
// Create Table "tblTermine"
SQLQuery1.SQL.text := 'CREATE TABLE IF NOT EXISTS tblKalender (Datum DATETIME, Termin VARCHAR(10))';
SQLQuery1.ExecSQL;
SQLTransaction1.commit;
end;
There are two further procedures:
// Display Appointments
procedure TForm1.Button1Click(Sender: TObject);
begin
ListBox1.Clear;
SQLQuery1.Close;
SQLQuery1.SQL.text:='SELECT * FROM tblKalender';
SQLQuery1.Open;
while not SQLQuery1.Eof do
begin
// Should return 'dd.mm.yyyy'
ListBox1.Items.add(SQLQuery1.Fields[0].AsString+ ': ' + SQLQuery1.Fields[1].AsString);
SQLQuery1.Next;
end;
end;
// Save Appointment
procedure TForm1.Button2Click(Sender: TObject);
var Termin: string;
Datum: TDate;
begin
Termin:=Edit1.text;
if calendardialog1.execute then
Datum:=TDate(calendardialog1.date);
SQLQUERY1.close;
SqlQuery1.SQL.text:= 'Insert into tblKalender Values (:Datum, :Termin)';
SqlQuery1.ParamByName('Datum').AsDate:= Datum;
SqlQuery1.ParamByName('Termin').AsString:= Termin;
SqlQuery1.ExecSQL;
SqlTransaction1.Commit;
Button1.Click;
Edit1.Text := '';
end;
The intended output into the TListBox would be something like this.
you should convert first the DateTime to a Julian Date
function DateTimeToJulianDate(const Datum: TDateTime): Double;
and
SqlQuery1.SQL.text:= 'Insert into tblKalender Values (:Datum, :Termin)';
SqlQuery1.ParamByName('Datum').AsFloat := DateTimeToJulianDate(Datum);
...
SqlQuery1.ExecSQL;
to test and get the value use :
function TryJulianDateToDateTime(const AValue: Double; ADateTime: TDateTime):Boolean;
if TryJulianDateToDateTime(SQLQuery1.Fields[0].AsFloat,myDate)
then
ListBox1.Items.add(DateTimeToStr(myDate)+ ': ' + .....
else
ShowMessage('Not a valid Julian date');
Update
SQLite are capable of storing dates and times as TEXT, REAL, or INTEGER values:
- TEXT as ISO8601 strings ("YYYY-MM-DD HH:MM:SS.SSS").
- REAL as Julian day numbers, the number of days since noon in Greenwich on November 24, 4714 B.C. according to the proleptic Gregorian calendar.
- INTEGER as Unix Time, the number of seconds since 1970-01-01 00:00:00 UTC.
Double Test
procedure TForm1.Button3Click(Sender: TObject);
var
Datum : TDate;
myDate : TDateTime;
JulianDouble : Double;
begin
// uses ....,DateUtils
Datum := StrToDate('01.01.2013'); //German Culture settings
Memo1.Lines.Add('01/01/2013 = '+DateTimeToStr(Datum)+ ' TDate as Text');
Memo1.Lines.Add('01/01/2013 = '+FloatToStr(Datum) + ' TDate Double');
JulianDouble := DateTimeToJulianDate(Datum);
Memo1.Lines.Add('01/01/2013 = '+FloatToStr(JulianDouble) + ' Julian Double');
if TryJulianDateToDateTime(JulianDouble,myDate)
then
Memo1.Lines.Add('01/01/2013 = '+DateTimeToStr(myDate)+ ' TDate as Text')
else
ShowMessage('Not a valid Julian date');
end;
Output :
01/01/2013 = 01.01.2013 TDate as Text
01/01/2013 = 41275 TDate Double
01/01/2013 = 2456293,5 Julian Double
01/01/2013 = 01.01.2013 TDate as Text
Update-2 : To write Delphi TDate Double to a SQLite Date field is wrong
Your comment shows me that you do not know the problems.
Of course you can directly write a Delphi Double value into a database field. And read it back to a TDateTime.
This will quickly lead to problems.
Examples:
SQLite:
These functions only work for dates between 0000-01-01 00:00:00 and 9999-12-31 23:59:59 (julidan day numbers 1721059.5 through 5373484.5).
For dates outside that range, the results of these functions are undefined.
41275 Delphi TDate Double for 2013/01/01 is outside of above range !!
SQLite's own date functions can no longer be used.
Compute the current date.
SELECT date('now');
Compute the last day of the current month.
SELECT date('now','start of month','+1 month','-1 day');
Compute the date and time given a unix timestamp 1092941466.
SELECT datetime(1092941466, 'unixepoch');
Compute the date and time given a unix timestamp 1092941466, and compensate for your local timezone
SELECT datetime(1092941466, 'unixepoch', 'localtime');
Compute the number of days since the signing of the US Declaration of Independence.
SELECT julianday('now') - julianday('1776-07-04');
etc. etc.
Changing the date value with above functions will give you a double 2456293,5 for a Date 2013/01/01
If you now use unproved and pass it to a Delphi TDateTime it will be 3387/11/26).
This is not far from the maximum value of a TDateTimePicker
.
which is 2958465.5 and means 9999/12/31
DateTimePicker1.DateTime := 2958465.5;
DateTimePicker1 9999/12/31
If one already know that it is wrong one should not use it up to a crash.