Lazarus SQLite date entry displays improperly - sqlite

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.

SQLite uses something called manifest typing which means that if you store values of (Delphi) type TDateTime, SQLite will store the underlying floating point value without warning or message, and offer you the value back so you won't notice that SQLite doesn't treat this as a date-time-value, unless you're trying to manipulate the value from SQL.
To counteract this, I use this bit of SQL code to convert Delphi TDateTime values to an actual SQL datetime:
datetime('1900-01-01','+'||(myDateField-2)||' day')
(see also here)

Related

PLS-00221 'to_date' is not a procedure or undefined error and converting dates separated by period

I want to convert an IN parameter which is in type of varchar2 to a date so that I can pull the data from the database between the two input dates. I have one problem and one question.
Problem is that I am getting the error
"PLS-00221: to_date is not a procedure or undefined"
when I have the following code:
create or replace procedure display_users( pi_date1 in varchar2,
pi_date2 in varchar2,
po_userc out sys_refcursor) is
begin
if pi_date1 is not null then
to_date(pi_date1, 'DD/MM/YYYY HH24:MI:SS');
end if;
begin
open po_userc for
select ...
from ...
where ...
and ...
and t.inserted_date betweeen pi_date1 and pi_date2 ;
end;
end;
I am not very familiar with pl/sql procedures, I tried lots of other ways to make it work but I wasn't able to do it.
The question is that in the database my dates are like this: '16.08.2018 10:30:48' (I cannot change the data format in database)
The date part is separated by periods (.) . Will to_date(pi_date1, 'DD/MM/YYYY HH24:MI:SS'); work since date is separated by "/" ?
as the error message already says, to_date is not a procedure. to_date is a function and a function always returns a value. You must assign this value to a variable.
declare
v_date date;
begin
v_date := to_date(pi_date1, 'DD/MM/YYYY HH24:MI:SS');
end;
/
When converting to a date you have to pay attention to the format. Is there a separator between day, month and year, hour, Minute and seconds? If it is a Point (.) then you have to specify it as a Format.
If your string looks like this: 01.01.2000 01:01:01
Then your format must look like this: 'DD.MM.YYYY HH24:MI:SS'
If your string looks like this: 01/01/2000 01:01:01
Then your format must look like this: 'DD/MM/YYYY HH24:MI:SS'

SQL Server Filtering by DateTime column, when TIME portion is provided sometimes

