Oracle SQL Convert number field to hh:mm - sql

I have looked at other similar articles but non are giving me the correct outcome.
I have a field called 'Duration' with numbers in minutes stored as an integer.
I need to convert that to hh:mm
so:
Duration
hh:mm
120
02:00
545
09:08
3600
60:00
I've experimented with to_char, to_date, and a few others but not getting anywhere.

You can try using:
NUMTODSINTERVAL function, that will transform your integer to seconds
TO_CHAR function, that will format your seconds to your required time frame
SELECT Duration,
TO_CHAR(TIME'0:0:0'+NUMTODSINTERVAL(Duration,'second'),'hh24:mi:ss')
FROM tab
Check the demo here.

Divide the duration (seconds) by 60. The quotient is the minutes, the remainder is the seconds.
select
duration,
to_char(trunc(duration / 60), 'FM00') ||
':' ||
to_char(mod(duration, 60), 'FM00') as time
from mytable;

Related

Convert minutes to Hours:Minutes in Oracle SQL

Morning Team,
I have an Oracle SQL script that is calculating from creation of an event and how many minutes old compared to the systimestamp.
I need to convert the minutes, which are coming out as 120 for 2 hour for example, into Hours:Minutes version, i.e. 2:00
I'm struggling with that part and would like to ask if someone could help? My current code for the calculation is:
(ROUND(((cast(systimestamp as date) - cast(n.createdttm as date)))*1440,0)) "Minutes old",
I'm sure it's something simple but with all my fiddling I am not able to get it.
Thank you
You can create an INTERVAL form the minutes.
select numtodsinterval(120, 'minute') from dual
Or create a datetime from the minutes and convert its time part to a string of hours and minutes.
select to_char(trunc(sysdate) + interval '1' minute * 120, 'hh24:mi') from dual
It looks like createdttm is a timestamp, so you can just subtract:
systimestamp - createdtm
... to get an interval value like +000000000 02:00:00.00000. You can't format that directly, but you can either extract the various elements and concatenate those back together, or treat it as a string and cut out the bits you want.
If you only want the time part and it will always be less than a day you can just do:
substr(systimestamp - createdtm, 12, 5)
02:00
But if it can go over 24 hours then you probably want the day part too, which you could still get just with substr (and maybe replace to change the space to another colon) if you know it can never be more than 2 days:
substr(systimestamp - createdtm, 10, 7)
0 02:00
That's unlikely to be a safe assumption though, so instead you could extract the number of days and concatenate that:
extract(day from (systimestamp - createdtm)) || ':' || substr(systimestamp - createdtm, 12, 5)
0:02:00
You could only show the number of days if it's non-zero, but that would probably be quite confusing to who/whatever is looking at the results; but if you really wanted to:
case when extract(day from (systimestamp - createdtm)) > 0
then extract(day from (systimestamp - createdtm)) || ':'
end || substr(systimestamp - createdtm, 12, 5)
02:00
db<>fiddle with a few sample values.
One thing to note is this effectively truncates the seconds off the time; your original attempt included round(), but that might not have been what you meant.
If u want to convert minutes to hours and minutes.
If x is the number of minutes (such as 350):
TO_CHAR ( FLOOR (x / 60)) || ':'
|| TO_CHAR ( MOD (x, 60)
, 'FM00'
)
If u want to convert hours and minutes to minutes.
SELECT (TRUNC (x) * 60) +
( (MOD (x, 1)
* 100
)
FROM dual;
where x is a NUMBER. If you have a sting, s, instead, use TO_NUMBER (s) in place of x.

how to add dates with keep their formats?

I have a start_time which is already formatted as date type and have duration as number like 449. It means 449 seconds. So i need end_time. Of course i can obviously convert duration to date format and add duration on start_time using below simply queries
select to_char(to_date(USE_SEC,'sssss'),'hh24miss')
from ABA_RM_INB_USAGE;
USE_SEC column is containing integer(number in oracle) like 1167
and above query is returning date formatted result like 001927 that is okay.
This is query that add duration on start_time
select to_char(USE_STRT_DTTM, 'hh24miss') + to_char(to_date(USE_SEC, 'sssss'), 'hh24miss') as duration
from ABA_RM_INB_USAGE;
This is returning that result which is problem that convert to date format
95980.
It means 09:59:80 oops 80 seconds is absolutely wrong. Can i add dates with keep their formats. How can i ?
You can use +. This is the traditional method:
select start_time + duration / 24*60*60
You can write this now as:
select start_time + duration * interval '1' second
Your first query is converting your number-of-second value to a string. In your second query you are converting the start time to another string. Both represent HHMISS. Then you add them together, effectively:
'094053' + '001927'
For the addition operator to work they are implicitly converted to numbers, so it becomes:
94053 + 1927
which gives you your (numeric) result of 95980.
As soon as you convert to strings you are losing the ability to treat them as dates and honour the mod-60 behaviour for minutes and seconds, which is my you appear to end up with 80 seconds - but they aren't really seconds at all, it's just a number. You also lose the mod-24 behaviour for hours, so if your start time is just before midnight and the duration pushes you over midnight, your result wouldn't reflect that either.
As #GordonLinoff suggested, keep your date as a date, and add the number of seconds as a number, or a number converted to an interval:
USE_STRT_DTTM + USE_SEC / (24*60*60)
or:
USE_STRT_DTTM + USE_SEC * interval '1' second
Demo:
-- CTE for sample data
with ABA_RM_INB_USAGE (USE_STRT_DTTM, USE_SEC) as (
select to_date('09:40:53', 'HH24:MI:SS'), 1167 from dual
union all
select to_date('23:54:55', 'HH24:MI:SS'), 449 from dual
)
-- query showing working
select USE_STRT_DTTM,
USE_SEC,
to_char(to_date(USE_SEC, 'sssss'), 'hh24:mi:ss') as use_sec_hhmiss,
USE_SEC * interval '1' second as use_sec_interval,
USE_STRT_DTTM + USE_SEC / (24*60*60) as result1,
USE_STRT_DTTM + USE_SEC * interval '1' second as result2
from ABA_RM_INB_USAGE;
USE_STRT_DTTM USE_SEC USE_SEC_HHMISS USE_SEC_INTERVAL RESULT1 RESULT2
------------------- ------- -------------- ------------------- ------------------- -------------------
2019-08-01 09:40:53 1167 00:19:27 +00 00:19:27.000000 2019-08-01 10:00:20 2019-08-01 10:00:20
2019-08-01 23:54:55 449 00:07:29 +00 00:07:29.000000 2019-08-02 00:02:24 2019-08-02 00:02:24
Read more about Datetime/Interval Arithmetic.
I have a start_time which is already formatted as date type
Your column is (I hope, and seems to be the case from your query) a date. Dates do not have intrinsic human-readable formats. When you query your table your client will format the date to something readable, using either its own preferences or your session's NLS_DATE_FORMAT.
Of course i can obviously convert duration to date format and add duration on start_time
You originally converted your duration to a date data type (via to_date()), at 00:19:27 on the first day of the current month (which is what if defaults to if not day, month or year components are supplied; my CTE above is doing the same). You cannot add a date to another date. That even has its own error, "ORA-00975: date + date not allowed". So you then converted both your date values (start time and converted duration) to strings. You can't add strings together either, as that makes no sense; but if you try Oracle will implicitly try to convert both strings to numbers. In this case that implicit conversion works for both strings, but it usually won't; the superficially-similar '09:40:53' + '00:19:27' would get "ORA-01722: invalid number".
In Oracle DATE values do not have a format - you use the TO_CHAR function to format them when you need to output them.
In this case it looks like you need to use an interval. You have a field which contains a number of seconds that you want to convert to an interval - for this you can use the TO_DSINTERVAL function, although amusingly enough you have to convert the number to a string in order to use the function to convert it to an interval:
-- Version using TO_DSINTERVAL
WITH cteData AS (SELECT USE_STRT_DTTM + TO_DSINTERVAL('PT' || TO_CHAR(USE_SEC) || 'S') AS DT_TIME
FROM ABA_RM_INB_USAGE)
SELECT TO_CHAR(DT_TIME, 'YYYY-MM-DD HH24:MI:SS') FORMATTED_DATE_TIME
FROM cteData;
Docs for TO_DSINTERVAL here
dbfiddle demonstrating this in use here
EDIT
As #AlexPoole points out, the better function to use here is NUMTODSINTERVAL:
-- Version using NUMTODSINTERVAL
WITH cteData AS (SELECT USE_STRT_DTTM + NUMTODSINTERVAL(USE_SEC, 'SECOND') AS DT_TIME
FROM ABA_RM_INB_USAGE)
SELECT TO_CHAR(DT_TIME, 'YYYY-MM-DD HH24:MI:SS') FORMATTED_DATE_TIME
FROM cteData;
Docs for NUMTODSINTERVAL here
updated dbfiddle here

PLSQL - convert unix timestamp with millsecond precision to timestamp(6)

I have a unix timstamp with millsecond precision like below:
1523572200000
I need to convert it to timestamp(6). This is the format I need:
05-NOV-14 09.45.00.000000000 AM
(Fyi examples above are not matching dates, just using as example.)
What's the best way to go about this?
Thanks!
The following might work for you (where myunixtimestamp is the name of the column in which your Unix timestamps are stored):
SELECT TIMESTAMP'1970-01-01 00:00:00.000' + NUMTODSINTERVAL(myunixtimestamp/1000, 'SECOND')
FROM mytable;
For example,
SELECT TIMESTAMP'1970-01-01 00:00:00.000' + NUMTODSINTERVAL(1523572200000/1000, 'SECOND')
FROM dual;
gives a result of 2018-04-12 10:30:00.000000000 PM.
Hope this helps.
Assuming that current timestamp is: 1523572200000, try following:
select cast (to_date('1970-01-01', 'YYYY-MM-DD') + 1523572200000/1000/60/60/24 as timestamp) from dual;
where:
to_date('1970-01-01', 'YYYY-MM-DD') is epoch time
<unix_timestamp>/60/60/24 was divided by 1000 miliseconds 60 second and 60 minutes and 24 hours because in oracle we are adding days

Insert UTC timestamp (millis) into Oracle timestamp column

Is there a way how to directly convert value in milliseconds (e.g. 1480515430991) to Oracle TIMESTAMP(6) column? Like some pattern I'm missing for the TO_TIMESTAMP or TO_DATE functions?
All I could find so far are some calculations with intervals and to_date('1970-01-01','YYYY-MM-DD') or other crazy "manual" calculations.
Thanks
EDIT:
Thanks guys. I didn't ask how to do the conversion though. I asked if there is a direct (native, more straightforward) way to achieve it and avoid these calculations for a given input. I am just a curious person and there are many undocumented features out there (Oracle not excluded). I guess NO is my answer then.
Correct function, i.e. include time zone consideration and milliseconds would be this one (using literals):
create or replace function epoch2timestamp(epoch number) return timestamp as
begin
return (TIMESTAMP '1970-01-01 00:00:00 UTC' + epoch/1000 * INTERVAL '1' SECOND) AT LOCAL;
end;
/
This is how to get timestamp from epoch.:
select to_timestamp('1970-01-01','yyyy-mm-dd') + ( 1 / 24 / 60 / 60 / 1000) * epoch from dual;
So in insert please insert to_timestamp('1970-01-01','yyyy-mm-dd') + ( 1 / 24 / 60 / 60 / 1000) * epoch instead of epoch. You can also create function for that:
create or replace function epoch2timestamp(epoch number) return timestamp as
begin
return to_timestamp('1970-01-01','yyyy-mm-dd') + ( 1 / 24 / 60 / 60 / 1000) * epoch;
end;
/
And then operate on function. These are not "crazy manual calculations" just a legal way of conversion.
If you want to express the unix time, the time since epoch, you need seconds, not milliseconds. See Unixtime
Oracle's datatype TIMESTAMP is in fractional seconds, as you can read in the Oracle documentation. Link zu 11g documentation
Year, month, and day values of date, as well as hour, minute, and second values of time, where fractional_seconds_precision is the number of digits in the fractional part of the SECOND datetime field. Accepted values of fractional_seconds_precision are 0 to 9.
And to answer your question: there is a TO_TIMESTAMP function. See 11g documentation
The TO_TIMESTAMP function converts text data to a value of TIMESTAMP data type.
You can use it like this
TO_TIMESTAMP('2016/11/30 15:53:18', 'YYYY/MM/DD HH:MI:SS')
and would get '30-NOV-16 15.53.18.000000000 AM'
If, for some reason, you really need to display the seconds sind epoch, you can use the noncrazy calculation
select (SYSDATE - to_date('1970-01-01', 'yyyy-MM-dd')) * 24 * 60 * 60 from dual;

Display correct subtraction of two timestamps in create view

By using normal minus '-' function between two timestamps, the answer given from oracle is incorrect.
This is what i want to do:
ALTER SESSION SET NLS_TIMESTAMP_TZ_FORMAT='DD-MON-RR HH24:MI TZR';
Created table:
CREATE TABLE TEST (
StartTime timestamp with time zone
,EndTime timestamp with time zone
,Science varchar2(7)
);
I create the column data type as timestamp with time zone. This is value I have inserted:
INSERT INTO TEST
VALUES('05-OCT-2013 01:00 +08:00'
,'05-OCT-2013 23:00 +06:00'
,'SCIENCE');
INSERT INTO TEST
VALUES('05-OCT-2013 12:00 +08:00'
,'05-OCT-2013 15:00 -12:00'
,'Maths');
Attempted for rounding time:
CREATE VIEW TESTRECRDS AS
SELECT (Extract(hour FROM(ENDTIME- STARTTIME)) || 'Hours' ||
Extract(minute FROM(ENDTIME- STARTTIME))>=60 Then (Extract(hour FROM(ENDTIME- STARTTIME)) + Extract(minute FROM(ENDTIME- STARTTIME))/60 ELSE 0 END || 'Minutes' AS DURATION,
Science
FROM Test;
Now i have two questions regarding on the calculation and rounding off the minutes to nearest hours.
First let's say the endtime is 1535 +0600 and starttime is 01:50 +0800
So when i deduct endtime - starttime:
the formula should be:
2135 - 0950 = 2085 - 0950
= 1135
But if i use my successful attempt answer to calculate, it is not the correct exact answer. The oracle answer would be 15 hours 45 minutes.
In your last CREATE VIEW statement you try to multiply text, which cannot work:
SELECT To_Char(STARTTIME - ENDTIME, 'HH24:MI TZR')*24 AS DURATION
*24 is operating on the text to_char() returns.
You have to multiply the interval before converting to text.
You define the column Science varchar2(6), then you insert 'SCIENCE', a 7-letter word?
I also fixed a syntax error in your INSERT statement: missing '.
About your comment:
"I would like to insert timestamp with timezone during creation of my tables. Can DATE data type do that too?
Read about data types in the manual.
The data type date does not include time zone information.
If by "timezone difference" you mean the difference between the timezone modifiers, use this to calculate:
SELECT EXTRACT(timezone_hour FROM STARTTIME) AS tz_modifier FROM tbl
Keywords here are timezone_hour and is timezone_minute. Read more in the manual.
But be aware that these numbers depend on the daylight saving hours and such shenanigans. Very uncertain territory!
Get it in pretty format - example:
SELECT to_char((EXTRACT (timezone_hour FROM STARTTIME) * 60
+ EXTRACT (timezone_minutes FROM STARTTIME))
* interval '1 min', 'HH:MI')
In PostgreSQL you would have the simpler EXTRACT (timezone FROM STARTTIME), but I don't think Oracle supports that. Can't test now.
Here is a simple demo how you could round minutes to hours:
SELECT EXTRACT(hour FROM (ENDTIME - STARTTIME))
+ CASE WHEN EXTRACT(minute FROM (ENDTIME - STARTTIME)) >= 30 THEN 1 ELSE 0 END
FROM Test;
I'm not sure what number you're trying to calculate, but when you subtract two dates in Oracle, you get the difference between the dates in units of days, not a DATE datatype
SELECT TO_DATE('2011-01-01 09:00', 'yyyy-mm-dd hh24:mi') -
TO_DATE('2011-01-01 08:00', 'yyyy-mm-dd hh24:mi') AS diff
FROM dual
DIFF
----------
.041666667
In this case 8am and 9am are 0.41667 days apart. This is not a date object, this is a scalar number, so formatting it as HH24:MI doesn't make any sense.
To round you will need to do a bit of more math. Try something like:
TO_DATE(ROUND((ENDTIME - STARTTIME) * 96) / 96, 'HH24:MI')
The difference between dates is in days. Multiplying by 96 changes the measure to quarter hours. Round, then convert back to days, and format. It might be better to use a numeric format want to format, in which case you would divide by 4 instead of 96.
Timezone is not particularly relevant to a time difference. You will have to adjust the difference from UTC to that timezone to get the right result with Timezone included.