Function to convert epoch to timestamp in Amazon Redshift - sql

I'm working on Amazon Redshift database and I have dates in milliseconds since epoch. I want to convert that to timestamp, and writing this query that I found on another thread
SELECT TIMESTAMP 'epoch' + column_with_time_in_ms/1000 *INTERVAL '1 second'
FROM table_name LIMIT 1000;
gives me the result in YYYY-MM-DD HH:MM:SS.
My question is:
How do I write a SQL function in Redshift that takes integer parameter that are the milliseconds and does this conversion?
Thanks.

You seem to want a scalar UDF that wraps the conversion code.
In Redshift, you could write this as:
create function ms_epoch_to_ts(int)
returns timestamp
immutable
as $$
select timestamp 'epoch' + $1 / 1000 * interval '1 second'
$$ language sql;

Related

Add one day to Now() and return as default value in SQL query

I am attempting to add a day to NOW() and return as the values for a column.
This works
SELECT NOW() as date
But this gives an error
SELECT DATE_ADD( NOW(), INTERVAL 1 DAY) as date
Is there a way to achieve this in a postgres query?
Thanks
I don't think there's a date_add() function in PostgreSQL:
ERROR: function date_add(timestamp with time zone, interval) does not
exist
LINE 1: select date_add(now(), interval '1 day');
^
HINT: No function matches the given name and argument types. You
might need to add explicit type casts.
but you can use a regular + operator to add an interval to timestamptz that's returned by now(). Demo:
select now() + '1 day'::interval;
You can define that function for convenience:
create function date_add(arg1 timestamptz, arg2 interval)
returns timestamptz language sql as $$
select arg1+arg2
$$;
select date_add(now(), interval '1 day') as date;
-- date
---------------------------------
-- 2022-11-29 12:28:12.393508+00
But I don't think it's really more convenient than the operator. You'd also have to overload it to make sure how it deals with different types - you can see in the demo how by default PostgreSQL will try to guess and cast automatically.

Read & evaluate cell as keyword in PostgreSQL

I have a table called providers where the column notice_period takes values such as interval '1 month' - interval '2 days'. If I do the following:
select to_date('11.11.2020', 'dd-mm-yyyyy') + notice_period from providers
I get the following error:
42883] ERROR: operator does not exist: ` text
How can I avoid this error and use the value of notice_period to directly calculate a valid date?
Cheers
Postgres readily converts intervals to strings. I would recommend removing the interval key word and just using ::interval:
select date_trunc('year', now()) + notice_period::interval
from (values ('1 day'), ( '3 month 1 day')) v(notice_period)
If you insist on the interval in the string, then remove it:
+ replace(notice_period, 'interval ', '')::interval
or:
+ substr(notice_period, 10)::interval
Another option is to use dynamic SQL in a function:
CREATE FUNCTION add_period(timestamp with time zone) RETURNS timestamp with time zone
LANGUAGE plpgsql STRICT AS
$$DECLARE
result timestamp with time zone;
BEGIN
EXECUTE format(
'SELECT TIMESTAMP WITH TIME ZONE %L + %s',
$1,
(SELECT notice_period FROM providers)
) INTO result;
RETURN result;
END;$$;
The problem with that is that the whole thing is vulnerable to SQL injection by bad values in the table providers, so use that only if you can trust the source.
This problem cannot be avoided with SQL strings supplied from outside.

How to pass the number of days to a Postgres function?

Argument days in function getAvgByDay() doesn't work, I guess because it is inside quotes:
CREATE OR REPLACE FUNCTION getAvgByDay(days int)
RETURNS TABLE ( average text,
date timestamp with time zone
) AS
$func$
BEGIN
RETURN QUERY
SELECT to_char( AVG(measure), '99999D22') AS average, ( now() - interval '$1 day') AS date
FROM (
SELECT mes.date, mes.measure
FROM measures mes
WHERE mes.date < ( now() - interval '$1 day')
) AS mydata;
END
$func$
LANGUAGE plpgsql;
Assuming the column measures.date is actually data type timestamptz and not a date:
CREATE OR REPLACE FUNCTION get_avg_by_day(_days int)
RETURNS TABLE (average text, ts timestamptz) AS -- not using "date" for a timestamp
$func$
SELECT to_char(avg(measure), '99999D22') -- AS average
, now() - interval '1 day' * $1 -- AS ts
FROM measures m
WHERE m.date < now() - interval '1 day' * $1
$func$ LANGUAGE sql;
No need for PLpgSQL, can be a simper SQL function.
Difference between language sql and language plpgsql in PostgreSQL functions
No need for a subquery. Only adds complexity and cost for no gain.
No need for column aliases in the outer query level. Those are not used, as visible column names are defined in the RETURNS clause.
No need for extra parentheses. Operator precedence works as desired anyway. (No harm in this case, either.)
Don't use CaMeL case identifier in Postgres if you can avoid it.
Are PostgreSQL column names case-sensitive?
Don't call a timestamptz column "date". That's misleading. Using "ts" instead.
Most importantly: You suspected as much, and "sticky bit" already explained: no interpolation inside strings. But just multiply the time unit with your integer input to subtract the given number of days:
interval '1 day' * $1
That's faster and cleaner than string concatenation.
There's no interpolation in strings. But you can concatenate strings and cast them to an interval. Try:
... concat(days, ' day')::interval ...
Or you could use format(), that's probably a little closer to what you originally had:
... format('%s day', days)::interval ...

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;

Convert date from long time postgres

How do I select the date as a readable string from epoch time in milliseconds?
Some like: SELECT *, to_date(time_in_milli_sec) FROM mytable
Per PostgreSQL docs:
SELECT *, to_timestamp(time_in_milli_sec / 1000) FROM mytable
SELECT timestamp 'epoch' + time_in_millisec * interval '1 ms'
FROM mytable;
See the manual here.
For milliseconds
SELECT timestamp 'epoch' + proyecto.fecha_inicio * interval '1 ms'
from proyecto.proyecto
where proyecto.fecha_inicio is not null
For seconds
SELECT TIMESTAMP WITH TIME ZONE 'epoch' + 982384720 * INTERVAL '1 second';
In the manual : http://www.postgresql.org/docs/current/interactive/functions-datetime.html.
Line: .. "Here is how you can convert an epoch value back to a time stamp"..
Original question was related to Date data type, but all the answers so far relate to Timestamp data type.
One way to convert milliseconds to Date would be:
SELECT DATE(any_time_field_containing_milliseconds/ 1000) FROM mytable;
This seems to use the timezone defined for database