I'm looking for some help in Oracle SQL. I need to query date and time in the where clause to find shift data based on current date. There are 3 shifts, 5am to 1pm, 1pm to 9pm and 9pm to 5am(next day morning. For example
SELECT 'T1' AS SHIFT, WORK_CENTER, SUM(CASE WHEN AQL='PASS' THEN 1 ELSE 0) END AS AQL_PASSED
FROM Z_INSPECTION_DEFECTS
WHERE DATE_TIME >= TO_DATE((SELECT CONCAT(TO_CHAR(CURRENT_DATE, ''DD-MON-YYYY''), '' 5:00:00 '') FROM DUAL) , ''DD-MON-YYYY HH:MI:SS '') AND
DATE_TIME < TO_DATE((SELECT CONCAT(TO_CHAR(CURRENT_DATE, ''DD-MON-YYYY''), '' 1:00:00 '') FROM DUAL) , ''DD-MON-YYYY HH:MI:SS '')
I do not get any results from this query. The date time field is a timestamp on local Los Angeles time.
The immediate problem is that you're looking dor times that are after 5am and before 1am, which logically means nothing matches - as no time can fulfil both at once. You can use 24-hour times instead:
WHERE DATE_TIME >= TO_DATE((SELECT CONCAT(TO_CHAR(CURRENT_DATE, ''DD-MON-YYYY''), '' 5:00:00 '') FROM DUAL) , ''DD-MON-YYYY HH24:MI:SS '') AND
DATE_TIME < TO_DATE((SELECT CONCAT(TO_CHAR(CURRENT_DATE, ''DD-MON-YYYY''), ''13:00:00 '') FROM DUAL) , ''DD-MON-YYYY HH24:MI:SS '')
But there are other ways to get those, e.g. just without the queries against dual:
WHERE DATE_TIME >= TO_DATE(TO_CHAR(CURRENT_DATE, ''DD-MON-YYYY'') || '' 5:00:00 '', ''DD-MON-YYYY HH24:MI:SS '') AND
DATE_TIME < TO_DATE(TO_CHAR(CURRENT_DATE, ''DD-MON-YYYY'') || ''13:00:00 '', ''DD-MON-YYYY HH24:MI:SS '')
or with truncation and date arithmetic:
WHERE DATE_TIME >= TRUNC(CURRENT_DATE) + (5/24) AND
DATE_TIME < TRUNC(CURRENT_DATE) + (13/24)
You really need to be getting those times in the target time zone though, e.g.:
WHERE DATE_TIME >= FROM_TZ(CAST(TRUNC(CURRENT_DATE) + (5/24) AS TIMESTAMP), ''America/Los_Angeles'') AND
DATE_TIME < FROM_TZ(CAST(TRUNC(CURRENT_DATE) + (13/24) AS TIMESTAMP), ''America/Los_Angeles'')
You need to be careful with current_date, which is in you current session time zone, and sysdate which is in the server time zone. If your session is UTC then current_date might not give you the day you expect.
(I've stuck with escaped single quotes as that is mostly what you have in the question, implying you're probably running this with dynamic SQL; whether you need to is another matter. If you're only doing that to provide the period offsets at runtime then that wouldn't need to be dynamic...)
You can see the generated times from those calculations with:
select FROM_TZ(CAST(TRUNC(CURRENT_DATE) + (5/24) AS TIMESTAMP), 'America/Los_Angeles'),
FROM_TZ(CAST(TRUNC(CURRENT_DATE) + (13/24) AS TIMESTAMP), 'America/Los_Angeles')
from dual;
FROM_TZ(CAST(TRUNC(CURRENT_DATE)+(5/24)ASTIMESTAM FROM_TZ(CAST(TRUNC(CURRENT_DATE)+(13/24)ASTIMESTA
------------------------------------------------- -------------------------------------------------
2018-10-22 05:00:00.000000000 AMERICA/LOS_ANGELES 2018-10-22 13:00:00.000000000 AMERICA/LOS_ANGELES
Related
I'm struggling with something that seems very obvious on first sight and most probably I'm overlooking something stupid but anyway.
I need to calculate the difference between timestamp fields and convert the result (which is as I assume a timestamp ) into the number of days and the elapsed time.
I can't seem to get the cast(xx to time) wright
I made a small example
SELECT
Cast(Cast( c_date AS CHAR(10)) || ' ' || Cast( c_time AS CHAR(10)) AS TIMESTAMP(6)) AS starttime ,
Cast(Cast( e_date AS CHAR(10)) || ' ' || Cast( e_time AS CHAR(10)) AS TIMESTAMP(6)) AS endtm,
(endtm - starttime) DAY(4) TO SECOND AS difftime
,Extract(DAY From difftime) --> gives the days
,Cast(difftime AS TIME)
,Extract (HOUR From difftime)
FROM (
SELECT Cast(Current_Timestamp AS DATE) c_date,
Cast(Current_Timestamp(0) AS TIME(0)) c_time,
Cast(Current_Timestamp + Random(1,10) * INTERVAL '1' DAY AS DATE) e_date,
Cast(Current_Timestamp(0) + Random(1,24) * INTERVAL '1' HOUR + Random(1,60) * INTERVAL '1' MINUTE AS TIME(0)) e_time
) t
,Cast(difftime AS TIME) gives me the trouble
the extract day and hour works => the difftime is really a timestamp (is it ? and if not what kind of field is it then ? ).
some advise would be nice :-)
I have a generated column that calculates the time difference of time in and time out adding if over time is not empty.
ALTER TABLE tbl_dtr
ADD COLUMN total_worked_hours FLOAT(2) AS (CASE
WHEN time_out = '' then 0
WHEN over_time_out = '' then 0
WHEN time_out <> '' then (TIMESTAMPDIFF(SECOND, time_in, time_out) / 3600)
WHEN over_time_out <> '' then ((TIMESTAMPDIFF(SECOND, time_in, time_out) / 3600) + (TIMESTAMPDIFF(SECOND, over_time_in, over_time_out) / 3600))
END);
The problem here the column total_worked_hours will results null, I don't know how to convert AM/PM to timestamp to calculate timestamdiff.
id
date
time_in
time_out
over_time_in
over_time_out
total_worked_hours
1
2021-08-04
08:35 AM
06:51 PM
07:05 PM
11:58 PM
NULL
My "quick and dirty" solution would be:
SELECT
CASE WHEN SUBSTRING('08:35 AM',7,2)= 'AM'
THEN CONVERT(TIME,SUBSTRING('08:35 AM',1,5))
ELSE DATEADD(HOUR,12,CONVERT(TIME,SUBSTRING('08:35 AM',1,5)) ) END as time
Basically
substring your time column with format '08:35 AM'
Test if is AM or PM
if AM >>>> CONVERT(TIME, Substringed value)
else > >>>> CONVERT(TIME, Substringed value) + 12 hours
Now you can perform your DATEDIFF operations.
I have a date field in oracle which returns
17-APR-19 12:00:00 AM
I also have a time column (VARCHAR) which returns HHMM in Military
1810
I'd like to combine these two fields to create a timestamp that is formatted to RFC 3339 standards. Preferable like this.
2019-04-17T18:10:00Z
I can convert a timestamp into the correct time using this:
SELECT
TO_CHAR(
SYSTIMESTAMP AT TIME ZONE 'UTC',
'yyyy-mm-dd"T"hh24:mi:ss"Z"'
)
FROM dual;
Is there a way to convert my date and time field into this timestamp format? The time on the date field is incorrect and needs to be replaced by the time field.
You can TRUNCate your date back to midnight and then use NUMTODSINTERVAL to add hours and minutes to it to get the correct time component:
Oracle Setup:
CREATE TABLE your_table ( your_date_column, your_time_column ) AS
SELECT DATE '2019-04-17', '1810' FROM DUAL
Query:
SELECT TO_CHAR(
TRUNC( your_date_column )
+ NUMTODSINTERVAL( SUBSTR( your_time_column, 1, 2 ), 'HOUR' )
+ NUMTODSINTERVAL( SUBSTR( your_time_column, 3, 2 ), 'MINUTE' ),
'YYYY-MM-DD"T"HH24:MI:SS"Z"'
) AS combined_date_time
FROM your_table
Output:
| COMBINED_DATE_TIME |
| :------------------- |
| 2019-04-17T18:10:00Z |
db<>fiddle here
If you want the value as a TIMESTAMP WITH TIME ZONE then:
SELECT CAST(
TRUNC( your_date_column )
+ NUMTODSINTERVAL( SUBSTR( your_time_column, 1, 2 ), 'HOUR' )
+ NUMTODSINTERVAL( SUBSTR( your_time_column, 3, 2 ), 'MINUTE' )
AS TIMESTAMP
) AT TIME ZONE 'UTC' AS combined_date_time
FROM your_table
Just do a bit of string concatenation
to_char( your_date, 'yyyy-mm-dd' ) ||
'T' ||
substr( your_time, 1, 2 ) ||
':' ||
substr( your_time, 3, 2 ) ||
':00Z'
assuming that your_time is always 4 characters long (i.e. 2 AM is represented as the string '0200' rather than '200'). This also assumes that the seconds will always be '00'.
You can achieve this by converting your_number into minutes and add it to your date, then cast it to timestamp as following:
SELECT CAST(
your_date +
(FLOOR(YOUR_TIME/100)*60 + MOD(YOUR_TIME,100)) / 1440
AS TIMESTAMP
) AT TIME ZONE 'UTC' AS YOUR_TIME_STAMP
FROM your_table;
Cheers!!
I am trying to get the time format difference of two different times in firebird sql. I tried subtracting the two times but the result is not in time format.
TIME_IN TIME,
TIME_OUT TIME,
DIFFERENCE TIME
SELECT
TIME_IN,
TIME_OUT,
TIME_OUT - TIME_IN
Sample Data
TIME_IN = 7:00
TIME_OUT = 12:00
12:00 - 7:00
my expected output is 5:00
also tried using DATEDIFF but my result is not in time format
DATEDIFF (HOUR, TIME_IN, TIME_OUT)
Something like this:
SELECT
CAST(DATEDIFF(HOUR, CAST(EXTRACT(HOUR FROM CAST(CAST(:TIME_IN as varchar(5)) as time)) || ':00' as TIME), CAST(EXTRACT(HOUR FROM CAST(CAST(:TIME_OUT as varchar(5)) as time)) || ':00' as TIME))
|| ':' ||
DATEDIFF(MINUTE, CAST('00:' || EXTRACT(MINUTE FROM CAST(CAST(:TIME_IN as varchar(5))as time)) as TIME), CAST('00:' || EXTRACT(MINUTE FROM CAST(CAST(:TIME_OUT as varchar(5)) as time)) as TIME)) as TIME)
FROM sth
The result of DATEDIFF is an integral value for the specified type. So the result of DATEDIFF(HOUR, '07:00', '12:00') is 5, and not 5:00.
The SQL TIME datatype is only for a 24 hour time within a day, it is not for measuring duration.
If for some reason you want to format it as a time, you will need to do that yourself. For example, by taking the difference in minutes, and then calculating the correct representation in hours and minutes.
I have a DB that stores dates as datetime with UTC offset.
How do I convert these dates from UTC to say CET in a select statement?
And is there are some kind of syntax sugar that takes care of the whole daylight autumn/winter and springs/summer offset thing?
EDIT:
I've found this library on CodePlex useful:
DateTimeUtil
A set of UDFs and configuration data for extended datetime handling, e. g. it offers easy timezone conversion incl. support for daylight saving times using native T-SQL (no CLRs).
SELECT CONVERT(VARCHAR, dbo.udfToLocalTime(t.CreatedDateTime, '1'), 113)
FROM yourtable t
In case of update:
UPDATE yourtable t
SET t.CreatedDateTime = CONVERT(VARCHAR, dbo.udfToLocalTime(t.CreatedDateTime, '1'), 113)
Use a UDF as below
CREATE FUNCTION udfToLocalTime
(
#UtcDateTimeAS DATETIME
,#UtcOffset AS INT = -8 --PST
)
RETURNS DATETIME
AS
BEGIN
DECLARE #PstDateTimeAS DATETIME
,#Year AS CHAR(4)
,#DstStart AS DATETIME
,#DstEndAS DATETIME
,#Mar1 AS DATETIME
,#Nov1 AS DATETIME
,#MarTime AS TIME
,#NovTime AS TIME
,#Mar1Day AS INT
,#Nov1Day AS INT
,#MarDiff AS INT
,#NovDiff AS INT
SELECT #Year = YEAR(#UtcDateTime)
,#MarTime = CONVERT(TIME, DATEADD(HOUR, -#UtcOffset, '1900-01-01 02:00'))
,#NovTime = CONVERT(TIME, DATEADD(HOUR, -#UtcOffset - 1, '1900-01-01 02:00'))
,#Mar1 = CONVERT(CHAR(16), #Year + '-03-01 ' + CONVERT(CHAR(5), #MarTime), 126)
,#Nov1 = CONVERT(CHAR(16), #Year + '-11-01 ' + CONVERT(CHAR(5), #NovTime), 126)
,#Mar1Day = DATEPART(WEEKDAY, #Mar1)
,#Nov1Day = DATEPART(WEEKDAY, #Nov1)
--Get number of days between Mar 1 and DST start date
IF #Mar1Day = 1
SET #MarDiff = 7
ELSE
SET #MarDiff = 15 - #Mar1Day
--Get number of days between Nov 1 and DST end date
IF #Nov1Day = 1
SET #NovDiff = 0
ELSE
SET #NovDiff = 8 - #Nov1Day
--Get DST start and end dates
SELECT #DstStart = DATEADD(DAY, #MarDiff, #Mar1)
,#DstEnd= DATEADD(DAY, #NovDiff, #Nov1)
--Change UTC offset if #UtcDateTime is in DST Range
IF #UtcDateTime >= #DstStart AND #UtcDateTime < #DstEnd
SET #UtcOffset = #UtcOffset + 1
--Get Conversion
SET #PstDateTime = DATEADD(HOUR, #UtcOffset, #UtcDateTime)
RETURN #PstDateTime
END
It's perfect if your DB contains UTC dates. Better way is to use your OS timezone offset.
For example:
select
dateadd(minute,DATEPART(TZ, SYSDATETIMEOFFSET()), YourUTCDate) as timestamp
from YourTable
Use the following function to convert Date TImeZones.
create or replace FUNCTION timezoneConverter
(v_date in date, inputDateZone in varchar2 default 'UTC', outputDateZone in varchar2 default 'UTC')
return date AS date_out date;
BEGIN
SELECT
to_date(
to_char(FROM_TZ(to_timestamp(to_char( v_date,'dd.mm.yyyy hh24:mi:ss' ),'dd.mm.yyyy hh24:mi:ss'),
to_char(inputDateZone)) AT TIME ZONE
to_char(outputDateZone),
'dd.mm.yyyy hh24:mi:ss'), 'dd.mm.yyyy hh24:mi:ss')
into date_out FROM dual;
return date_out;
EXCEPTION
when others then
DBMS_OUTPUT.put_line('Date:' || v_date || 'TZ:' || inputDateZone || '-' || outputDateZone || ' / ' || sqlcode || ' / ' || SQLERRM(sqlcode));
--date_out := to_date(to_char(v_date-1/24, 'yyyymmddhh24miss'), 'yyyymmddhh24miss');
date_out := null;
return date_out;
END;
/
DayLight Central European Time (CET) vs Central European Summer Time (CEST)
Short Hour 2nd hour skipped. Note: hours shift because clocks change forward 1 hour.
--set serveroutput on; -- View -> Dbms Output : set serveroutput on [serveroutput must be set ON or OFF or OPTIMIZED or UNOPTIMIZED]
SELECT
timezoneConverter(to_timestamp('2020-03-29T01:00Z', 'yyyy-mm-dd"T"hh24:mi"Z"'), 'CET', 'UTC') CETtoUTC1, -- 29-MAR-2020 00:00:00
timezoneConverter(to_timestamp('2020-03-29T02:00Z', 'yyyy-mm-dd"T"hh24:mi"Z"'), 'CET', 'UTC') CETtoUTC2, -- (null)
timezoneConverter(to_timestamp('2020-03-29T02:00Z', 'yyyy-mm-dd"T"hh24:mi"Z"'), 'UTC', 'CET') UTCtoCET FROM dual; -- 29-MAR-2020 04:00:00
-- Date:29-MAR-2020 02:00:00TZ:CET-UTC / -1878 / ORA-01878: specified field not found in datetime or interval
Long Hour 2nd hour repeats Note: hours shift because clocks change backward 1 hour.
SELECT
timezoneConverter(to_timestamp('2020-10-25T02:00Z', 'yyyy-mm-dd"T"hh24:mi"Z"'), 'UTC', 'CET') UTCtoCET, -- 25-OCT-2020 03:00:00
timezoneConverter(to_timestamp('2020-10-25T02:00Z', 'yyyy-mm-dd"T"hh24:mi"Z"'), 'CET', 'UTC') CETtoUTC2, -- 25-OCT-2020 01:00:00
timezoneConverter(to_timestamp('2020-10-25T01:00Z', 'yyyy-mm-dd"T"hh24:mi"Z"'), 'CET', 'UTC') CETtoUTC1 FROM dual; -- 24-OCT-2020 23:00:00
The following example converts the datetime value of one time zone to another time zone: docs.oracle.com
SELECT FROM_TZ(CAST(TO_DATE('1999-12-01 11:00:00',
'YYYY-MM-DD HH:MI:SS') AS TIMESTAMP), 'America/New_York')
AT TIME ZONE 'America/Los_Angeles' "West Coast Time"
FROM DUAL;
West Coast Time
------------------------------------------------
01-DEC-99 08.00.00.000000 AM AMERICA/LOS_ANGELES