Lazarus SQLite 日期条目显示不正确

Lazarus SQLite date entry displays improperly

更新:问题已由@whosrdaddy 解决。请参阅此问题下方的评论。

我正在尝试解决以下特殊情况:在朋友的 Lazarus 项目中,他试图查询 SQLite 中的条目。 asString() 方法(在显示约会的过程中)returns Windows 64 位上的正确日期。但是,在 32 位操作系统上,仅显示前两位数字(“16”而不是“28.02.2016”)。可能是什么原因?

这是初始化表单的源代码:

// 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;

还有两个进一步的程序:

// 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.

您应该首先将日期时间转换为儒略日期

function DateTimeToJulianDate(const Datum: TDateTime): Double;

SqlQuery1.SQL.text:= 'Insert into tblKalender Values (:Datum, :Termin)';
SqlQuery1.ParamByName('Datum').AsFloat := DateTimeToJulianDate(Datum);
...
SqlQuery1.ExecSQL;

测试并获取值使用:
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');

更新

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.

双重测试

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;

输出:

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


更新 2 : 将 Delphi TDate Double 写入 SQLite 日期字段是错误的

你的评论表明你不知道这些问题。

当然你可以直接在数据库字段中写入一个DelphiDouble值。并将其读回 TDateTime。
这将很快导致问题。

示例:

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 2013/01/01 的 TDate Double 超出上述范围!!

  • 不能再使用 SQLite 自带的日期函数。

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.

  • 使用上述函数更改日期值将为日期 2013/01/01[= 提供双 2456293,5 78=]
    如果您现在使用 unproved 并将其传递给 Delphi TDateTime,它将是 3387/11/26).
    这离a的最大值不远了TDateTimePicker.
    这是 2958465.5 表示 9999/12/31

    DateTimePicker1.DateTime := 2958465.5;

    DateTimePicker1 9999/12/31

明知道错了就不要用到崩溃

SQLite 使用称为 manifest typing 的东西,这意味着如果您存储 (Delphi) 类型的 TDateTime 值,SQLite 将存储基础浮点值没有警告或消息,并为您返回值,这样您就不会注意到 SQLite 不会将其视为日期时间值,除非您试图操纵 [=21] 中的值=].

为了抵消这一点,我使用这段 SQL 代码将 Delphi TDateTime 值转换为实际的 SQL 日期时间:

datetime('1900-01-01','+'||(myDateField-2)||' day')

(另见 here