i have 2 rows in a column, one feeding me the julian date (number of days since Jan 1, 1970, with that day being 1), and the second column is the number of minutes past midnight of the current day (why it was done this way, i have no idea).
i would like to get my sql query to create a timestamp out of these two columns.
if i had access to the data ahead of time, i could do something like SELECT timestamp '1970-01-01 00:00:00' + INTERVAL 'X DAYS' + INTERVAL 'Y MINUTES' AS my_time FROM mytable, but since X and Y are actual members of the table i'm querying, it's not that simple.
anyone have any suggestions?
essentially, i'm looking for the equivalent [legal] solution:
SELECT timestamp '1970-01-01 00:00:00' + INTERVAL 'my_col1 DAYS' + INTERVAL 'my_col2 MINUTES' AS my_time FROm mytable
i can get my server-side code to do it for me, but i would love it if i could build it into my query.
You can construct the interval strings and then cast them to the interval type:
SELECT
timestamp '1970-01-01 00:00:00' +
cast(my_col1 || ' DAYS' AS interval) +
cast(my_col2 || ' MINUTES' AS interval) my_time FROM mytable
Without resorting to string substitution or concatenation:
SELECT
timestamp '1970-01-01 00:00:00' +
my_col1 * interval '1 DAY' +
my_col2 * interval '1 MINUTE' FROM mytable
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 get a date format EPOCH in database table - "19072"
First I need to convert to time- 21/03/2022 and concatenate 23:59:59
date conversion reference - https://www.timeanddate.com/date/dateadded.html?d1=1&m1=1&y1=1970&type=add&ay=&am=&aw=&ad=19072&rec=
Now I need to convert "21/03/2022 23:59:59" to UTC +7:00(Jakarta, Indonesia)
Final expected output is - "22/03/2022 06:59:59"
You can add a number of days directly to a date:
select date '1970-01-01' + 19072 from dual;
21-MAR-22
or add an interval to a timestamp; which is probably more appropriate as you need to end up with a timestamp with time zone anyway, so this starts with a UTC value:
select timestamp '1970-01-01 00:00:00 UTC' + (19072 * interval '1' day) from dual;
21-MAR-22 00.00.00.000000000 PM UTC
Then you can add hours, minutes and seconds (or go ahead another day and subtract one second):
select
timestamp '1970-01-01 00:00:00 UTC'
+ (19072 * interval '1' day)
+ (23 * interval '1' hour)
+ (59 * interval '1' minute)
+ (59 * interval '1' second)
from dual;
21-MAR-22 11.59.59.000000000 PM UTC
and convert to your target time zone with at time zone:
select
(
timestamp '1970-01-01 00:00:00 UTC'
+ (19072 * interval '1' day)
+ (23 * interval '1' hour)
+ (59 * interval '1' minute)
+ (59 * interval '1' second)
)
at time zone 'Asia/Jakarta'
from dual;
22-MAR-22 06.59.59.000000000 AM ASIA/JAKARTA
and then for display purposes, convert to a string in the format you want:
select
to_char(
(
timestamp '1970-01-01 00:00:00 UTC'
+ (19072 * interval '1' day)
+ (23 * interval '1' hour)
+ (59 * interval '1' minute)
+ (59 * interval '1' second)
)
at time zone 'Asia/Jakarta',
'DD/MM/YYYY HH24:MI:SS'
) as result
from dual;
22/03/2022 06:59:59
You can simplify a bit by modifying your epoch, though it looks a bit odd:
select
to_char(
(
timestamp '1970-01-01 23:59:59 UTC'
+ (19072 * interval '1' day)
)
at time zone 'Asia/Jakarta',
'DD/MM/YYYY HH24:MI:SS'
) as result
from dual;
22/03/2022 06:59:59
(I'd probably prefer to keep the usual epoch and do the extra explicit interval additions...)
db<>fiddle
Only format as a string to display it. If you need to pass the value somewhere else then leave it as a timestamp with time zone, or if necessary cast that to a plain timestamp or even a date.
For example I have 8am to 8pm.
I want the table where it can generate the table where it has entry one by one starttime and endtime (two columns):
8:00-9:00
9:00-10:00
....
19:00-20:00
Like this way. No date before the time.
You can use the generate_series function to do that:
psql# select generate_series('2014-01-01 16:00'::timestamp, '2014-01-01 20:00'::timestamp, '1 hour');
generate_series
---------------------
2014-01-01 16:00:00
2014-01-01 17:00:00
2014-01-01 18:00:00
2014-01-01 19:00:00
2014-01-01 20:00:00
(5 rows)
And then you can use:
SELECT
t AS starttime,
t + INTERVAL '1 hour' as endtime
FROM
GENERATE_SERIES(
'2014-01-01 16:00'::TIMESTAMP,
'2014-01-01 20:00'::TIMESTAMP,
'1 hour'
) AS t
To get the start and end times.
Alternatively, to just get the times, you can use:
SELECT
'08:00'::time + (t || ' hours')::interval as starttime,
'08:00'::time + ((t + 1)::text || ' hours')::interval as endtime
FROM
GENERATE_SERIES(0, 12) AS t
Simpler, use a full timestamp with a dummy date component and cast to time:
SELECT t::time AS starttime
, t::time + interval '1h' AS endtime
FROM generate_series('2000-1-1 08:00'::timestamp
, '2000-1-1 19:00'::timestamp
, interval '1h') t;
The same for text columns:
SELECT to_char(t, 'HH24:MI') AS starttime
, to_char(t + interval '1h', 'HH24:MI') AS endtime
FROM generate_series('2000-1-1 08:00'::timestamp
, '2000-1-1 19:00'::timestamp
, interval '1h') t;
The statement gives me the date and time.
How could I modify the statement so that it returns only the date (and not the time)?
SELECT to_timestamp( TRUNC( CAST( epoch_ms AS bigint ) / 1000 ) );
You use to_timestamp function and then cast the timestamp to date
select to_timestamp(epoch_column)::date;
You can use more standard cast instead of ::
select cast(to_timestamp(epoch_column) as date);
More details:
/* Current time */
select now(); -- returns timestamp
/* Epoch from current time;
Epoch is number of seconds since 1970-01-01 00:00:00+00 */
select extract(epoch from now());
/* Get back time from epoch */
-- Option 1 - use to_timestamp function
select to_timestamp( extract(epoch from now()));
-- Option 2 - add seconds to 'epoch'
select timestamp with time zone 'epoch'
+ extract(epoch from now()) * interval '1 second';
/* Cast timestamp to date */
-- Based on Option 1
select to_timestamp(extract(epoch from now()))::date;
-- Based on Option 2
select (timestamp with time zone 'epoch'
+ extract(epoch from now()) * interval '1 second')::date;
In your case:
select to_timestamp(epoch_ms / 1000)::date;
PostgreSQL Docs
select to_timestamp(cast(epoch_ms/1000 as bigint))::date
worked for me
On Postgres 10:
SELECT to_timestamp(CAST(epoch_ms as bigint)/1000)
The solution above not working for the latest version on PostgreSQL. I found this way to convert epoch time being stored in number and int column type is on PostgreSQL 13:
SELECT TIMESTAMP 'epoch' + (<table>.field::int) * INTERVAL '1 second' as started_on from <table>;
For more detail explanation, you can see here https://www.yodiw.com/convert-epoch-time-to-timestamp-in-postgresql/#more-214
This works for me fine:
SELECT t.*,
to_timestamp(cast(t.prev_fire_time/1000 as bigint)) as prev_fire_time,
to_timestamp(cast(t.next_fire_time/1000 as bigint)) as next_fire_time,
to_timestamp(cast(t.start_time/1000 as bigint)) as start_time
FROM public.qrtz_triggers t;
Seconds since epoch with GNU date:
$ date +%s.%N
1627059870.945134901
This works with PostgreSQL 11:
# select to_timestamp (1627059870.945134901);
to_timestamp
-------------------------------
2021-07-23 19:04:30.945135+02
(1 row)
# select to_timestamp (1627059870.945134901)::date;
to_timestamp
--------------
2021-07-23
(1 row)
When subtracting timestamps the return value is an interval data-type. Is there an elegant way to convert this value into the total number of (milli/micro) seconds in the interval, i.e. an integer.
The following would work, but it's not very pretty:
select abs( extract( second from interval_difference )
+ extract( minute from interval_difference ) * 60
+ extract( hour from interval_difference ) * 60 * 60
+ extract( day from interval_difference ) * 60 * 60 * 24
)
from ( select systimestamp - (systimestamp - 1) as interval_difference
from dual )
Is there a more elegant method in SQL or PL/SQL?
An easy way:
select extract(day from (ts1-ts2)*86400) from dual;
The idea is to convert the interval value into days by times 86400 (= 24*60*60).
Then extract the 'day' value which is actually second value we wanted.
I hope this help:
zep#dev> select interval_difference
2 ,sysdate + (interval_difference * 86400) - sysdate as fract_sec_difference
3 from (select systimestamp - (systimestamp - 1) as interval_difference
4 from dual)
5 ;
INTERVAL_DIFFERENCE FRACT_SEC_DIFFERENCE
------------------------------------------------------------------------------- --------------------
+000000001 00:00:00.375000 86400,375
With your test:
zep#dev> select interval_difference
2 ,abs(extract(second from interval_difference) +
3 extract(minute from interval_difference) * 60 +
4 extract(hour from interval_difference) * 60 * 60 +
5 extract(day from interval_difference) * 60 * 60 * 24) as your_sec_difference
6 ,sysdate + (interval_difference * 86400) - sysdate as fract_sec_difference
7 ,round(sysdate + (interval_difference * 86400) - sysdate) as sec_difference
8 ,round((sysdate + (interval_difference * 86400) - sysdate) * 1000) as millisec_difference
9 from (select systimestamp - (systimestamp - 1) as interval_difference
10 from dual)
11 /
INTERVAL_DIFFERENCE YOUR_SEC_DIFFERENCE FRACT_SEC_DIFFERENCE SEC_DIFFERENCE MILLISEC_DIFFERENCE
------------------------------------------------------------------------------- ------------------- -------------------- -------------- -------------------
+000000001 00:00:00.515000 86400,515 86400,515 86401 86400515
zep#dev>
I've found this to work. Apparently, if you do arithmetics with timestamps they are converted to some internal datatype that, when substracted from each other, returns the interval as a number.
Easy? Yes. Elegant? No. Gets the work done? Oh yeah.
SELECT ( (A + 0) - (B + 0) ) * 24 * 60 * 60
FROM
(
SELECT SYSTIMESTAMP A,
SYSTIMESTAMP - INTERVAL '1' MINUTE B
FROM DUAL
);
Unfortunately, I don't think that there is an alternative (or more elegant) way of calculating total seconds from an interval type in pl/sql. As this article mentions:
... unlike .NET, Oracle provides no simple equivalent to TimeSpan.TotalSeconds.
therefore extracting day, hour etc from the interval and multiplying them with corresponding values seems like the only way.
Based on zep's answer, I wrapped things up into a function for your convenience:
CREATE OR REPLACE FUNCTION intervalToSeconds(
pMinuend TIMESTAMP , pSubtrahend TIMESTAMP ) RETURN NUMBER IS
vDifference INTERVAL DAY TO SECOND ;
vSeconds NUMBER ;
BEGIN
vDifference := pMinuend - pSubtrahend ;
SELECT EXTRACT( DAY FROM vDifference ) * 86400
+ EXTRACT( HOUR FROM vDifference ) * 3600
+ EXTRACT( MINUTE FROM vDifference ) * 60
+ EXTRACT( SECOND FROM vDifference )
INTO
vSeconds
FROM DUAL ;
RETURN vSeconds ;
END intervalToSeconds ;
Use following query:
select (cast(timestamp1 as date)-cast(timestamp2 as date))*24*60*60)
Similar to #Zhaoping Lu answer but directly extracting seconds instead of getting them from the number of days.
SELECT extract(second from (end_date - start_date)) as "Seconds number"
FROM my_table
(worked on PostgresSQL 9.6.1)
A shorter method to convert timestamp to nanoseconds.
SELECT (EXTRACT(DAY FROM (
SYSTIMESTAMP --Replace line with desired timestamp --Maximum value: TIMESTAMP '3871-04-29 10:39:59.999999999 UTC'
- TIMESTAMP '1970-01-01 00:00:00 UTC') * 24 * 60) * 60 + EXTRACT(SECOND FROM
SYSTIMESTAMP --Replace line with desired timestamp
)) * 1000000000 AS NANOS FROM DUAL;
NANOS
1598434427263027000
A method to convert nanoseconds to timestamp.
SELECT TIMESTAMP '1970-01-01 00:00:00 UTC' + numtodsinterval(
1598434427263027000 --Replace line with desired nanoseconds
/ 1000000000, 'SECOND') AS TIMESTAMP FROM dual;
TIMESTAMP
26/08/20 09:33:47,263027000 UTC
As expected, above methods' results are not affected by time zones.
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
))) * 1000000000 AS NANOS FROM DUAL;
NANOS
1598434427263027000
A method to convert nanoseconds to interval.
SELECT numtodsinterval(
1598434427263027000 --Replace line with desired nanoseconds
/ 1000000000, 'SECOND') AS INTERVAL FROM dual;
INTERVAL
+18500 09:33:47.263027
Replace 1000000000 by 1000, for example, if you'd like to work with milliseconds instead of nanoseconds.
I've tried some of posted methods, but a got the exception "ORA-01873: the leading precision of the interval is too smalll" when multiplying the interval by 86400, so I've decided do post the methods that works for me.
SELECT to_char(ENDTIME,'yyyymmddhh24missff')-to_char(STARTTIME,'yyyymmddhh24missff') AS DUR
FROM DUAL;
yyyymmddhh24miss- WILL GIVE DURATION IN SEC
yyyymmddhh24mi DURATION IN MIN
yyyymmddhh24 - DURATION - HOURS
yyyymmdd DURATION IN DAYS