In an SSRS report, the user searches based on start date and end date.
The challenge is, as I discovered recently, he sometimes, not always, provides the time component while searching.
Currently, the filter is done like this:
if #pEndDate is null
SET #pEndDate = getdate()
SET #PEndDate = DateAdd(dd,1,#PEndDate)
SELECT ........
FROM .....
WHERE ( Createdon >= #PStartDate AND Createdon < #PEndDate)
This is fine when he searches without time (example - #PStartDate = 2/23/2015 and #PEndDate = 2/24/2015)
How should I structure the query to deal with the time portion when he provides it? (example - #PStartDate = 2/23/2015 15:00 and #PEndDate = 2/24/2015 15:00)
If this is answered elsewhere, please point me to it. Thank you.
If you just want to match the date part then there are lot options.
1) You can use the Date type for the parameter PEndDate and PStartDate to nullify the time part
2) You can use the Convert method to get only date part of the parameter while matching.CONVERT (DATE, #PEndDate) OR CONVERT(varchar,#PEndDate,103)
3) Get Date Part only from DateTime using DateTime functions
ATEADD(dd, 0,
DATEDIFF(dd, 0, #PEndDate))
4) Get Date Part only from DateTime using FLOOR and CAST functions
CAST( -- Convert the integer to DATE
FLOOR(-- Get largest Integer less than or equal to the decimal value
CAST(GETDATE() AS DECIMAL(12, 5)) -- Convert DATETIME to DECIMAL)
AS DATETIME) 'Date Part Only'
5) Get Date Part only from DateTime using DATEPART and CONVERT functions
CONVERT(VARCHAR(4),DATEPART(YEAR, #GETDATE))
+ '/'+ CONVERT(VARCHAR(2),DATEPART(MONTH, #GETDATE))
+ '/' + CONVERT(VARCHAR(2),DATEPART(DAY, #GETDATE))
'Date Part Only'
Use whichever method suits you and you find fancy.
UPDATE
As you mentioned you need to get the time part to 00:00 with date so you can try as,
SELECT CAST( convert(varchar(10),GETDATE(),112) AS DATETIME)
--This will give you 2015-02-27 00:00:00.000
SELECT DATEADD(ms,-3, DATEADD(day, DATEDIFF(day,0,GETDATE())+1,0))
--This will give you end of days time 2015-02-27 23:59:59.997
SELECT CONVERT(nvarchar,getdate(),103) + ' 12:59:59 PM'
--This will give you custom time 27/02/2015 12:59:59 PM

How to keep the military format when converting string to timestap in Oracle?

This is my code:
l_ts := to_timestamp((d || ' 13:00:00'), 'DD.MM.YYYY:HH24:MI:SS');
u_ts := to_timestamp((d || ' 17:00:00'), 'DD.MM.YYYY:HH24:MI:SS');
I keep getting:
26-MAY-13 01.00.00.000000 PM 26-MAY-13 05.00.00.000000 PM
I want:
26-MAY-13 13.00.00.000000 PM 26-MAY-13 17.00.00.000000 PM
i.e. 13 and 17 instead of 1 and 5.
Thanks for hrlping
You are converting string to date, and then display the date directly.
When You do it that way, the output is formatted to string internally and it depends on You session locale settings.
If You want to get the date in exactly the format You want, You should use TO_CHAR function that will format Your date back to string when You need it to be displayed to User. With TO_CHAR You can use format models to get Your date in any format You wish (try to do it in SQL server... ;).
Consider this script:
set serveroutput on;
DECLARE
l_ts TIMESTAMP;
BEGIN
-- Here You get DATE from STRING
l_ts := to_timestamp(('01.01.2013' || ' 13:00:00'), 'DD.MM.YYYY:HH24:MI:SS');
-- Here You get STRING representation of Your DATE.
-- Representation depends on You session settings,
-- and should not be relayed upon.
dbms_output.put_line(l_ts);
-- Examples how You can display DATE as STRING in any way You need
dbms_output.put_line(TO_CHAR(l_ts,'YYYY-MM-DD HH24:MI:SS'));
dbms_output.put_line(TO_CHAR(l_ts,'YYYY-MM-DD HH12:MI:SS AM'));
END;
Hope that will clear the issue. :)

I want to combine date and time and compare with utc date

When I write this query
SELECT Convert(datetime,Convert(varchar,CAST(GETUTCDATE() AS DATE))+' '+
CONVERT(varchar, cast(meas_pain.datetime AS time))) FROM meas_pain
it works for me but when I use the same part in WHERE clause it gives error 'Conversion failed when converting date and/or time from character string.'
SELECT schedules.id
FROM meas_pain LEFT JOIN schedules ON schedules.id=meas_pain.schd_id
WHERE meas_pain.schd_id=9150 AND
Convert(datetime,(Convert(varchar,CAST(GETUTCDATE() AS DATE))+' '+
CONVERT(varchar, cast(meas_pain.datetime AS time)))) <
CONVERT(datetime,DATEADD(Minute,0,getutcdate()))
can anybody explain??
I am not sure why this error does not appear in your select statement, since I can reproduce the error using just
SET DATEFORMAT DMY;
SELECT CONVERT(DATETIME, CONVERT(VARCHAR,CAST(GETUTCDATE() AS DATE)))
Example of Error
You are relying on localised conversion settings, you should use explicit conversion, e.g.
SET DATEFORMAT DMY;
SELECT CONVERT(DATETIME, CONVERT(VARCHAR, CAST(GETUTCDATE() AS DATE), 111), 111)
By explicitly defining the date format to convert both to varchar and from varchar (111) you can avoid any implied conversions.
However, If your dates/times are stored as such there should be no need for all the conversion to and from varchar, this is just more chance for things to go wrong, and unnecessary work, you can simply add a time to a datetime. e.g.
DECLARE #Date1 DATETIME = DATEADD(HOUR, 1, GETUTCDATE()),
#Date2 DATETIME = DATEADD(DAY, 1, GETUTCDATE());
SELECT [Date1] = #Date1,
[Date2] = #Date2,
[Date1/Time2] = CAST(CAST(#Date1 AS DATE) AS DATETIME) +
CAST(#Date2 AS TIME);
From what I can gather from your query you are just trying to get results where the time of meas_pain.datetime is less that the current UTC time, regardless of date. So you should be able to simplify your query to just:
SELECT schedules.id
FROM meas_pain
LEFT JOIN schedules
ON schedules.id = meas_pain.schd_id
WHERE meas_pain.schd_id = 9150
AND CAST(meas_pain.[DateTime] AS TIME) < CAST(GETUTCDATE() AS TIME);
And remove further redundant conversions.
Simplified example on SQL Fiddle
ADENDUM
Apparently this time comparison is not what you are after (although it is what the query you have posted is doing), so I am assuming GETUTCDATE() is just for demonstration.
The conversion you are trying to perform is equivalent to this:
CAST(CAST(GETUTCDATE() AS DATE) AS DATETIME) + CAST(meas_pain.[DateTime] AS TIME)
Another example on SQL Fiddle using the above conversion

PLSQL - Measure the execution duration of a procedure

I have a procedure that runs every one hour populating a table. The records handled from the procedure are many so it takes approximately 12~17 mins each time it is executed.
Do you now if there is a way (i.e. trigger) to record the duration of each execution (i.e. into a table)?
I don't know of a trigger that would allow this to be done automatically. One way to do this would be something like
PROCEDURE MY_PROC IS
tsStart TIMESTAMP;
tsEnd TIMESTAMP;
BEGIN
tsStart := SYSTIMESTAMP;
-- 'real' code here
tsEnd := SYSTIMESTAMP;
INSERT INTO PROC_RUNTIMES (PROC_NAME, START_TIME, END_TIME)
VALUES ('MY_PROC', tsStart, tsEnd);
END MY_PROC;
If you only need this for a few procedures this might be sufficient.
Share and enjoy.
I typically use a log table with a date or timestamp column that uses a default value of sysdate/systimestamp. Then I call an autonomous procedure that does the log inserts at certain places I care about (starting/ending a procedure call, after a commit, etc):
See here (look for my answer).
If you are inserting millions of rows, you can control when (how often) you insert to the log table. Again, see my example.
To add to the first answer, once you have start and end timestamps, you can use this function to turn them into a number of milliseconds. That helps with readability if nothing else.
function timestamp_diff(
start_time_in timestamp,
end_time_in timestamp) return number
as
l_days number;
l_hours number;
l_minutes number;
l_seconds number;
l_milliseconds number;
begin
select extract(day from end_time_in-start_time_in)
, extract(hour from end_time_in-start_time_in)
, extract(minute from end_time_in-start_time_in)
, extract(second from end_time_in-start_time_in)
into l_days, l_hours, l_minutes, l_seconds
from dual;
l_milliseconds := l_seconds*1000 + l_minutes*60*1000
+ l_hours*60*60*1000 + l_days*24*60*60*1000;
return l_milliseconds;
end;

Resources