Postgres equivalent for TO_UTC_TIMESTAMP_TZ - postgresql

What would be the Postgres equivalent for TO_UTC_TIMESTAMP_TZ function in oracle.
For the below query:
SELECT TO_UTC_TIMESTAMP_TZ('1998-01-01') FROM DUAL;
The result is "01-JAN-98 12.00.00.000000000 AM GMT" in oracle.

I am not entirely sure what the Oracle function does, but I think this should be equivalent:
TO_TIMESTAMP('1998-01-01', 'yyyy-mm-dd') at time zone 'UTC' at time zone 'GMT'

Append 'T00:00 UTC' and cast to timestamptz.
select ('1998-01-01'||'T00:00 UTC')::timestamptz;
Check:
select to_char(
('1998-01-01'||'T00:00 UTC')::timestamptz,
'yyyy-mm-dd hh24:mi:ss.us OF'
);
Result "1998-01-01 02:00:00.000000 +02".
This is exactly the same as "01-JAN-98 12.00.00.000000000 AM GMT".
It might make sense to define a compatibility function.
create function to_utc_timestamp_tz(ts text) returns timestamptz immutable as
$$
select (ts || 'T00:00 UTC')::timestamptz;
$$ language sql;

Related

What is the format for an `INTERVAL` parameter in a `format()` call

