how to convert date format in sql - sql

if i have dates/time like these
8/5/2014 12:00:01 AM
8/5/2014 12:00:16 AM
8/5/2014 12:00:18 AM
8/5/2014 12:17:18 AM
8/5/2014 12:19:18 AM
i want these date/times
if the minutes less than 15 and greater than 00 i want the time for minutes to be 00
if the minutes less than 30 and greater than 15 i want the minutes to be 15
if the minutes less than 45 and greater than 30 i want the minutes to be 30
if the minutes less than 00 and greater than 45 i want the minutes to be 45
8/5/2014 12:00:00 AM
...
...
8/5/2014 12:15:00AM
...
...
8/5/2014 12:30:00AM
...
...
8/5/2014 12:45:00AM
i need to do that for my report
how can i apply these in oracle

Here's another method, which is really a variation of Gordon Linoff's approach:
trunc(dt, 'HH24') + ((15/1440) * (floor(to_number(to_char(dt, 'MI'))/15)))
The trunc(dt, 'HH24') gives you the value truncated to hour precision, so for your sample that's always midnight. Then floor(to_number(to_char(dt, 'MI'))/15) gives you the number of complete 15-minute periods represented by the minute value; with your data that's either zero or 1. As Gordon mentioned when you add a numeric value to a date it's treated as fractions of a day, so that needs to be multiplied by '15 minutes' (15/1400).
with t as (
select to_date('8/5/2014 12:00:01 AM') as dt from dual
union all select to_date('8/5/2014 12:00:16 AM') as dt from dual
union all select to_date('8/5/2014 12:00:18 AM') as dt from dual
union all select to_date('8/5/2014 12:17:18 AM') as dt from dual
union all select to_date('8/5/2014 12:19:18 AM') as dt from dual
union all select to_date('8/5/2014 12:37:37 AM') as dt from dual
union all select to_date('8/5/2014 12:51:51 AM') as dt from dual
)
select dt, trunc(dt, 'HH24')
+ ((15/1440) * (floor(to_number(to_char(dt, 'MI'))/15))) as new_dt
from t;
DT NEW_DT
---------------------- ----------------------
08/05/2014 12:00:01 AM 08/05/2014 12:00:00 AM
08/05/2014 12:00:16 AM 08/05/2014 12:00:00 AM
08/05/2014 12:00:18 AM 08/05/2014 12:00:00 AM
08/05/2014 12:17:18 AM 08/05/2014 12:15:00 AM
08/05/2014 12:19:18 AM 08/05/2014 12:15:00 AM
08/05/2014 12:37:37 AM 08/05/2014 12:30:00 AM
08/05/2014 12:51:51 AM 08/05/2014 12:45:00 AM

To add yet another method this looks like a situation where the function NUMTODSINTERVAL() could be useful - it makes it slightly more obvious what's happening:
select trunc(dt)
+ numtodsinterval(trunc(to_char(dt, 'sssss') / 900) * 15, 'MINUTE')
from ...
TRUNC() truncates the date to the beginning of that day. The format model sssss calculates the number of seconds since midnight. The number of complete quarter-hours since midnight is the number of seconds divided by 900 (as there are 900 quarter-hours in the day). This is truncated again to remove any part-completed quarter-hours, multipled by 15 to give the number of minutes (there are 15 minutes in a quarter hour). Lastly, convert this to an INTERVAL DAY TO SECOND and add to the original date.
SQL> alter session set nls_date_format = 'dd/mm/yyyy hh24:mi:ss';
Session altered.
SQL> with t as (
2 select to_date('05/08/2014 12:00:01') as dt from dual union all
3 select to_date('05/08/2014 12:00:16') as dt from dual union all
4 select to_date('05/08/2014 12:00:18') as dt from dual union all
5 select to_date('05/08/2014 12:17:18') as dt from dual union all
6 select to_date('05/08/2014 12:19:18') as dt from dual union all
7 select to_date('05/08/2014 12:37:37') as dt from dual union all
8 select to_date('05/08/2014 12:51:51') as dt from dual
9 )
10 select trunc(dt)
11 + numtodsinterval(trunc(to_char(dt, 'sssss') / 900) * 15, 'MINUTE')
12 from t
13 ;
TRUNC(DT)+NUMTODSIN
-------------------
05/08/2014 12:00:00
05/08/2014 12:00:00
05/08/2014 12:00:00
05/08/2014 12:15:00
05/08/2014 12:15:00
05/08/2014 12:30:00
05/08/2014 12:45:00
7 rows selected.
I've explicitly set my NLS_DATE_FORMAT so I can rely on implicit conversion in TO_DATE() so that it fits on the page without scrolling. It is not recommended to use implicit conversion normally.

