I need time as duration in postgresql
Eg:- Current time 3/16/2020 13:23:0000
table column entry by 3/15/2020 12:23:0000
so value i need to get is ' 1 day 1 hour ago'
if its like this 3/16/2020 13:20:0000 it should be 3 minutes ago like that
You can do formatting in SQL with something like:
select * from tt;
x
---------------------
2020-02-16 13:30:41
(1 row)
select current_timestamp;
current_timestamp
-------------------------------
2020-03-16 09:38:24.267299+01
(1 row)
select
case when dd > 0 then dd || ' days ' else ' ' end
||
case when hh > 0 then hh || ' hours ' else ' ' end
||
case when mi > 0 then mi || ' minutes ' else ' ' end
||
'ago' as when
from
(
select
extract(day from (current_timestamp - x)) as dd,
extract(hour from (current_timestamp - x)) as hh,
extract(minute from (current_timestamp - x)) as mi
from tt
) as t;
when
--------------------------------
28 days 20 hours 7 minutes ago
(1 row)
You can create a stored function for that:
create or replace function format_timestamp(timestamp) returns text
as
$$
select
case when dd > 0 then dd || ' days ' else ' ' end
||
case when hh > 0 then hh || ' hours ' else ' ' end
||
case when mi > 0 then mi || ' minutes ' else ' ' end
||
'ago' as when
from
(
select
extract(day from (current_timestamp - $1)) as dd,
extract(hour from (current_timestamp - $1)) as hh,
extract(minute from (current_timestamp -$1 )) as mi
) as t;
$$
language sql;
Related
I am trying to set up an SQL query in Firebird 2.5 in order to make a statistic about sales grouped by time.
My approach I followed up so far would be:
SELECT EXTRACT(WEEKDAY FROM ODR.ORDERDATE) AS S1, EXTRACT(HOUR FROM ODR.ORDERDATE) AS S2,
CASE
WHEN (EXTRACT(HOUR FROM ODR.ORDERDATE)) < 10 THEN
'0' || CAST(EXTRACT(HOUR FROM ODR.ORDERDATE) AS VARCHAR(2)) || ':00 - ' || '0' ||
CAST(EXTRACT(HOUR FROM ODR.ORDERDATE) AS VARCHAR(2)) || ':59 '
WHEN EXTRACT(HOUR FROM ODR.ORDERDATE) >= 10 THEN
CAST(EXTRACT(HOUR FROM ODR.ORDERDATE) AS VARCHAR(2)) || ':00 - ' ||
CAST(EXTRACT(HOUR FROM ODR.ORDERDATE) AS VARCHAR(2)) || ':59 '
END AS TINTERVAL,
COUNT(ODR.ID) AS ABSCOUNT
FROM ODR
GROUP BY S1,S2,TINTERVAL
ORDER BY S1,S2 ASC
The syntax of this query seems to be fine for SQL-Connection established by Firebird ODBC 2.5.
My question:
For hour=0 (<10) the query returns a result set which is not presented by the data given by the orders.
e.g. the result-set for this query might look like this:
S1 S2 TINTERVAL ABSCOUNT
0 0 00:00 - 00:59 30
0 1 01:00 - 01:59 2
I don't know how this query comes to its strange count in hour = 0.
This does not make any sense to me.
I have the following scenario:
I have employees who register their check in/out from their work. But they have 10 minutes of tolerance.
The late entries I get with this view:
CREATE OR REPLACE VIEW employees_late_entries
(
id,
created_datetime,
entry_datetime,
contact_id,
contact_name,
user_id,
employees_perm_id
)
AS
SELECT precence_records.id,
precence_records.created AS created_datetime,
("substring"(precence_records.created::text, 0, 11) || ' '::text) || contacts.entry_time::text AS entry_datetime,
contacts.id AS contact_id,
contacts.name AS contact_name,
precence_records.user_id,
precence_records.employees_perm_id
FROM precence_records,
contacts
WHERE
precence_records.type::text = 'entry'::text AND
contacts.employee = true AND
contacts.id = precence_records.contact_id AND
( ("substring"(precence_records.created::text, 0, 11) || ' '::text) || contacts.entry_time::text) < precence_records.created::text AND
precence_records.employees_perm_id IS NULL;
the precence_records.created is the check in time and contacts.entry_time its the time of the schedule entry time for the employee.
This is the condition contacts.entry_time vs precence_records.created to get the late entries:
( ("substring"(precence_records.created::text, 0, 11) || ' '::text) || contacts.entry_time::text) < precence_records.created::text
So I wanna do something like that:
( ("substring"(precence_records.created::text, 0, 11) || ' '::text) || (contacts.entry_time::text + 10 MINUTES) ) < precence_records.created::text
DATA TYPES:
precence_records.created TIMESTAMP
contacts.entry_time VARCHAR
Can you help me please
Dates, Times and Timestamps in PostgreSQL can be added/subtracted an INTERVAL value:
SELECT now()::time - INTERVAL '10 min'
If your timestamp field is varchar, you can cast it first to timestamp data type and then subtract the interval:
( (left(precence_records.created::text, 11) || ' ') ||
(contacts.entry_time::time + INTERVAL '10min')::text )::timestamp <
precence_records.created::timestamp
select getdate()- INTERVAL '10 min'
Even if you have to subtract days it can be done; just use " 10 days" instead of minutes.
How do I calculate the time difference in milliseconds between two timestamps in Oracle?
When you subtract two variables of type TIMESTAMP, you get an INTERVAL DAY TO SECOND which includes a number of milliseconds and/or microseconds depending on the platform. If the database is running on Windows, systimestamp will generally have milliseconds. If the database is running on Unix, systimestamp will generally have microseconds.
1 select systimestamp - to_timestamp( '2012-07-23', 'yyyy-mm-dd' )
2* from dual
SQL> /
SYSTIMESTAMP-TO_TIMESTAMP('2012-07-23','YYYY-MM-DD')
---------------------------------------------------------------------------
+000000000 14:51:04.339000000
You can use the EXTRACT function to extract the individual elements of an INTERVAL DAY TO SECOND
SQL> ed
Wrote file afiedt.buf
1 select extract( day from diff ) days,
2 extract( hour from diff ) hours,
3 extract( minute from diff ) minutes,
4 extract( second from diff ) seconds
5 from (select systimestamp - to_timestamp( '2012-07-23', 'yyyy-mm-dd' ) diff
6* from dual)
SQL> /
DAYS HOURS MINUTES SECONDS
---------- ---------- ---------- ----------
0 14 55 37.936
You can then convert each of those components into milliseconds and add them up
SQL> ed
Wrote file afiedt.buf
1 select extract( day from diff )*24*60*60*1000 +
2 extract( hour from diff )*60*60*1000 +
3 extract( minute from diff )*60*1000 +
4 round(extract( second from diff )*1000) total_milliseconds
5 from (select systimestamp - to_timestamp( '2012-07-23', 'yyyy-mm-dd' ) diff
6* from dual)
SQL> /
TOTAL_MILLISECONDS
------------------
53831842
Normally, however, it is more useful to have either the INTERVAL DAY TO SECOND representation or to have separate columns for hours, minutes, seconds, etc. rather than computing the total number of milliseconds between two TIMESTAMP values.
Here's a stored proc to do it:
CREATE OR REPLACE function timestamp_diff(a timestamp, b timestamp) return number is
begin
return extract (day from (a-b))*24*60*60 +
extract (hour from (a-b))*60*60+
extract (minute from (a-b))*60+
extract (second from (a-b));
end;
/
Up Vote if you also wanted to beat the crap out of the Oracle developer who negated to his job!
BECAUSE comparing timestamps for the first time should take everyone an hour or so...
Easier solution:
SELECT numtodsinterval(date1-date2,'day') time_difference from dates;
For timestamps:
SELECT (extract(DAY FROM time2-time1)*24*60*60)+
(extract(HOUR FROM time2-time1)*60*60)+
(extract(MINUTE FROM time2-time1)*60)+
extract(SECOND FROM time2-time1)
into diff FROM dual;
RETURN diff;
Select date1 - (date2 - 1) * 24 * 60 *60 * 1000 from Table;
I know that this has been exhaustively answered, but I wanted to share my FUNCTION with everyone. It gives you the option to choose if you want your answer to be in days, hours, minutes, seconds, or milliseconds. You can modify it to fit your needs.
CREATE OR REPLACE FUNCTION Return_Elapsed_Time (start_ IN TIMESTAMP, end_ IN TIMESTAMP DEFAULT SYSTIMESTAMP, syntax_ IN NUMBER DEFAULT NULL) RETURN VARCHAR2 IS
FUNCTION Core (start_ IN TIMESTAMP, end_ IN TIMESTAMP DEFAULT SYSTIMESTAMP, syntax_ IN NUMBER DEFAULT NULL) RETURN VARCHAR2 IS
day_ VARCHAR2(7); /* This means this FUNCTION only supports up to 99 days */
hour_ VARCHAR2(9); /* This means this FUNCTION only supports up to 999 hours, which is over 41 days */
minute_ VARCHAR2(12); /* This means this FUNCTION only supports up to 9999 minutes, which is over 17 days */
second_ VARCHAR2(18); /* This means this FUNCTION only supports up to 999999 seconds, which is over 11 days */
msecond_ VARCHAR2(22); /* This means this FUNCTION only supports up to 999999999 milliseconds, which is over 11 days */
d1_ NUMBER;
h1_ NUMBER;
m1_ NUMBER;
s1_ NUMBER;
ms_ NUMBER;
/* If you choose 1, you only get seconds. If you choose 2, you get minutes and seconds etc. */
precision_ NUMBER; /* 0 => milliseconds; 1 => seconds; 2 => minutes; 3 => hours; 4 => days */
format_ VARCHAR2(2) := ', ';
return_ VARCHAR2(50);
BEGIN
IF (syntax_ IS NULL) THEN
precision_ := 0;
ELSE
IF (syntax_ = 0) THEN
precision_ := 0;
ELSIF (syntax_ = 1) THEN
precision_ := 1;
ELSIF (syntax_ = 2) THEN
precision_ := 2;
ELSIF (syntax_ = 3) THEN
precision_ := 3;
ELSIF (syntax_ = 4) THEN
precision_ := 4;
ELSE
precision_ := 0;
END IF;
END IF;
SELECT EXTRACT(DAY FROM (end_ - start_)) INTO d1_ FROM DUAL;
SELECT EXTRACT(HOUR FROM (end_ - start_)) INTO h1_ FROM DUAL;
SELECT EXTRACT(MINUTE FROM (end_ - start_)) INTO m1_ FROM DUAL;
SELECT EXTRACT(SECOND FROM (end_ - start_)) INTO s1_ FROM DUAL;
IF (precision_ = 4) THEN
IF (d1_ = 1) THEN
day_ := ' day';
ELSE
day_ := ' days';
END IF;
IF (h1_ = 1) THEN
hour_ := ' hour';
ELSE
hour_ := ' hours';
END IF;
IF (m1_ = 1) THEN
minute_ := ' minute';
ELSE
minute_ := ' minutes';
END IF;
IF (s1_ = 1) THEN
second_ := ' second';
ELSE
second_ := ' seconds';
END IF;
return_ := d1_ || day_ || format_ || h1_ || hour_ || format_ || m1_ || minute_ || format_ || s1_ || second_;
RETURN return_;
ELSIF (precision_ = 3) THEN
h1_ := (d1_ * 24) + h1_;
IF (h1_ = 1) THEN
hour_ := ' hour';
ELSE
hour_ := ' hours';
END IF;
IF (m1_ = 1) THEN
minute_ := ' minute';
ELSE
minute_ := ' minutes';
END IF;
IF (s1_ = 1) THEN
second_ := ' second';
ELSE
second_ := ' seconds';
END IF;
return_ := h1_ || hour_ || format_ || m1_ || minute_ || format_ || s1_ || second_;
RETURN return_;
ELSIF (precision_ = 2) THEN
m1_ := (((d1_ * 24) + h1_) * 60) + m1_;
IF (m1_ = 1) THEN
minute_ := ' minute';
ELSE
minute_ := ' minutes';
END IF;
IF (s1_ = 1) THEN
second_ := ' second';
ELSE
second_ := ' seconds';
END IF;
return_ := m1_ || minute_ || format_ || s1_ || second_;
RETURN return_;
ELSIF (precision_ = 1) THEN
s1_ := (((((d1_ * 24) + h1_) * 60) + m1_) * 60) + s1_;
IF (s1_ = 1) THEN
second_ := ' second';
ELSE
second_ := ' seconds';
END IF;
return_ := s1_ || second_;
RETURN return_;
ELSE
ms_ := ((((((d1_ * 24) + h1_) * 60) + m1_) * 60) + s1_) * 1000;
IF (ms_ = 1) THEN
msecond_ := ' millisecond';
ELSE
msecond_ := ' milliseconds';
END IF;
return_ := ms_ || msecond_;
RETURN return_;
END IF;
END Core;
BEGIN
RETURN(Core(start_, end_, syntax_));
END Return_Elapsed_Time;
For example, if I called this function right now (12.10.2018 11:17:00.00) using Return_Elapsed_Time(TO_TIMESTAMP('12.04.2017 12:00:00.00', 'DD.MM.YYYY HH24:MI:SS.FF'),SYSTIMESTAMP), it should return something like:
47344620000 milliseconds
Better to use procedure like that:
CREATE OR REPLACE 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;
You can check it by calling:
SELECT timestamp_diff (TO_TIMESTAMP('12.04.2017 12:00:00.00', 'DD.MM.YYYY HH24:MI:SS.FF'),
TO_TIMESTAMP('12.04.2017 12:00:01.111', 'DD.MM.YYYY HH24:MI:SS.FF'))
as milliseconds
FROM DUAL;
The timestamp casted correctly between formats else there is a chance the fields would be misinterpreted.
Here is a working sample that is correct when two different dates (Date2, Date1) are considered from table TableXYZ.
SELECT ROUND (totalSeconds / (24 * 60 * 60), 1) TotalTimeSpendIn_DAYS,
ROUND (totalSeconds / (60 * 60), 0) TotalTimeSpendIn_HOURS,
ROUND (totalSeconds / 60) TotalTimeSpendIn_MINUTES,
ROUND (totalSeconds) TotalTimeSpendIn_SECONDS
FROM (SELECT ROUND (
EXTRACT (DAY FROM timeDiff) * 24 * 60 * 60
+ EXTRACT (HOUR FROM timeDiff) * 60 * 60
+ EXTRACT (MINUTE FROM timeDiff) * 60
+ EXTRACT (SECOND FROM timeDiff))
totalSeconds,
FROM (SELECT TO_TIMESTAMP (
TO_CHAR (Date2,
'yyyy-mm-dd HH24:mi:ss')
- 'yyyy-mm-dd HH24:mi:ss'),
TO_TIMESTAMP (
TO_CHAR (Date1,
'yyyy-mm-dd HH24:mi:ss'),
'yyyy-mm-dd HH24:mi:ss')
timeDiff
FROM TableXYZ))
Above one has some syntax error, Please use following on oracle:
SELECT ROUND (totalSeconds / (24 * 60 * 60), 1) TotalTimeSpendIn_DAYS,
ROUND (totalSeconds / (60 * 60), 0) TotalTimeSpendIn_HOURS,
ROUND (totalSeconds / 60) TotalTimeSpendIn_MINUTES,
ROUND (totalSeconds) TotalTimeSpendIn_SECONDS
FROM
(SELECT ROUND ( EXTRACT (DAY FROM timeDiff) * 24 * 60 * 60 + EXTRACT (HOUR FROM timeDiff) * 60 * 60 + EXTRACT (MINUTE FROM timeDiff) * 60 + EXTRACT (SECOND FROM timeDiff)) totalSeconds
FROM
(SELECT TO_TIMESTAMP(TO_CHAR( date2 , 'yyyy-mm-dd HH24:mi:ss'), 'yyyy-mm-dd HH24:mi:ss') - TO_TIMESTAMP(TO_CHAR(date1, 'yyyy-mm-dd HH24:mi:ss'),'yyyy-mm-dd HH24:mi:ss') timeDiff
FROM TABLENAME
)
);
I've posted here some methods to convert interval to nanoseconds and nanoseconds to interval. These methods have a nanosecond precision.
You just need to adjust it to get milliseconds instead of nanoseconds.
A shorter method to convert interval to nanoseconds.
SELECT (EXTRACT(DAY FROM (
INTERVAL '+18500 09:33:47.263027' DAY(5) TO SECOND --Replace line with desired interval --Maximum value: INTERVAL '+694444 10:39:59.999999999' DAY(6) TO SECOND(9) or up to 3871 year
) * 24 * 60) * 60 + EXTRACT(SECOND FROM (
INTERVAL '+18500 09:33:47.263027' DAY(5) TO SECOND --Replace line with desired interval
))) * 100 AS MILLIS FROM DUAL;
MILLIS
1598434427263.027
I) if you need to calculate the elapsed time in seconds between two timestamp columns try this:
SELECT
extract ( day from (end_timestamp - start_timestamp) )*86400
+ extract ( hour from (end_timestamp - start_timestamp) )*3600
+ extract ( minute from (end_timestamp - start_timestamp) )*60
+ extract ( second from (end_timestamp - start_timestamp) )
FROM table_name
II) if you want to just show the time difference in character format try this:
SELECT to_char (end_timestamp - start_timestamp) FROM table_name
I know that many people finding this solution simple and clear:
create table diff_timestamp (
f1 timestamp
, f2 timestamp);
insert into diff_timestamp values(systimestamp-1, systimestamp+2);
commit;
select cast(f2 as date) - cast(f1 as date) from diff_timestamp;
bingo!
Please help me to solve this.
I have a table that contain users check in (checktype = I) and check out (checktype = 0) time everyday, and I would like to get the total amount of check in time per user which occur > 08:00 AM in a specific date range.
I am using the query below, but only handle one day per query not in a range, so I have to loop using javascript to get the amount of delay ( > 08:00 AM) per user for example from 01/06/2012 to 06/06/2012
Please help me to get the amount (count) check in time > 08:00 AM per user (ex: userid 708) from ex:01/06/2012 to 06/06/2012 in a single query.
with tt as
(
select TO_DATE('01/06/2012 08:00:00','dd/mm/yyyy hh24:mi:ss') date1 ,
checktime date2
from
checkinout
where
userid = '708' and
to_char(checktime,'dd/mm/yyyy') = '01/06/2012' and
checktype='I' -- checktype I is check in
) , t2 as
(
select numtodsinterval(date2 - date1,'day') dsinterval from tt
)
select extract(hour from dsinterval) || ' hours ' ||
extract(minute from dsinterval) || ' minutes ' ||
round(extract(second from dsinterval)) || ' seconds' late from t2
I assume you wanted to get how many hours late (i.e. after 08:00) the checkins have been done:
with t2 as (
select userid
,numtodsinterval(sum(checktime - (trunc(checktime)+8/24)),'day') dsinterval
,count(1) cnt
from checkinout
where userid='708'
and checktime > trunc(checktime)+8/24
and trunc(checktime) between to_date('01/06/2012','DD/MM/YYYY') and to_date('06/06/2012','DD/MM/YYYY')
and checktype = 'I'
group by userid
)
select extract(hour from dsinterval) || ' hours ' ||
extract(minute from dsinterval) || ' minutes ' ||
round(extract(second from dsinterval)) || ' seconds' late
,cnt
from t2;
See http://sqlfiddle.com/#!4/c4670/11 for my test case.
edit: added column "cnt" to show how many times
Consider the following example on base of this you can write your own logic
WITH tbl AS
(SELECT SYSDATE dt
FROM DUAL
UNION
SELECT SYSDATE + (1 + (10 / 1440))
FROM DUAL
UNION
SELECT SYSDATE + (2 + (12 / 1440))
FROM DUAL
UNION
SELECT SYSDATE + (3 + (13 / 1440))
FROM DUAL
UNION
SELECT SYSDATE + (6 + (15 / 1440))
FROM DUAL
UNION
SELECT SYSDATE + (8 + (18 / 1440))
FROM DUAL)
SELECT EXTRACT (HOUR FROM dsinterval)
|| ' hours '
|| EXTRACT (MINUTE FROM dsinterval)
|| ' minutes '
|| ROUND (EXTRACT (SECOND FROM dsinterval))
|| ' seconds' late
FROM (SELECT NUMTODSINTERVAL (dt1 - dt2, 'day') dsinterval
FROM (SELECT TO_DATE (TO_CHAR (dt, 'DD/MM/YYYY') || ' 08:00:00',
'DD/MM/YYYY HH24:MI:SS'
) dt1,
TO_DATE (TO_CHAR (dt, 'DD/MM/YYYY HH24:MI:SS'),
'DD/MM/YYYY HH24:MI:SS'
) dt2
FROM tbl
WHERE dt BETWEEN SYSDATE + 2 AND SYSDATE + 5))
As per code you can write like
SELECT EXTRACT (HOUR FROM dsinterval)
|| ' hours '
|| EXTRACT (MINUTE FROM dsinterval)
|| ' minutes '
|| ROUND (EXTRACT (SECOND FROM dsinterval))
|| ' seconds' late
FROM (SELECT NUMTODSINTERVAL (dt1 - dt2, 'day') dsinterval
FROM (SELECT TO_DATE (TO_CHAR (checktime , 'DD/MM/YYYY') || ' 08:00:00',
'DD/MM/YYYY HH24:MI:SS'
) dt1,
TO_DATE (checktime, 'DD/MM/YYYY HH24:MI:SS') dt2
FROM checkinout
WHERE checktime BETWEEN start_date AND end_date
AND checktype='I'))
How to convert number 1.33408564814814 to time 32:01:05?
If you actually want the result as a character string, you could use a function like this:
set serveroutput on format wrapped;
declare
function days_to_time (p_days number) return varchar2
is
d number := p_days;
h integer;
m integer;
s integer;
begin
h := trunc(d*24);
d := d - h/24;
m := trunc(d*24*60);
d := d - m/24/60;
s := round(d*24*60*60);
return(h||':'||to_char(m,'FM00')||':'||TO_CHAR(s,'FM00'));
end;
begin
dbms_output.put_line(days_to_time(1.33408564814814));
end;
/
anonymous block completed
32:01:05
SELECT
-- EXTRACT(day FROM numtodsinterval(1.33408564814814 , 'DAY')) Days,
EXTRACT(day FROM numtodsinterval(1.33408564814814 , 'DAY')) * 24 + EXTRACT(hour FROM numtodsinterval(1.33408564814814 , 'DAY')) TotalHours,
-- EXTRACT(hour FROM numtodsinterval(1.33408564814814 , 'DAY')) Hours,
EXTRACT(minute FROM numtodsinterval(1.33408564814814 , 'DAY')) Minutes,
EXTRACT(second FROM numtodsinterval(1.33408564814814 , 'DAY')) Seconds
FROM dual;
The original number is an interval expressed as a number of days. The following is a bit ugly but might help show what's going on:
SELECT days,
hours,
minutes,
seconds,
TO_CHAR(days, 'FM9') || ' ' ||
TO_CHAR(day_hours, 'FM09') || ':' ||
TO_CHAR(minutes, 'FM09') || ':' ||
TO_CHAR(seconds, 'FM09') AS interval_string,
TO_DSINTERVAL(TO_CHAR(days, 'FM9') || ' ' ||
TO_CHAR(day_hours, 'FM09') || ':' ||
TO_CHAR(minutes, 'FM09') || ':' ||
TO_CHAR(seconds, 'FM09')) actual_interval
FROM (SELECT float_val,
days, hours, minutes,
hours - (days * 24) AS day_hours,
ROUND((((decimal_hours - hours) * 60) - trunc((decimal_hours - hours) * 60)) * 60) AS seconds
FROM (SELECT float_val,
trunc(hours / 24) AS days,
decimal_hours,
hours,
(decimal_hours - hours) * 60 AS decimal_minutes,
trunc((decimal_hours - hours) * 60) AS minutes
FROM (SELECT float_val,
float_val * 24 AS decimal_hours,
TRUNC(float_val* 24) AS hours
FROM (SELECT 1.33408564814814 AS float_val FROM dual))));
Share and enjoy.