I'm updating an existing stored function, handling two additional parameters and inserting them, where required. Within the function, I INSERT a row into a table using a call to EXECUTE format(), something like this...
CREATE OR REPLACE PROCEDURE function_p (
p_name TEXT DEFAULT '',
p_step INT DEFAULT NULL,
p_project_duration INTERVAL DEFAULT '2W',
)
LANGUAGE plpgsql AS $$
BEGIN
EXECUTE format('
INSERT INTO table_2 (column_1, column_2, column_3, name, step)
SELECT one_column, two_column, three_column, %L, %s
FROM generate_series(now()::DATE, now()::DATE + %L, INTERVAL ''1 day'') d
CROSS JOIN table_1',
p_name, p_step, p_project_duration);
END;
$$
I know this is a pretty rubbish example, but it's pseudo-code, ok?! ;)
In the generate_series() call...
If I use format %L I get error "operator is not unique: date + unknown"
If I use format %I I get error "column "P14d" does not exist"
If I use format %s I get error "column "P14d" does not exist"
Don't pass strings as parameters, use placeholders and pass the correct data types. Only use the format() placeholders for the identifiers, pass the actual parameters with the using clause of the execute command:
CREATE OR REPLACE PROCEDURE function_p (
p_name TEXT DEFAULT '',
p_step INT DEFAULT NULL,
p_project_duration INTERVAL DEFAULT '2W',
)
LANGUAGE plpgsql AS $$
BEGIN
EXECUTE format('
INSERT INTO table_2 (column_1, column_2, column_3, name, step)
SELECT one_column, two_column, three_column, %L, %s
FROM generate_series($1, $2, $3) d
CROSS JOIN table_1', p_name, p_step)
using current_date, current_date + p_project_duration, interval '1 day';
END;
$$
It seems I've found a hack-fix/cludge/bodge, yay!
It works if I use the '%s' format and then cast it to INTERVAL.
FROM generate_series(now()::DATE, now()::DATE + ''%s''::INTERVAL, INTERVAL ''1 day'') d

How to extract miliseconds from timestamp dynamically in postgres plpgsql?

I want to get milliseconds from a 'Timestamp with timezone' using a plsql function.
I am able to generate the following function, but it is leading to truncation of miliseconds.
CREATE or REPLACE FUNCTION getMSFromTime(t1 timestamp with time zone)
RETURNS bigint AS $$
declare time1 double precision ;
BEGIN
SELECT EXTRACT(EPOCH FROM t1) into time1;
return time1;
END;
$$ LANGUAGE plpgsql;
but it ignores miliseconds
SELECT getMSFromTime('2019-02-11 08:01:33.423+00') //1549872093
SELECT getMSFromTime('2019-02-11 08:01:33.000+00') //1549872093
I am able to get a PostgreSQL way so that millisecond decimals are preserved as well, using:
SELECT EXTRACT(EPOCH FROM TIMESTAMP WITH TIME ZONE '2019-02-11 08:01:33.423+00'); // 1549872093.423
But I am not able to integrate it into a function and it gives following error:
syntax error at or near "t1"
LINE 5: ...ELECT EXTRACT(EPOCH FROM TIMESTAMP WITH TIME ZONE t1) into t..
CREATE or REPLACE FUNCTION getMSFromTime2(t1 timestamp with time zone)
RETURNS bigint AS $$
declare time1 double precision ;
BEGIN
SELECT EXTRACT(EPOCH FROM TIMESTAMP WITH TIME ZONE t1) into time1;
return time1;
END;
$$ LANGUAGE plpgsql;
Please suggest a way so as to create a PostgreSQL function which can do this functionality.
extract() returns a double value that represents seconds, by casting that to a bigint you lose the fractional seconds (=milliseconds)
If you want a bigint representing milliseconds, you need to multiple the result with 1000.
There is also no reason to use PL/pgSQL for such a simple thing:
CREATE or REPLACE FUNCTION getmsfromtime(t1 timestamp with time zone)
RETURNS bigint
AS $$
SELECT (EXTRACT(EPOCH FROM t1)*1000)::bigint;
$$
LANGUAGE sql;

LAST_DAY function in postgres

Is there any function(s) in postgres equivalent to Oracle function LAST_DAY().
I need to get last day in postgres (including month and year)
Well, In postgres, it seems there's no such function equivalent to LAST_DAY() available in oracle.
If you need to, you can have your own in the following ways as a
Select Query
SELECT (date_trunc('MONTH', now()) + INTERVAL '1 MONTH - 1 day')::date;
plsql Function
CREATE OR REPLACE FUNCTION last_day(date)
RETURNS date AS
$$
SELECT (date_trunc('MONTH', $1) + INTERVAL '1 MONTH - 1 day')::date;
$$ LANGUAGE 'sql'
IMMUTABLE STRICT;
Hope this helps.
create or replace funCtion last_day(fromdt anyelement)
returns date as
$BODY$
SELECT (date_trunc('MONTH', cast(fromdt as date)) + INTERVAL '1 MONTH - 1 day')::date;
$BODY$
LANGUAGE sql VOLATILE
COST 100;
ALTER FUNCTION last_day(anyelement)
OWNER TO postgres;

How to convert DATETIME to FILETIME value in T-SQL?

I need to convert a SQL Server DATETIME value to FILETIME in a T-SQL SELECT statement (on SQL Server 2000). Is there a built-in function to do this? If not, can someone help me figure out how to implement this conversion routine as a UDF (or just plain Transact-SQL)? Here is what I know:
FILETIME is 64-bit value representing the number of 100-nanosecond intervals since
January 1, 1601 (UTC) (per MSDN: FILETIME Structure).
SQL Server base time starts on 1900-01-01 00:00:00 (per SELECT CAST(0 as DATETIME)).
I found several examples showing how to convert FILETIME values to T-SQL DATETIME (I'm not 100% sure they are accurate, though), but could not find anything about reverse conversion. Even the general idea (or algorithm) would help.
Okay, I think I was able to implement this myself. Here is the function:
IF EXISTS
(
SELECT 1
FROM sysobjects
WHERE id = OBJECT_ID('[dbo].[fnDateTimeToFileTime]')
AND type = 'FN'
)
BEGIN
DROP FUNCTION [dbo].[fnDateTimeToFileTime]
END
GO
-- Create function.
CREATE FUNCTION [dbo].[fnDateTimeToFileTime]
(
#DateTime AS DATETIME
)
RETURNS
BIGINT
BEGIN
IF #DateTime IS NULL
RETURN NULL
DECLARE #MsecBetween1601And1970 BIGINT
DECLARE #MsecBetween1970AndDate BIGINT
SET #MsecBetween1601And1970 = 11644473600000
SET #MsecBetween1970AndDate =
DATEDIFF(ss, CAST('1970-01-01 00:00:00' as DATETIME), #DateTime) *
CAST(1000 AS BIGINT)
RETURN (#MsecBetween1601And1970 + #MsecBetween1970AndDate) * CAST(10000 AS BIGINT)
END
GO
IF ##ERROR = 0
GRANT EXECUTE ON [dbo].[fnDateTimeToFileTime] TO Public
GO
It seems to be accurate up to 1 second, which is okay with me (I could not make it more accurate due to data overflow). I used the TimeAndDate web tool to calculate the durations between dates.
What do you think?
2 SQL Server time era starts on
1900-01-01 00:00:00 (per SELECT CAST(0
as DATETIME).
No, that is the base date, datetime starts at 1753
run this
select cast('17800122' as datetime)
output
1780-01-22 00:00:00.000
But this is still less than filetime so you need to add that...however remember the gregorian and Julian calendars (also the reason that datetime starts at 1753)
The accepted answer work well, but will crash for date above 19 January 2038. Either use
DATEDIFF_BIG instead of DATEDIFF if you are on SQL Server 2016 or above, or use the following correction
CREATE FUNCTION [dbo].[fnDateTimeToFileTime]
(
#DateTime AS DATETIME
)
RETURNS
BIGINT
BEGIN
IF #DateTime IS NULL
RETURN NULL
DECLARE #MsecBetween1601And1970 BIGINT
DECLARE #MsecBetween1970AndDate BIGINT
DECLARE #MaxNumberDayBeforeOverflowDateDiff int;
SET #MaxNumberDayBeforeOverflowDateDiff = 24855; --SELECT DATEDIFF(day, CAST('1970-01-01 00:00:00' as DATETIME), CAST('2038-01-19 00:00:00' as DATETIME))
DECLARE #nbMaxDaysBetween1970AndDate int;
SET #nbMaxDaysBetween1970AndDate = DATEDIFF(day, CAST('1970-01-01 00:00:00' as DATETIME), #DateTime) / #MaxNumberDayBeforeOverflowDateDiff;
DECLARE #moduloResteDay int
SET #moduloResteDay = DATEDIFF(day, CAST('1970-01-01 00:00:00' as DATETIME), #DateTime) % #MaxNumberDayBeforeOverflowDateDiff;
DECLARE #nbSecondBefore19700101And20380119 bigint = 2147472000;
SET #MsecBetween1601And1970 = 11644473600000;
DECLARE #DateTimeModulo datetime;
SET #DateTimeModulo = DATEADD(day, -#nbMaxDaysBetween1970AndDate * #MaxNumberDayBeforeOverflowDateDiff, #DateTime)
SET #MsecBetween1970AndDate = CAST(CAST(#nbMaxDaysBetween1970AndDate as bigint) * #nbSecondBefore19700101And20380119 +
DATEDIFF(ss, CAST('1970-01-01 00:00:00' as DATETIME), #DateTimeModulo) as bigint)*
CAST(1000 AS BIGINT)
RETURN (#MsecBetween1601And1970 + #MsecBetween1970AndDate) * CAST(10000 AS BIGINT)
END

What's the Postgresql equivalent of string.Format("{0:s}", DateTime.Now)?

What's the equivalent of sortable datetime/timestamp varchar in PostgreSQL?
Console.WriteLine("{0:s}", DateTime.Now);
sample format output:
2000-05-04T15:30:59
This works, SELECT now::varchar, output is '2009-03-25', but only for date type, wondering what's the equivalent for timestamp.
Note, i know date is sortable in and of itself, I just encounter a DateTime incompatibility between .NET and Mono, so i'll just transport(Remoting) date/timestamp types as varchar, underlying database type is still proper date/timestamp field type. For those who encounter this same problem, the work-around is to cast the date to varchar when retrieving the data, and casting the varchar back to date when saving.
Basically you just use to_char function.
The problem with your example, is that while theoretically this should work:
select to_char( now(), 'YYYY-MM-DDTHH24:MI:SS' );
In reality - PostgreSQL cannot "understand" that T is separator and HH24 is next "tag", and prints it as:
2009-03-24THH24:32:45
But you can change it relatively simply to:
select translate( to_char( now(), 'YYYY-MM-DD HH24:MI:SS' ), ' ', 'T' );
which outputs requested format:
2009-03-24T21:33:21
Of course remembering always to use translate gets old pretty fast, so instead you can create your own function and use it instead:
create function custom_ts(timestamptz) returns text as $$
select translate( to_char( $1, 'YYYY-MM-DD HH24:MI:SS' ), ' ', 'T' );
$$ language sql;
# select custom_ts(now());
custom_ts
---------------------
2009-03-24T21:35:47
(1 row)
Or simply:
SELECT TO_CHAR(CURRENT_TIMESTAMP, 'YYYY-MM-DD"T"HH24:MI:SS');