Here is one method. Extract the date and then add in what you want as hours and minutes:
select trunc(dt) + extract(hour from dt) / 24.0 +
(trunc(extract(minute from dt) / 15) * 15) / (24.0 * 60);
This uses the fact that + for dates adds a number of days. The three terms are the original date at midnight, the number of hours converted to days (hence the / 24) and the third is the number of minutes, suitably rounded.

Here is an example for ORACLE with actual date:
select trunc(sysdate,'HH')+trunc(to_number(to_char(sysdate,'MI'))/15)*15/24/60
from dual;

Related

Split row data base on timestamp SQL Oracle

Good day everyone. I have a table as below. Duration is the time from current state to next state.
Timestamp
State
Duration(minutes)
10/9/2022 8:50:00 AM
A
35
10/9/2022 9:25:00 AM
B
10
10/9/2022 9:35:00 AM
C
...
How do I split data at 9:00 AM of each day like below:
Timestamp
State
Duration(minutes)
10/9/2022 8:50:00 AM
A
10
10/9/2022 9:00:00 AM
A
25
10/9/2022 9:25:00 AM
B
10
10/9/2022 9:35:00 AM
C
...
Thank you.
Use a row-generator function to generate extra rows when the timestamp is before 09:00 and the next timestamp is after 09:00 (and calculate the diff value rather than storing it in the table):
SELECT l.ts AS timestamp,
t.state,
ROUND((l.next_ts - l.ts) * 24 * 60, 2) As diff
FROM (
SELECT timestamp,
LEAD(timestamp) OVER (ORDER BY timestamp) AS next_timestamp,
state
FROM table_name
) t
CROSS APPLY (
SELECT GREATEST(
t.timestamp,
TRUNC(t.timestamp - INTERVAL '9' HOUR) + INTERVAL '9' HOUR + LEVEL - 1
) AS ts,
LEAST(
t.next_timestamp,
TRUNC(t.timestamp - INTERVAL '9' HOUR) + INTERVAL '9' HOUR + LEVEL
) AS next_ts
FROM DUAL
CONNECT BY
TRUNC(t.timestamp - INTERVAL '9' HOUR) + INTERVAL '9' HOUR + LEVEL - 1 < t.next_timestamp
) l;
Which, for your sample data:
CREATE TABLE table_name (Timestamp, State) AS
SELECT DATE '2022-10-09' + INTERVAL '08:50' HOUR TO MINUTE, 'A' FROM DUAL UNION ALL
SELECT DATE '2022-10-09' + INTERVAL '09:25' HOUR TO MINUTE, 'B' FROM DUAL UNION ALL
SELECT DATE '2022-10-09' + INTERVAL '09:35' HOUR TO MINUTE, 'C' FROM DUAL UNION ALL
SELECT DATE '2022-10-12' + INTERVAL '09:35' HOUR TO MINUTE, 'D' FROM DUAL;
Outputs:
TIMESTAMP
STATE
DIFF
2022-10-09 08:50:00
A
10
2022-10-09 09:00:00
A
25
2022-10-09 09:25:00
B
10
2022-10-09 09:35:00
C
1405
2022-10-10 09:00:00
C
1440
2022-10-11 09:00:00
C
1440
2022-10-12 09:00:00
C
35
2022-10-12 09:35:00
D
null
fiddle

Group by with Unix time stamps

