Wrong conversion of date-string to date - sql

When running these statements:
select to_date('201103270100', 'YYYYMMDDHH24MI') from dual;
select to_date('201103270130', 'YYYYMMDDHH24MI') from dual;
I get these results:
27/3/2011 1:00:00
27/3/2011 1:30:00
which are correct.
But when running:
select to_date('201103270200', 'YYYYMMDDHH24MI') from dual
I get a wrong hour as result
27/3/2011 3:00:00
More samples:
select to_date('201103270215', 'YYYYMMDDHH24MI') from dual
select to_date('201103270245', 'YYYYMMDDHH24MI') from dual
select to_date('201103270300', 'YYYYMMDDHH24MI') from dual
select to_date('201103270330', 'YYYYMMDDHH24MI') from dual
27/3/2011 3:15:00
27/3/2011 3:45:00
27/3/2011 3:00:00
27/3/2011 3:30:00
When the date is 20110326, I get the correct result.
This is crazy... Is Oracle SQL bullying me with this conversion?
Help is welcome!

Is Oracle SQL bullying me with this conversion?
Oracle is too dumb to bully a developer, unless the developer tries to bully it ;-)
There can't be different outputs without any reason. In Europe, in the year 2011, the day light saving was with effect from 27th March, 2011 at 2:00 AM. Source is wikipedia.
So, keeping in mind the day light saving, let's see the results for the datetime after 2:00 AM:
SQL> SELECT to_char(to_date('201103270100', 'YYYYMMDDHH24MI'), 'DD/MM/YYYY HH24:MI:SS') FROM dual;
TO_CHAR(TO_DATE('20
-------------------
27/03/2011 01:00:00
SQL> SELECT to_char(to_date('201103270130', 'YYYYMMDDHH24MI'), 'DD/MM/YYYY HH24:MI:SS') FROM dual;
TO_CHAR(TO_DATE('20
-------------------
27/03/2011 01:30:00
SQL> SELECT to_char(to_date('201103270200', 'YYYYMMDDHH24MI'), 'DD/MM/YYYY HH24:MI:SS') FROM dual;
TO_CHAR(TO_DATE('20
-------------------
27/03/2011 02:00:00
SQL> SELECT to_char(to_date('201103270215', 'YYYYMMDDHH24MI'), 'DD/MM/YYYY HH24:MI:SS') FROM dual;
TO_CHAR(TO_DATE('20
-------------------
27/03/2011 02:15:00
SQL> SELECT to_char(to_date('201103270245', 'YYYYMMDDHH24MI'), 'DD/MM/YYYY HH24:MI:SS') FROM dual;
TO_CHAR(TO_DATE('20
-------------------
27/03/2011 02:45:00
SQL> SELECT to_char(to_date('201103270300', 'YYYYMMDDHH24MI'), 'DD/MM/YYYY HH24:MI:SS') FROM dual;
TO_CHAR(TO_DATE('20
-------------------
27/03/2011 03:00:00
SQL> SELECT to_char(to_date('201103270330', 'YYYYMMDDHH24MI'), 'DD/MM/YYYY HH24:MI:SS') FROM dual;
TO_CHAR(TO_DATE('20
-------------------
27/03/2011 03:30:00
SQL>
If your provided data and examples are correct, then this should be the reason.
So, you should see the time portion exactly at 2:00 AM to shift to 3:00 AM. Similarly, 2:15 AM would be 3:15 AM. Just add an hour to the time for those which are after 2:00 AM.

Related

I have a table containing in_time and out_time in the format 'HH12:MI'(varchar2).I want to calculate the total time in hours between this column

select 24 * ROUND(
to_date(OUT_TIME, 'YYYY-MM-DD hh24:mi')
- to_date(IN_TIME, 'YYYY-MM-DD hh24:mi'),
2
) diff_hours
from attendances
where employee_id = 1001;
select 24 * ROUND(
to_date('2009-07-07 23:44', 'YYYY-MM-DD hh24:mi')
- to_date('2009-07-07 19:30', 'YYYY-MM-DD hh24:mi'),
2
) diff_hours
from dual;
If you stored (date)time values as strings (which is a bad idea), you'll have to use that format with to_date. Using sample data you posted:
SQL> with test (employee, in_time, out_time) as
2 (select 'Palash', '08:45 PM', '08:45 PM' from dual union all
3 select 'Palash', '07:40 PM', '07:45 PM' from dual union all
4 select 'Sagor' , '08:10 PM', '08:43 PM' from dual
5 )
6 select employee, in_time, out_time,
7 round((to_date(out_time, 'hh:mi pm') - to_date(in_time, 'hh:mi pm')) * 24, 2) diff
8 from test;
EMPLOY IN_TIME OUT_TIME DIFF
------ -------- -------- ----------
Palash 08:45 PM 08:45 PM 0
Palash 07:40 PM 07:45 PM .08
Sagor 08:10 PM 08:43 PM .55
SQL>

Oracle find gaps split by allowed slots

I have this table of calendar gaps (for a report):
begindate enddate
2017-12-14 16:45:00 2017-12-14 21:45:00
2017-12-15 17:45:00 2017-12-16 10:00:00
The second line range on 2 different days.
I want to split it with given 'allowed' time slots, gaps can only be between 7am and 8pm (20:00:00).
So the result should be 3 lines:
begindate enddate
2017-12-14 16:45:00 2017-12-14 20:00:00
2017-12-15 17:45:00 2017-12-15 20:00:00
2017-12-16 07:00:00 2017-12-16 10:00:00
How can I do that in sql (oracle function allowed).
This was an interesting one, here is my answer:
WITH test_data (beginDate, endDate) AS
(
SELECT TO_DATE('2017-12-14 16:45:00', 'YYYY-MM-DD HH24:MI:SS'),
TO_DATE('2017-12-14 21:45:00', 'YYYY-MM-DD HH24:MI:SS') FROM DUAL
UNION ALL
SELECT TO_DATE('2017-12-15 17:45:00', 'YYYY-MM-DD HH24:MI:SS'),
TO_DATE('2017-12-16 10:00:00', 'YYYY-MM-DD HH24:MI:SS') FROM DUAL
UNION ALL
SELECT TO_DATE('2017-12-15 01:45:00', 'YYYY-MM-DD HH24:MI:SS'),
TO_DATE('2017-12-15 06:00:00', 'YYYY-MM-DD HH24:MI:SS') FROM DUAL
),
split_dates (beginDate, endDate, actEnd, remaining, lvl) AS
(
SELECT beginDate, LEAST(endDate, TRUNC(beginDate)+1), endDate, TRUNC(endDate) - TRUNC(beginDate), 1
FROM test_data
UNION ALL
SELECT TRUNC(beginDate)+lvl, LEAST(actEnd, TRUNC(beginDate)+lvl+1), actEnd, remaining-1, lvl+1
FROM split_dates sd
WHERE sd.remaining > 0
)
SELECT TO_CHAR(GREATEST(sd.beginDate, TRUNC(sd.beginDate)+7/24), 'YYYY-MM-DD HH24:MI:SS') AS beginDate,
TO_CHAR(LEAST(sd.endDate, TRUNC(sd.beginDate)+5/6), 'YYYY-MM-DD HH24:MI:SS') AS endDate
FROM split_dates sd
WHERE GREATEST(sd.beginDate, TRUNC(sd.beginDate)+7/24) <= LEAST(sd.endDate, TRUNC(sd.beginDate)+5/6);
The problem is two-fold:
You need to split multi-day records in separate rows. I accomplished this with the split_records CTE.
You need to overlay your valid times on the calculated splits and check that the new times are valid.
I created a DBFiddle to show you the query in action (Link)

SQL: I need to format a Select Distinct Statement with a Case as one of the values

So I'm trying to select the distinct operators from a table with a time component that is formatted like this: 'MM/DD/YYYY HH:MI:SS AM.'
The logic is this:
(if C_StartTime >= date(C_StartTime) + 6:00:00 AM
AND C_StartTime < date(C_StartTime) + 5:59:59AM
then C_StartTime,'MM/DD/YYYY'
ELSE (C_StartTime,'MM/DD/YYYY')-1)
AS DateOnly
I can select distinct operators right now but they sign in and out a few times a day so the time is different. It should be noted that "Today" at this company is 3/13/19 6:00:00 AM to 3/14/19 5:59:59 AM.
Below is the final code I tried executing
SELECT
DISTINCT (
(
CASE WHEN C_StartTime >= date(C_StartTime) +.25
AND C_StartTime < date(C_StartTime) +.9999
THEN date(C_StartTime)
ELSE date(C_StartTime) -1
) as DateOnly,
C_operator,
C_operatorname,
C_WorkCentreName
FROM
OPERATORTABLE
WHERE
.....
EDIT>>>>>>
This is what I get.
This is what I need
I'm Looking for the operator number, the operator name, and the date only (with the knowledge that 1/4/2019 5:59:00 AM = 1/3/2019
I think you're probably after something like trunc(c_starttime - 6/24) + 6/24 to get the day to start at 6am instead of midnight:
WITH dts AS (SELECT to_date('13/03/2019 05:59:59', 'dd/mm/yyyy hh24:mi:ss') dt FROM dual UNION ALL
SELECT to_date('13/03/2019 06:00:00', 'dd/mm/yyyy hh24:mi:ss') dt FROM dual UNION ALL
SELECT to_date('13/03/2019 06:00:01', 'dd/mm/yyyy hh24:mi:ss') dt FROM dual UNION ALL
SELECT to_date('14/03/2019 05:59:59', 'dd/mm/yyyy hh24:mi:ss') dt FROM dual UNION ALL
SELECT to_date('14/03/2019 06:00:00', 'dd/mm/yyyy hh24:mi:ss') dt FROM dual)
SELECT dt,
dt - 6/24 adj_dt,
TRUNC(dt - 6/24) trunc_adj_dt,
TRUNC(dt - 6/24) + 6/24 adj_start_of_dt
FROM dts;
DT ADJ_DT TRUNC_ADJ_DT ADJ_START_OF_DT
------------------- ------------------- ------------------- -------------------
13/03/2019 05:59:59 12/03/2019 23:59:59 12/03/2019 00:00:00 12/03/2019 06:00:00
13/03/2019 06:00:00 13/03/2019 00:00:00 13/03/2019 00:00:00 13/03/2019 06:00:00
13/03/2019 06:00:01 13/03/2019 00:00:01 13/03/2019 00:00:00 13/03/2019 06:00:00
14/03/2019 05:59:59 13/03/2019 23:59:59 13/03/2019 00:00:00 13/03/2019 06:00:00
14/03/2019 06:00:00 14/03/2019 00:00:00 14/03/2019 00:00:00 14/03/2019 06:00:00
I've selected the adj_dt and trunc_adj_dt columns so that you can see how the adj_start_of_dt column was calculated from the original dt column.
You may not need to output 6am on the start date column, so you can skip that (i.e. it's trunc_adj_dt that is the column you'd be after in that case).

Find the difference between two dates in hours and minutes

I am trying to come up with a way to calculate the difference between two dates in hours and minutes.
I have a table with two columns Start Date and TimeStamp:
Start Date Timestamp
-------------------- --------------------
05/JAN/2016 05:30:00 01/JAN/2016 10:02:29
30/JAN/2016 06:10:00 18/JAN/2016 19:24:00
23/JAN/2016 06:10:00 08/JAN/2016 10:46:00
05/JAN/2016 05:30:00 30/DEC/2015 16:07:00
23/JAN/2016 06:10:00 08/JAN/2016 12:18:05
01/JAN/2016 14:10:00 16/DEC/2015 16:36:56
01/JAN/2016 14:10:00 16/DEC/2015 11:41:00
03/JAN/2016 05:15:00 02/JAN/2016 11:23:15
03/JAN/2016 05:15:00 02/JAN/2016 07:52:00
I use the query:
select ROUND(RM_LIVE.CRWGNDACTTIME.GNDACTSTARTRM_LIVE.TRANSACTIONLOG.TIMESTAMP,2)
AS "Difference"
from Transaction;
The query result is:
0.002721428571428571428571428571428571428571
0.008178571428571428571428571428571428571429
0.0105785714285714285714285714285714285714
0.003971428571428571428571428571428571428571
Expected result:
133:23
91:28
355:24
353:52
274:46
I got that expected result in Excel using this formula:
= MAX(T982+U982,W982+V982) - MIN(T982+U982,W982+V982)
How can I get the same result in Oracle SQL?
CASE
WHEN trunc(24 * abs(RM_LIVE.TRANSACTIONLOG.TIMESTAMP
- RM_LIVE.CRWGNDACTTIME.GNDACTSTART))
||':'|| lpad(round(60 * mod(24 * abs(RM_LIVE.TRANSACTIONLOG.TIMESTAMP
- RM_LIVE.CRWGNDACTTIME.GNDACTSTART), 1)), 2, '0') <= '11:00' THEN 'LESS'
ELSE 'MORE'
END AS "mORE/LESS",
386:29 1055 01-JAN-16 16-DEC-15 MORE
**102:41 1055 08-NOV-15 04-NOV-15 LESS**
381:33 1055 01-JAN-16 16-DEC-15 MORE
176:45 1055 20-NOV-15 12-NOV-15 MORE
**119:54 1055 08-NOV-15 03-NOV-15 LESS**
I've shown a couple of variations with explanations in this answer, but it seems to be doing slightly more than you want - you don't want to see the seconds - and doesn't allow more than 100 hours.
The simplest way to get the output you want is with:
trunc(24 * (RM_LIVE.CRWGNDACTTIME.GNDACTSTART
- RM_LIVE.TRANSACTIONLOG.TIMESTAMP))
||':'|| lpad(round(60 * mod(24 * (RM_LIVE.CRWGNDACTTIME.GNDACTSTART
- RM_LIVE.TRANSACTIONLOG.TIMESTAMP), 1)), 2, '0')
as difference
The first part gets the whole number of hours, which is similar to a method you added in a comment, but truncating instead of rounding to only get the whole hours. Then there's a colon separator. Then the minutes are calculated by getting the remainder from the hours calculation - via mod() - which is the fractional number of hours, and multiplying that by 60. The lpad() adds a leading zero to the number of minutes, but you coudl use to_char() instead.
If you have a mix of ranges where timestamp could be before or after the start time then you can use the abs() function to always get a positive result.
trunc(24 * abs(RM_LIVE.CRWGNDACTTIME.GNDACTSTART
- RM_LIVE.TRANSACTIONLOG.TIMESTAMP))
||':'|| lpad(round(60 * mod(24 * abs(RM_LIVE.CRWGNDACTTIME.GNDACTSTART
- RM_LIVE.TRANSACTIONLOG.TIMESTAMP), 1)), 2, '0')
as difference
As a demo with your data mocked up in a single table:
create table your_table(id, start_time, timestamp) as
select 1, to_date ('05/JAN/2016 05:30:00', 'DD/MON/YYYY HH24:MI:SS'), to_date('01/JAN/2016 10:02:29', 'DD/MON/YYYY HH24:MI:SS') from dual
union all select 2, to_date ('30/JAN/2016 06:10:00', 'DD/MON/YYYY HH24:MI:SS'), to_date('18/JAN/2016 19:24:00', 'DD/MON/YYYY HH24:MI:SS') from dual
union all select 3, to_date ('23/JAN/2016 06:10:00', 'DD/MON/YYYY HH24:MI:SS'), to_date('08/JAN/2016 10:46:00', 'DD/MON/YYYY HH24:MI:SS') from dual
union all select 4, to_date ('05/JAN/2016 05:30:00', 'DD/MON/YYYY HH24:MI:SS'), to_date('30/DEC/2015 16:07:00', 'DD/MON/YYYY HH24:MI:SS') from dual
union all select 5, to_date ('23/JAN/2016 06:10:00', 'DD/MON/YYYY HH24:MI:SS'), to_date('08/JAN/2016 12:18:05', 'DD/MON/YYYY HH24:MI:SS') from dual
union all select 6, to_date ('01/JAN/2016 14:10:00', 'DD/MON/YYYY HH24:MI:SS'), to_date('16/DEC/2015 16:36:56', 'DD/MON/YYYY HH24:MI:SS') from dual
union all select 7, to_date ('01/JAN/2016 14:10:00', 'DD/MON/YYYY HH24:MI:SS'), to_date('16/DEC/2015 11:41:00', 'DD/MON/YYYY HH24:MI:SS') from dual
union all select 8, to_date ('03/JAN/2016 05:15:00', 'DD/MON/YYYY HH24:MI:SS'), to_date('02/JAN/2016 11:23:15', 'DD/MON/YYYY HH24:MI:SS') from dual
union all select 9, to_date ('03/JAN/2016 05:15:00', 'DD/MON/YYYY HH24:MI:SS'), to_date('02/JAN/2016 07:52:00', 'DD/MON/YYYY HH24:MI:SS') from dual
union all select 10, to_date ('16/JAN/2016 11:15:00', 'DD/MON/YYYY HH24:MI:SS'), to_date('16/JAN/2016 12:44:00', 'DD/MON/YYYY HH24:MI:SS') from dual
union all select 11, to_date ('16/JAN/2016 11:15:00', 'DD/MON/YYYY HH24:MI:SS'), to_date('16/JAN/2016 12:50:00', 'DD/MON/YYYY HH24:MI:SS') from dual;
The equivalent query:
select start_time, timestamp, trunc(24 * abs(start_time - timestamp))
||':'|| lpad(round(60 * mod(24 * abs(start_time - timestamp), 1)), 2, '0')
as difference
from your_table
order by id;
START_TIME TIMESTAMP DIFFERENCE
------------------- ------------------- ----------
2016-01-05 05:30:00 2016-01-01 10:02:29 91:28
2016-01-30 06:10:00 2016-01-18 19:24:00 274:46
2016-01-23 06:10:00 2016-01-08 10:46:00 355:24
2016-01-05 05:30:00 2015-12-30 16:07:00 133:23
2016-01-23 06:10:00 2016-01-08 12:18:05 353:52
2016-01-01 14:10:00 2015-12-16 16:36:56 381:33
2016-01-01 14:10:00 2015-12-16 11:41:00 386:29
2016-01-03 05:15:00 2016-01-02 11:23:15 17:52
2016-01-03 05:15:00 2016-01-02 07:52:00 21:23
2016-01-16 11:15:00 2016-01-16 12:44:00 1:29
2016-01-16 11:15:00 2016-01-16 12:50:00 1:35
You can't easily compare the string value you want - and it has to be a string with a value like 91:28 - with anything else because string comparison of numbers doesn't work well. As you've see, comparing '119:54' with '11:00' is effectively comparing the third character of each string since the first two are the same, so 9 with :.
It would be simpler to leave it as a decimal fraction for comparison:
CASE
WHEN round(24 * abs(RM_LIVE.TRANSACTIONLOG.TIMESTAMP
- RM_LIVE.CRWGNDACTTIME.GNDACTSTART), 2) <= 11 THEN 'LESS"
ELSE 'MORE'
END AS "mORE/LESS",
For the 91:28 example, that will compare the decimal fraction version 91.46 instead; and for 119:54 will compare 119.9, which is more than 11; 102:41 will be compared as 102.68, which is also more than 11.
Or you could simplify it slightly by dividing the fixed value by 24 (hours in a day) instead of multiplying the time difference:
CASE
WHEN abs(RM_LIVE.TRANSACTIONLOG.TIMESTAMP
- RM_LIVE.CRWGNDACTTIME.GNDACTSTART) <= 11/24 THEN 'LESS"
ELSE 'MORE'
END AS "mORE/LESS",

Oracle. Correct offset in timestamp with timezone

I'm executing this select now
SELECT FROM_TZ(to_timestamp('2015-08-08 10:00:00', 'yyyy-mm-dd hh24:mi:ss'),'Asia/Singapore') AT TIME ZONE 'UTC'
FROM DUAL
I'm interesting in time, in this case time is 02:00 AM, because Singapore has difference between UTC in 8 hours.Everything is ok, but, if I'm changing month from 08 to 01, I'm expecting to get 03:00 AM, because it was winter time in Singapore, but I get 02:00 AM again. So the question is, how could I get the correct result with correct offset?
Asia/Singapore does not have any Daylight saving times, see here: Singapore Standard Time
Crazy, Oracle implemented this list properly:
SELECT FROM_TZ(TO_TIMESTAMP('2015-01-08 10:00:00', 'yyyy-mm-dd hh24:mi:ss'), 'Asia/Singapore') AS TS FROM DUAL;
TS
----------------------------------------
08.01.2015 10:00:00.000000000 +08:00
1 row selected.
SELECT FROM_TZ(TO_TIMESTAMP('1970-01-08 10:00:00', 'yyyy-mm-dd hh24:mi:ss'), 'Asia/Singapore') AS TS FROM DUAL;
TS
----------------------------------------
08.01.1970 10:00:00.000000000 +07:30
1 row selected.
SELECT FROM_TZ(TO_TIMESTAMP('1943-01-08 10:00:00', 'yyyy-mm-dd hh24:mi:ss'), 'Asia/Singapore') AS TS FROM DUAL;
TS
----------------------------------------
08.01.1943 10:00:00.000000000 +09:00
1 row selected.
SELECT FROM_TZ(TO_TIMESTAMP('1940-01-08 10:00:00', 'yyyy-mm-dd hh24:mi:ss'), 'Asia/Singapore') AS TS FROM DUAL;
TS
----------------------------------------
08.01.1940 10:00:00.000000000 +07:20
1 row selected.
SELECT FROM_TZ(TO_TIMESTAMP('1920-01-08 10:00:00', 'yyyy-mm-dd hh24:mi:ss'), 'Asia/Singapore') AS TS FROM DUAL;
TS
----------------------------------------
08.01.1920 10:00:00.000000000 +07:00
1 row selected.