How to parse a ZoneDateTime format? - postgresql

I'm currently trying to parse a timestamp that looks like: 2020-08-03T11:37:42.529Z[UTC]
This timestamp was generated with Java's ZoneDateTime data type
What I have already tried was to parse it via:
SELECT '2020-08-03T11:37:42.529Z[UTC]'::timestamp with time zone;
But that would fail with an exception (unless I parse up until timezone Z).
Edit
For clarification, this is currently a String that is saved in a file, so this application has no direct interaction with Java.

As explained by Adrian, the below code (using at time zone) does not really work. The only other alternative I can think of is to replace the timezone abbreviation with a proper offset. To do that, a function is probably the easiest solution:
create or replace function replace_tz_abbrev(p_input text)
returns text
as
$$
declare
l_offset_hours text;
l_tz_abbrev text;
begin
l_tz_abbrev := substring(p_input from '\[([A-Z]+)\]');
select to_char(utc_offset, 'hh24:mi')
into l_offset_hours
from pg_catalog.pg_timezone_abbrevs
where abbrev = l_tz_abbrev;
return regexp_replace(p_input, '\[[A-Z]+\]', l_offset_hours);
end;
$$
language plpgsql;
This is a rough sketch, the function needs some error checking in case the abbreviation doesn't exist. Maybe checking pg_timezone_names as a fallback to deal with names like Europe/Berlin.
The result of replace_tz_abbrev() can be cast to a timestamptz (at least with the given example). This can either be done in the function itself (and changing it to returns timestamptz or when calling it.
The below is not correct
(I'll just leave it here for reference, so that the comments
One way I can think of is to extract the time zone from the string and use it together with the to_timestamp() function:
with data (input) as (
values ('2020-08-03T11:37:42.529Z[UTC]')
)
select to_timestamp(input, 'yyyy-mm-dd"T"hh24:mi:ss.ms') at time zone substring(input from '\[([A-Z]+)\]')
from data;

Assuming the timestamp string always ends with [timezone] then:
select regexp_replace('2020-08-03T11:37:42.529Z[UTC]', '\[[^\]]*\]', '')::timestamp with time zone;
regexp_replace
-----------------------------
08/03/2020 04:37:42.529 PDT
Where regexp_replace() replaces the [timezone] with an empty string and then you cast the 2020-08-03T11:37:42.529Z portion to a timestamp with time zone.

You can't directly convert this format to a timestamp with timezone. if you are allowed to manipulate the string to get the timedate separtly from the timezone you can do this:
select TIMESTAMP 'The date and time go here'AT TIME ZONE 'timezone name or abbreviation go here';
select TIMESTAMP '2020-08-03T11:37:42.529'AT TIME ZONE 'Africa/Dar_es_Salaam';
select TIMESTAMP '2020-08-03T11:37:42.529'AT TIME ZONE 'UTC';
The reason you have to split the string is that like you discovered a simple conversion does not work and the function TO_TIMESTAMP() that allows you to specify the format does not support specifiying in the format that the string will contain a timezone, only a time offset (example: -03 hours).
-- No way to include the timezone name in the format param.
-- if you insist to add 'TZ'or 'tz' you will get "ERROR: "TZ"/"tz"/"OF" format patterns are not supported in to_date"
select TO_TIMESTAMP('2020-08-03T11:37:42.529Z[UTC]','YYYY-MM-DD HH24:MI:SS.MS')
The difference between the a timezone and a time offset is also why you might not want to do what you are trying to do, postgres does not store timezones (or time offsets). Instead it converts to UTC, and later on reads the column as UTC if the column is 'timestamp without timezone', if the column is 'timestamp with timezone' it converts from UTC to what ever the current session is set to. if you care about the timezone you should store it in another column.

Related

Postgres: SQL Error [42846]: ERROR: cannot cast type bigint to date [duplicate]

I am new to postgresql bot not to sql in general. I have a table that I need to read values from, on of the columns is a unix timestamp that I want to convert in to a more human readable format thus I found this:
SELECT lt,dw,up,to_char(uxts, 'YYYY-MM-DD HH24:MI:SS')
from products;
But that produces an error:
ERROR: multiple decimal points
I am lost here. I am sure someone can show me how to do it. The documentation isn't that clear to me. Postgresql 9.5 is the database.
to_char() converts a number, date or timestamp to a string, not the other way round.
You want to_timestamp()
Convert Unix epoch (seconds since 1970-01-01 00:00:00+00) to timestamp
So just apply that function on your column
SELECT lt,dw,up,to_timestamp(uxts) as uxts
from products;
This assumes that uxts is some kind of number data type (integer, bigint or double precision)

Truncate time from datetime in PRESTO

I've a requirement to truncate time from datetime in Presto.
I noticed that I cannot use Trunc() function which is used in Redshift to achieve the same.
date_trunc() returns a timestamp again resetting to the start.
Cast the datetime to date
CAST(x AS DATE)
date(x) -- This is an alias for CAST(x AS DATE).
IF the date/time values to be converted are held as strings then such a conversion might fail; in which using TRY_CAST() is recommended as this returns NULL if the conversion fails.

How to convert timestamp field to ISO 8601 string in a given time zone? [duplicate]

This question already has answers here:
PostgreSQL - how to render date in different time zone?
(2 answers)
Closed 5 years ago.
I have a "timestamp with time zone" field in a table.
I need to return is as iso 8601 string in a give time zone.
The closest thing that I managed to do is this:
select to_char(crtdate, 'YYYY-MM-DD"T"HH24:MI:SS.MSOF:"00"') from t1
But it returns a timestamp in a system default time zone (UTC) i.e. something like this:
2017-07-12T02:46:26.194+00:00
while I need it be formatted for a specific time zone.
E.g.
2017-07-12T14:46:26.194+12:00
for "Pacific/Auckland".
Can someone please advise how this can be achieved?
we are on PG v 9.6.
Thank you,
You can play with GUC parameters datestyle and timezone inside a function to get what you want. Here is an example (however, it returns microseconds, so probably you'll need to tune it a bit):
create or replace function timestamp_iso8601(ts timestamptz, tz text) returns text as $$
declare
res text;
begin
set datestyle = 'ISO';
perform set_config('timezone', tz, true);
res := ts::timestamptz(3)::text;
reset datestyle;
reset timezone;
return replace(res, ' ', 'T') || ':00';
end;
$$ language plpgsql volatile;
Results:
test=# select timestamp_iso8601(now()::timestamptz, 'Europe/Moscow');
timestamp_iso8601
-------------------------------
2017-07-12T08:56:58.692985+03:00
test=# select timestamp_iso8601(now()::timestamptz, 'Pacific/Auckland');
timestamp_iso8601
-------------------------------
2017-07-12T17:59:05.863483+12:00
(1 row)
Update: edited. You can use timestamptz(3), specifying the precision (by default, it will go with microseconds, while 3 will keep only milliseconds). Alternatively, you can use res := to_char(ts::timestamptz, 'IYYY-MM-DDT HH24:MI:SS:MSOF'); instead of ::timestamptz(3)::text conversion chain, and in this case (3) will not be needed.

Is there any equivallent function in postgres like systimestamp() of oracle

I'm working on a oracle to postgresql database migration project. I need to read operating system date and time from postgres. In oracle sysdate() returns system date time in date type data and systimestamp() returns timestamp type data whatever the time_zone variable set. But in postgres current_date() and current_timestamp() always give the result relative to the timezone variable set for that database.
Synchronizing the timezone variable (i.e. set timezone='utc') is one way, but I don't want my timezone variable to be changed.
All I want to get is the current date and time of my system (time zone may include or not) like in oracle. Any pl/pgsql would be helpful. Thanks
The data type TIMESTAMP WITH TIME ZONE is different in Oracle and PostgreSQL.
While Oracle stores the timestamp information along with the timestamp, PostgreSQL stores the timestamp in UTC and displays it in the currently set time zone (available with the SQL statement SHOW TimeZone).
So the functions return the same time in PostgreSQL and Oracle, but it is displayed in a different fashion. That should normally be no problem.
If you really need to store time zone information along with a timestamp, you'll have to use a separate field to store the time zone information. You can then use AT TIME ZONE to convert the timestamp to that time zone for display.

Oracle - Get date from date time using to_date()

I getting an XML date/time from an XML file and need to load it in a datatype of just Date, so I have to truncate the time.
I would like to try something like this in order to let Oracle do the truncation:
TO_DATE('2015-11-04 13:45:19.387-05:00','YYYY-MM-DD')
I verify this failed by running this:
SELECT TO_DATE('2015-11-04 13:45:19.387-05:00','YYYY-MM-DD') FROM DUAL
It throws error: ORA-01830: data format picture ends before converting entire string.
I'm trying to minimize change to my C# program that is building the SQL statements. If I need to, I can change my C# code to generate this:
TO_DATE('2015-11-04','YYYY-MM-DD')
Can Oracle handle the truncation or must I do it in my program?
If you're storing your information in a DATE column in Oracle (which accepts date-and-time), then the following should help you see what you need to do:
select to_timestamp_tz('2015-11-04 13:45:19.387-05:00','YYYY-MM-DD hh24:mi:ss.ff3tzh:tzm') tz,
to_date(to_char(to_timestamp_tz('2015-11-04 13:45:19.387-05:00','YYYY-MM-DD hh24:mi:ss.ff3tzh:tzm'), 'dd/mm/yyyy hh24:mi:ss'), 'dd/mm/yyyy hh24:mi:ss') tz_char_date,
cast(to_timestamp_tz('2015-11-04 13:45:19.387-05:00','YYYY-MM-DD hh24:mi:ss.ff3tzh:tzm') as date) tz_cast_date
from dual;
TZ TZ_CHAR_DATE TZ_CAST_DATE
---------------------------------------- ------------------- ---------------------
04/11/2015 13:45:19.387000000 -05:00 04/11/2015 13:45:19 2015-11-04 13:45:19
An alternative is to use substr to shorten the string to get the portion you're interested in:
select to_date(substr('2015-11-04 13:45:19.387-05:00', 1, 19), 'yyyy-mm-dd hh24:mi:ss') substr_date
from dual;
SUBSTR_DATE
---------------------
2015-11-04 13:45:19
You can use the to_timestamp_tz() function to convert the string from XML into a timestamp with timezone value:
SELECT TO_TIMESTAMP_TZ('2015-11-04 13:45:19.387-05:00','YYYY-MM-DD HH24:MI:SS.FFTZH:TZM')
FROM DUAL;
TO_TIMESTAMP_TZ('2015-11-0413:45:19.387-05:00','YYYY-MM-DDHH24:MI:SS.FFTZH:TZM')
--------------------------------------------------------------------------------
04-NOV-15 13.45.19.387000000 -05:00
You can then use the trunc() function to truncate the time portion to midnight; this also converts it implicitly from a timestamp to a date:
SELECT TRUNC(TO_TIMESTAMP_TZ('2015-11-04 13:45:19.387-05:00','YYYY-MM-DD HH24:MI:SS.FFTZH:TZM'))
FROM DUAL;
TRUNC(TO_TIMESTAMP_TZ('2015-11-0413:45:19.387-05:00','YYYY-MM-DDHH24:MI:SS.FFTZH
--------------------------------------------------------------------------------
04-NOV-15
This ignores the actual time zone - essentially assuming the values are in your system timezone (i.e. you are in the same -05:00 region).
You could also use a substring to strip the time and timezone part from the raw string before converting:
SELECT TO_DATE(SUBSTR('2015-11-04 13:45:19.387-05:00', 1, 10), 'YYYY-MM-DD')
FROM DUAL;
TO_DATE(SUBSTR('2015-11-0413:45:19.387-05:00',1,10),'YYYY-MM-DD')
-----------------------------------------------------------------
04-NOV-15
... or performing the substring in C# if you prefer, assuming that is parsing the XML document.
(These are intentionally displaying in a format that is different from the ISO standard so they don't look like the original string value from your XML; they are actual date/timestamp with time zone types, my client is just using my NLS settings.)
You also have the option of using Oracle's built-in XML handling to extract relational data from your document, but that's rather off-topic and might not be appropriate.