I am trying write a query where time stamps are in Unix format.
The objective of the query is group by these time stamps in five minute segments and to count each unique Id in those segments.
Is there a simple way of doing this?
The result looking for this
Time_utc Id count
25/07/2019 1600 1 3
25/07/2019 1600 2 1
25/07/2019 1605 1 4
You haven't shown data, so as a starting point you can group the Unix timestamps by dividing by 300 (for 5 minutes worth of seconds):
select 300 * floor(unix_ts/300) as unix_five_minute,
timestamp '1970-01-01 00:00:00 UTC'
+ (300*floor(unix_ts/300)) * interval '1' second as oracle_timestamp,
count(*)
from cte2
group by floor(unix_ts/300);
or if you have millisecond precision adjust by a factor of 1000:
select 300000 * floor(unix_ts/300000) as unix_five_minute,
timestamp '1970-01-01 00:00:00 UTC'
+ (300*floor(unix_ts/300000)) * interval '1' second as oracle_timestamp,
count(*)
from cte2
group by floor(unix_ts/300000);
Demo using made-up data generated from current time:
-- CTEs to generate some sample data
with cte1 (oracle_interval) as (
select systimestamp - level * interval '42' second
- timestamp '1970-01-01 00:00:00.0 UTC'
from dual
connect by level <= 30
),
cte2 (unix_ts) as (
select trunc(
extract(day from oracle_interval) * 86400000
+ extract(hour from oracle_interval) * 3600000
+ extract(minute from oracle_interval) * 60000
+ extract(second from oracle_interval) * 1000
)
from cte1
)
-- actual query
select 300000 * floor(unix_ts/300000) as unix_five_minute,
timestamp '1970-01-01 00:00:00 UTC'
+ (300*floor(unix_ts/300000)) * interval '1' second as oracle_timestamp,
count(*)
from cte2
group by floor(unix_ts/300000);
UNIX_FIVE_MINUTE ORACLE_TIMESTAMP COUNT(*)
---------------- ------------------------- ----------------
1564072500000 2019-07-25 16:35:00.0 UTC 7
1564072200000 2019-07-25 16:30:00.0 UTC 7
1564071600000 2019-07-25 16:20:00.0 UTC 4
1564071900000 2019-07-25 16:25:00.0 UTC 8
1564072800000 2019-07-25 16:40:00.0 UTC 4
Unix time stamps such as 155639.600 or 155639.637
Those are unusual values; Unix/epoch times are usually 10-digit numbers, or 13 digits for millisecond precision. Assuming (or rather, guessing) that they are tenths of a second for some reason:
-- CTE for sample data
with cte (unix_ts) as (
select 155639.600 from dual
union all
select 155639.637 from dual
)
-- actual query
select 300 * floor(unix_ts*10000/300) as unix_five_minute,
timestamp '1970-01-01 00:00:00 UTC'
+ (300*floor(unix_ts*10000/300)) * interval '1' second as oracle_timestamp,
count(*)
from cte
group by floor(unix_ts*10000/300);
UNIX_FIVE_MINUTE ORACLE_TIMESTAMP COUNT(*)
---------------- ------------------------- ----------------
1556396100 2019-04-27 20:15:00.0 UTC 1
1556395800 2019-04-27 20:10:00.0 UTC 1
The 10000/300 could be simplified to 100/3, but I think it's clearer left as it is.

cast two separate columns which has hour ( datatype number) and minutes ( datatype number) to time datatype and subtract 90 minutes in oracle

