postgres convert a substring to epoch - sql

I'm trying to convert a epoch timestamp to a human readable timestamp in a single query but I'm getting a little stuck - any help is appreciated.
testing=# SELECT creation_time FROM users LIMIT 1;
creation_time
---------------
1354006445722
(1 row)
testing=# SELECT SUBSTRING('1498123813330', 1,10);
SUBSTRING
------------
1498123813
(1 row)
testing=# SELECT TIMESTAMP WITH TIME ZONE 'epoch' + 1498123813 * INTERVAL '1 second';
?column?
------------------------
2017-06-22 02:30:13-07
(1 row)
Anyway to put this into a single query?

What you want is CASTing, i.e.
SELECT TIMESTAMP WITH TIME ZONE 'epoch' +
SUBSTRING(creation_time, 1, 10)::NUMERIC * INTERVAL '1 second';
But, if creating time is epoch milliseconds, you could do:
SELECT to_timestamp(creation_time::double precision / 1000)
instead, which will preserve milliseconds too. You can print timestamp out with to_char if you want a format, other than the default timestamp output.
http://rextester.com/EHPNJ86308

Assuming created_time is stored as a varchar, you can apply the same substring logic to it and cast it to a number:
SELECT TIMESTAMP WITH TIME ZONE 'epoch' +
SUBSTRING(creation_time, 1,10)::NUMERIC * INTERVAL '1 second'
FROM users

Related

Oracle: Error in converting DateTime to Epoch

While trying to convert datetime to epoch, I am getting an error: ORA-01810: format code appears twice
QracleSQL query:
select (trunc(TO_TIMESTAMP('2022-05-08T19:09:17Z', 'yyyy-MM-dd"T"HH:mm:ssXXX')) - TO_DATE('01/01/1970', 'MM/DD/YYYY')) * 24 * 60 * 60 from dual;
You should use:
TO_TIMESTAMP_TZ instead of TO_TIMESTAMP
the format model YYYY-MM-DD"T"HH24:MI:SS.FF TZD rather than incorrectly using MM twice, HH24 instead of HH, .FF instead of XXX, and TZD instead of hardcoding "Z".
Make sure you always convert your timestamp to UTC time zone (yours is already but others may not be)
Don't TRUNCate the timestamp to a DATE at midnight or you will lose the time component.
Like this:
SELECT ROUND(
(
TRUNC(timestamp_value AT TIME ZONE 'UTC', 'MI')
- DATE '1970-01-01'
) * 86400
+ EXTRACT(SECOND FROM timestamp_value AT TIME ZONE 'UTC')
) AS epoch_time
FROM (
SELECT TO_TIMESTAMP_TZ(
'2022-05-08T19:09:17Z',
'YYYY-MM-DD"T"HH24:MI:SS.FF TZD'
) AS timestamp_value
FROM DUAL
);
Which outputs:
EPOCH_TIME
1652033357
db<>fiddle here
Something like this:
TEST DATA
create table sample_inputs (ts_string) as
select '2022-05-08T16:49:34Z' from dual union all
select '2022-04-15T04:20:13.525Z' from dual
;
QUERY AND OUTPUT
with
prep (ts_string, ts) as (
select ts_string,
to_timestamp(ts_string, 'yyyy-mm-dd"T"hh24:mi:ss.ff"Z"')
from sample_inputs
)
select ts_string,
round((trunc(ts, 'mi') - date '1970-01-01') * 24 * 3600)
+ extract(second from ts)
as epoch
from prep;
TS_STRING EPOCH
-------------------------- -----------
2022-05-08T16:49:34Z 1652028574
2022-04-15T04:20:13.525Z 1649996413.525
NOTES
In your attempt there are several mistakes. The Oracle fractional-seconds element is ff, not xxx. You are missing the placeholder for the hard-coded Z at the end (you have "T" in your mask, which is correct, but you are missing the similar "Z"). HH is insufficient - it must be either HH24 or HH followed by AM (or equivalently PM) at the end. In your example, it is obviously HH24. And MM and mm mean the same thing in Oracle - this is not Unix. The element for minutes is mi or equivalently MI.
The query I wrote preserves fractional seconds in the epoch. Another question earlier today (perhaps yours too, under another user name) was closed as being a "duplicate" - but the claimed "duplicate" has absolutely nothing about preserving fractional seconds, when the input is an Oracle timestamp vs an Oracle date (which always does have a time component, but only in whole seconds).

Timestamp conversion to EPOCH time in Oracle SQL