I have two separate columns for hours and minutes in my table and I have a report where i should be subtracting 90 minutes from total time put together or ( 1 hour from hour field) and 30 minutes from minutes field. The output can be in minutes or hours.
I tried "to_char ( hours_column -1,'00' ) || ':' || to_char ( minutes_column -30,'00' ) AS "MAX_TIME" " - this fails when I have time like 9:00 I get 8:-30 as the output when I need to get 7:30.
I came up with some sql code with DATEADD and cast functions which worked but it fails when I implement it in Oracle.
Select Substring(Cast(DATEADD(minute, -90, Cast(hourscolumn + ':' + minutes column as Time)) as varchar(20)),1,5) as max_time
Can someone help me to implement the above code in Oracle? I'm just trying to deduct 90 minutes by putting the hours and minutes columns together.
Something like this?
test CTE represents your data. How come you got that (bad) idea? Who/what prevents you from storing 32 hours and 87 minutes into those columns?
query itself contains
time: the way you create a valid date value. It'll fail if hours and/or minutes are invalid (such as previously mentioned 32:87)
subtracted: subtract 90 minutes from time; (24 * 60) represents 24 hours in a day, 60 minutes in an hour. It'll contain both date and time component
the final result is achieved by applying to_char with appropriate format mask (hh24:mi) to the subtracted value
SQL> alter session set nls_Date_format = 'dd.mm.yyyy hh24:mi';
Session altered.
SQL> with test (hours, minutes) as
2 (select '09', '00' from dual union all
3 select '23', '30' from dual union all
4 select '00', '20' from dual
5 )
6 select hours,
7 minutes,
8 to_date(hours||minutes, 'hh24mi') time,
9 --
10 to_date(hours||minutes, 'hh24mi') - 90 / (24 * 60) subtracted,
11 --
12 to_char(to_date(hours||minutes, 'hh24mi') - 90 / (24 * 60), 'hh24:mi') result
13 from test;
HO MI TIME SUBTRACTED RESUL
-- -- ---------------- ---------------- -----
09 00 01.07.2019 09:00 01.07.2019 07:30 07:30
23 30 01.07.2019 23:30 01.07.2019 22:00 22:00
00 20 01.07.2019 00:20 30.06.2019 22:50 22:50
SQL>
Use NUMTODSINTERVAL to convert the hours and minutes to INTERVAL data types and then you can subtract INTERVAL '90' MINUTE and EXTRACT the resulting hour and minute components.
Oracle Setup:
CREATE TABLE table_name ( hours_column, minutes_column ) AS
SELECT 0, 0 FROM DUAL UNION ALL
SELECT 1, 30 FROM DUAL UNION ALL
SELECT 2, 45 FROM DUAL UNION ALL
SELECT 3, 0 FROM DUAL UNION ALL
SELECT 27, 59 FROM DUAL
Query:
SELECT EXTRACT( HOUR FROM time ) + EXTRACT( DAY FROM time ) * 24 AS hours,
EXTRACT( MINUTE FROM time ) AS minutes,
time,
TO_CHAR( EXTRACT( HOUR FROM time ) + EXTRACT( DAY FROM time ) * 24, '00' )
|| ':' || TO_CHAR( ABS( EXTRACT( MINUTE FROM time ) ), 'FM00' ) AS as_string
FROM (
SELECT NUMTODSINTERVAL( hours_column, 'HOUR' )
+ NUMTODSINTERVAL( minutes_column, 'MINUTE' )
- INTERVAL '90' MINUTE AS time
FROM table_name
)
Output:
HOURS | MINUTES | TIME | AS_STRING
----: | ------: | :---------------------------- | :--------
-1 | -30 | -000000000 01:30:00.000000000 | -01:30
0 | 0 | +000000000 00:00:00.000000000 | 00:00
1 | 15 | +000000000 01:15:00.000000000 | 01:15
1 | 30 | +000000000 01:30:00.000000000 | 01:30
26 | 29 | +000000001 02:29:00.000000000 | 26:29
db<>fiddle here

ORA-01830 Failure at to_date function if the field contains more than 5 Digits

I have a larger SQL Statement, which works just fine, until a field which displays the median duration contains more than 5 Digits in the used field for displaying (B.AGGAVG)
to_char(to_date(ROUND(B.AGGAVG), 'SSSSS'), 'HH24:MI:SS') Mittlere_Dauer,
This line is causing the ORA Failure, because without its working fine.
It is also working fine as long as B.AGGAVG contains at most 5 Digits.
The SSSSS format model represents the number of seconds past midnight on a day, so it can't accept values of 86400 or above. You will get "ORA-01830: date format picture ends before converting entire input string" for values with more than five digits, but you will also get "ORA-01853: seconds in day must be between 0 and 86399" for values between 86400 and 99999.
How you handle this depends on what you want the result to be. You could convert the number of seconds to an interval data type; taking an arbitrary value of 250,000 seconds:
select numtodsinterval(250000, 'SECOND') as result
from dual;
RESULT
-------------------
+02 21:26:40.000000
but you can't directly format those. Or you could manually deconstruct the number into time components (based on the number of seconds in a day), and concatenate them into a string:
select trunc(250000/86400)
||' '|| trunc(mod(250000, 86400)/3600)
||':'|| trunc(mod(250000, 3600)/60)
||':'|| mod(250000, 60) result
from dual;
RESULT
----------
2 21:26:40
If you don't want a separate number of days then you could usual the manual approach to get the total number of hours (which can be more than 24) instead:
select trunc(250000/3600)
||':'|| trunc(mod(250000, 3600)/60)
||':'|| mod(250000, 60) result
from dual;
RESULT
--------
69:26:40
You could also add the seconds to a nominal date of the first day of any year, but this will only work if the values are always above a day:
select to_char(date '1970-01-01' + (250000/86400) - 1, 'FMDDD FMHH24:MI:SS') as result
from dual;
RESULT
----------
2 21:26:40
The problem is the -1 to deal with the Julian date starting from 1 not zero. With a shorter value that goes a bit wrong:
select to_char(date '1970-01-01' + (1000/86400) - 1, 'FMDDD FMHH24:MI:SS') as result
from dual;
RESULT
------------
365 00:16:40
which you could deal with with additional logic, e.g. modifying the format model based on the original value so it only shows the number of days for larger values and shows a fixed zero (or nothing) for smaller values:
select to_char(date '1970-01-01' + (1000/86400) - 1,
case when 1000 >= 86400 then 'FMDDD FMHH24:MI:SS' else '"0" HH24:MI:SS' end) as result
from dual;
RESULT
---------------------------------------------------------------------------
0 00:16:40
or if you don't want the zero:
select to_char(date '1970-01-01' + (1000/86400) - 1,
case when 1000 >= 86400 then 'FMDDD FMHH24:MI:SS' else 'HH24:MI:SS' end) as result
from dual;
RESULT
---------------------------------------------------------------------------
00:16:40
... but it's probably not worth the effort.
Whichever approach you use, who/whatever consumes this needs to be able to handle and understand whatever value is presented.
Demo of various source values from a CTE, showing the output from all the above methods:
with b (aggavg) as (
select 0 from dual
union all select 0.123 from dual
union all select 10 from dual
union all select 100 from dual
union all select 1000 from dual
union all select 10000 from dual
union all select 86399 from dual
union all select 86400 from dual
union all select 100000 from dual
union all select 250000 from dual
union all select 1000000 from dual
)
select b.aggavg,
numtodsinterval(round(b.aggavg), 'SECOND') as result1,
trunc(round(b.aggavg)/86400)
||' '|| trunc(mod(round(b.aggavg), 86400)/3600)
||':'|| trunc(mod(round(b.aggavg), 3600)/60)
||':'|| mod(round(b.aggavg), 60) result2,
trunc(round(b.aggavg)/3600)
||':'|| trunc(mod(round(b.aggavg), 3600)/60)
||':'|| mod(round(b.aggavg), 60) result3,
to_char(date '1970-01-01' + (round(b.aggavg)/86400) - 1,
case when round(b.aggavg) >= 86400 then 'FMDDD FMHH24:MI:SS'
else '"0" HH24:MI:SS' end) as result4,
to_char(date '1970-01-01' + (round(b.aggavg)/86400) - 1,
case when round(b.aggavg) >= 86400 then 'FMDDD FMHH24:MI:SS'
else 'HH24:MI:SS' end) as result5
from b;
AGGAVG RESULT1 RESULT2 RESULT3 RESULT4 RESULT5
---------- ------------------- ----------- ----------- ----------- -----------
0 +00 00:00:00.000000 0 0:0:0 0:0:0 0 00:00:00 00:00:00
.123 +00 00:00:00.000000 0 0:0:0 0:0:0 0 00:00:00 00:00:00
10 +00 00:00:10.000000 0 0:0:10 0:0:10 0 00:00:10 00:00:10
100 +00 00:01:40.000000 0 0:1:40 0:1:40 0 00:01:40 00:01:40
1000 +00 00:16:40.000000 0 0:16:40 0:16:40 0 00:16:40 00:16:40
10000 +00 02:46:40.000000 0 2:46:40 2:46:40 0 02:46:40 02:46:40
86399 +00 23:59:59.000000 0 23:59:59 23:59:59 0 23:59:59 23:59:59
86400 +01 00:00:00.000000 1 0:0:0 24:0:0 1 00:00:00 1 00:00:00
100000 +01 03:46:40.000000 1 3:46:40 27:46:40 1 03:46:40 1 03:46:40
250000 +02 21:26:40.000000 2 21:26:40 69:26:40 2 21:26:40 2 21:26:40
1000000 +11 13:46:40.000000 11 13:46:40 277:46:40 11 13:46:40 11 13:46:40