I have input date as a column in table "22/03/2022 06:59:59"
I need to convert into EPOCH time -
Expected output- "1647932399" timezone(jakarta, Indonesia)
reference link- https://www.epochconverter.com/
time zone of input data is - UTC +7:00(Jakarta)
Tried with this-SQL CODE
SELECT (CAST(SYS_EXTRACT_UTC(to_timestamp('22/03/2022 06:59:59','dd/mm/yyyy HH:MI:SS' )) AS DATE) - TO_DATE('01/01/1970','DD/MM/YYYY')) * 24 * 60 * 60 FROM DUAL;
Result displayed is 1647907199
But didn't get the expected output
1647932399 is the epoch for 2022-03-22 06:59:59 UTC. Your start time is 22-03-22 06:59:59 Asia/Jakarta, which is 22-03-21 23:59:59 UTC, and the epoch for that is 1647907199 - which is what you're getting. So your result is correct; but risky.
You are relying on implicit conversion.
to_timestamp('22/03/2022 06:59:59','dd/mm/yyyy HH:MI:SS') gives you a plain timestamp, with no time zone information. When you then do SYS_EXTRACT_UTC(...) the plain timestamp is implicitly converted to a timestamp with time zone using your session time zone - which happens to be Jakarta, it seems. So it works - for you, in this session. If you change your session time zone, or more likely if someone else runs the same code from a session with a different time zone, then the reslut will be different.
To be more explicit, you can convert your string to a plain timestamp, then declare that it represents a Jakarta time, and then convert that to UTC:
sys_extract_utc(from_tz(to_timestamp('22/03/2022 06:59:59', 'DD/MM/YYYY HH24:MI:SS'), 'Asia/Jakarta'))
21-MAR-22 23.59.59.000000000 UTC
There are then basically two ways to convert that to an epoch number; either cast it to a date, subtract 1970-01-01 as a date, and manipulate the resulting number of days (as in your question):
select
round(
(
cast(
sys_extract_utc(from_tz(to_timestamp('22/03/2022 06:59:59', 'DD/MM/YYYY HH24:MI:SS'), 'Asia/Jakarta'))
as date)
- date '1970-01-01'
) * 24 * 60 * 60
) as result
from dual;
1647907199
Or leave it as a timestamp, subtract 1970-01-01 as a timestamp, and manipulate the resulting interval:
select
(extract(day from diff) * 24 * 60 * 60)
+ (extract(hour from diff) * 60 * 60)
+ (extract(minute from diff) * 60)
+ extract(second from diff)
as result
from (
select sys_extract_utc(from_tz(to_timestamp('22/03/2022 06:59:59', 'DD/MM/YYYY HH24:MI:SS'), 'Asia/Jakarta'))
- timestamp '1970-01-01 00:00:00' as diff
from dual
);
1647907199
Note that either way you can easily get the short result that was the basis for your previous question, by truncating the number or extracting just the days, without explicitly subtracting the 23:59:59 part. Either gives you 19072.
db<>fiddle

Migrating Oracle query to PostgreSQL

Can you please help me with this? How can I convert below query to PostgreSQL.
The query below gives different output when executed in PostgreSQL than when executed in Oracle.
SELECT
to_char(to_date('01011970','ddmmyyyy') + 1/24/60/60 * 4304052,'dd-mon-yyyy hh24:mi:ss')
from dual;
Let's assume you want to use the same expression as in Oracle to compute the resulting value.
The reason it is not working when you simply remove from dual is because this expression is being evaluated to 0 as integer division truncates results towards 0.
select 1/24/60/60 * 4304052;
?column?
----------
0
(1 row)
If I make one of them a decimal, it will give you the required result
select 1.0/24/60/60 * 4304052;
?column?
-----------------------------
49.815416666666666347848000
Now, after changing this, your expression will return the same result you got in Oracle.
SELECT to_char( to_date('01011970','ddmmyyyy')
+ INTERVAL '1 DAY' * (1.0/24/60/60 * 4304052) ,'dd-mon-yyyy hh24:mi:ss') ;
to_char
----------------------
19-feb-1970 19:34:12
(1 row)
Note that I had to add an interval expression, because unlike Oracle, a Postgres DATE does not store time component and simply adding a number to date will result in an error. Using an interval will ensure that it will be evaluated as timestamp.
knayak=# select pg_typeof( current_date);
pg_typeof
-----------
date
(1 row)
knayak=# select pg_typeof( current_date + INTERVAl '1 DAY');
pg_typeof
-----------------------------
timestamp without time zone
(1 row)
I think you want:
select '1970-01-01'::date + 4304052 * interval '1 second';
You can use to_char() to convert this back to a string, if you really want:
select to_char('1970-01-01'::date + 4304052 * interval '1 second', 'YYYY-MM-SS HH24:MI:SS');

How to get Epoch with Microsecond accuracy from a timestamp column in Postgres?

In Postgres I am trying to create a timestamp field with microsecond accuracy.
If I query for the timestamp in printable format, it displays the date with the exact microseconds, but if I query for the epoch, it only displays 5 digits...
Example:
CREATE TABLE date_test (datetime timestamp(6) WITH TIME ZONE);
INSERT INTO date_test VALUES(TO_TIMESTAMP(1525745241.879112));
SELECT datetime FROM date_test; //returns 2018-05-08 12:07:21.879112+10
SELECT EXTRACT(epoch FROM datetime) from date_test; //returns 1525745241.87911
What is the reason for the last digit to be lost? Is the query inherently converting to floating point and losing accuracy? Is there any easy way to store dates with microsecond precision in Postgres?
It's ugly, but on the top of a head:
t=# select extract(epoch from TO_TIMESTAMP(1525745241.879112) - '1000000000 seconds'::interval)::numeric(20,6) + 1000000000;
?column?
-------------------
1525745241.879112
(1 row)
https://www.postgresql.org/docs/current/static/functions-datetime.html
extract returns double precision which is 15 decimal digits precision, so microseconds just do not fit...
Maybe:
select replace(extract (epoch from timestamp 'now')::text, '.', '');
select replace(extract (epoch from timestamp 'now')::text, '.', '')::bigint;
Or using maths...
select EXTRACT(epoch FROM now()) * 1000000

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