how to convert HH:MM representation to minutes in oracle sql

how to convert varchar(hh:mm) to minutes in oracle sql.
For example:
HH:MM Minutes
08:00 480
08:45 525
07:57 477
This will work even if the duration is 24 hours or greater:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE durations ( duration ) AS
SELECT '00:30' FROM DUAL UNION ALL
SELECT '07:57' FROM DUAL UNION ALL
SELECT '08:00' FROM DUAL UNION ALL
SELECT '12:00' FROM DUAL UNION ALL
SELECT '20:01' FROM DUAL UNION ALL
SELECT '23:59' FROM DUAL UNION ALL
SELECT '24:00' FROM DUAL UNION ALL
SELECT '24:59' FROM DUAL;
Query 1:
SELECT duration,
( (
DATE '1970-01-01'
+ NUMTODSINTERVAL( SUBSTR( duration, 1, INSTR( duration, ':' ) - 1 ), 'HOUR' )
+ NUMTODSINTERVAL( SUBSTR( duration, INSTR( duration, ':' ) + 1 ), 'MINUTE' )
)
- DATE '1970-01-01'
) * 24 * 60 AS Minutes
FROM durations
Results:
| DURATION | MINUTES |
|----------|---------|
| 00:30 | 30 |
| 07:57 | 477 |
| 08:00 | 480 |
| 12:00 | 720 |
| 20:01 | 1201 |
| 23:59 | 1439 |
| 24:00 | 1440 |
| 24:59 | 1499 |
However, there is an INTERVAL DAY TO SECOND data type that would be better suited to your data:
CREATE TABLE your_table (
duration INTERVAL DAY TO SECOND
);
Then you can just do:
INSERT INTO your_table ( duration ) VALUES ( INTERVAL '08:00' HOUR TO MINUTE );
To get the number of minutes you can then simply do:
SELECT ( ( DATE '1970-01-01' + duration ) - DATE '1970-01-01' ) *24*60 AS minutes
FROM your_table
Try this
TO_NUMBER(SUBSTR('(08:00)',2,INSTR('(08:00)',':')-2))*60+TO_NUMBER(SUBSTR('(08:00)',INSTR('(08:00)',':')+1,2))
If you can convert your input to a real date first, the task becomes much easier. Here, I have shamelessly appended the time to a fake date to create a date such as 2017-01-01 00:30. To find out the number of minutes since midnight, you simply subtract the date for "midnight". It will return the difference in days, so you need to multiply by number of minutes per day to get what you want.
select time
,(to_date('2017-01-01 ' || time, 'yyyy-mm-dd hh24:mi') - date '2017-01-01') * 24 * 60 as minutes
from (select '00:30' as time from dual union all
select '08:00' as time from dual union all
select '08:30' as time from dual union all
select '12:00' as time from dual union all
select '23:59' as time from dual
);
Here is some sample input and output
time minutes
==== =======
00:30 30
08:00 480
08:30 510
12:00 720
23:59 1 439
If you require to Print 08:00 hours as 480 minutes,
Extract the Digit before : and multply with 60 and add the digit after :. So you can convert the HH:MM representation in to minutes.
SELECT REGEXP_SUBSTR(ATT.workdur,'[^:]+',1,1)*60 + REGEXP_SUBSTR(ATT.workdur,'[^:]+',1,2) MINUTES FROM DUAL;