Having a problem finding those records between yesterday and today based on a UTC column value using PostgreSQL - postgresql

Using PostgreSQL and trying to query a table that contains a UTC column. The UTC column is a column without the timezone as I understand it from the developer. Example in my DB for a record is (2021-08-26 13:59:26.867578). And I have to search for records that are between yesterday's date and today's date. When I tried the SQL statement below I get this error:
[42883] ERROR: operator does not exist: timestamp without time zone - integer Hint: No operator matches the given name and argument types. You might need to add explicit type casts. Position: 63
Here is my SQL statement for PostgreSQL:
SELECT omd.*
FROM "OCRMetaDatas" omd
WHERE (omd."ScannedAt")-5 BETWEEN Now()-1 and now()
ORDER BY omd."ScannedAt" desc;
Any help/direction would be appreciated. Thanks.

You can't operate integer with datetimes. Here you are trying to do that twice:
(omd."ScannedAt")-5
and
Now()-1
You should use INTERVAL with datetime there, such as:
SELECT omd.*
FROM "OCRMetaDatas" omd
WHERE (omd."ScannedAt")- '5 days'::INTERVAL BETWEEN Now()- '1 day'::INTERVAL and now()
ORDER BY omd."ScannedAt" desc;

As illustration of how to use at time zone:
--My TimeZone
show TimeZone;
TimeZone
------------
US/Pacific
select '2021-08-26 13:59:26.867578'::timestamp;
timestamp
----------------------------
2021-08-26 13:59:26.867578
select '2021-08-26 13:59:26.867578'::timestamp at time zone 'UTC';
timezone
-------------------------------
2021-08-26 06:59:26.867578-07
select now();
now
-------------------------------
2021-08-26 09:23:57.818477-07
at time zone will normalize the timestamps to the same time zone.

Related

Why does PostgreSQL apparently coerce a string with timestamp to `TIMESTAMP WITH TIME ZONE` even without the time zone offset?

I am on PostgreSQL v11.10, and have the TimeZone set to UTC.
When I say select '2021-02-16 17:45+00' at time zone 'America/New_York';, I get 2021-02-16 12:45:00, which is correct and expected.
However, when I say select '2021-02-16 17:45' at time zone 'America/New_York';, I get the same result.
It seems like both strings are coerced to timestamp with time zone, which seems a bit counterintuitive to me in the case of the latter one. Why does PostgreSQL behave like that? Is it documented in the manual (I looked at the following places: AT TIME ZONE, then Date/Time Types and also Date/Time Input Interpretation and Handling of Invalid or Ambiguous Timestamps, all to no avail).
Because your TimeZone is set to UTC, any timestamp without a specified timezone will be interpreted as local to the UTC timezone. Since you as asking for a timestamp to be interpreted as America/New_York, Postgres will first interpret the timestamp as a UTC timestamp, then do the math to convert it to America/New_York.
Note that if you actually look at the types being sent to Postgres, it is not coercing into timestamp with timezone unless you specify it:
edb=# select pg_typeof('2021-02-16 17:45' at time zone 'America/New_York');
pg_typeof
-----------------------------
timestamp without time zone
(1 row)
edb=# select pg_typeof('2021-02-16 17:45+00' at time zone 'America/New_York');
pg_typeof
-----------------------------
timestamp without time zone
(1 row)
edb=# select pg_typeof('2021-02-16 17:45+00'::timestamptz at time zone 'America/New_York');
pg_typeof
-----------------------------
timestamp without time zone
(1 row)
edb=# select pg_typeof('2021-02-16 17:45+00'::timestamptz);
pg_typeof
--------------------------
timestamp with time zone
(1 row)
edb=# select pg_typeof('2021-02-16 17:45+00'::timestamp);
pg_typeof
-----------------------------
timestamp without time zone
(1 row)

Postgres cast to TIMESTAMPTZ

What is the behavior of PostgreSQL when we cast a DATE to TIMESTAMP to TIMESTAMPTZ
What time zone is used?
PostgreSQL server
Client that run the query (Current session)
If you cast a date to a timestamp, time zones don't play a role, because both data types are without a time zone. The resulting timestamp will be the beginning of the day.
If you cast date to timestamp with time zone, resulting timestamp will be the beginning of the date in the time zone defined by the parameter timezone in your current session.
SHOW timezone;
TimeZone
---------------
Europe/Vienna
(1 row)
SELECT CAST (DATE '2021-01-15' AS timestamp);
timestamp
---------------------
2021-01-15 00:00:00
(1 row)
SELECT CAST (DATE '2021-01-15' AS timestamp with time zone);
timestamptz
------------------------
2021-01-15 00:00:00+01
(1 row)
Casting date to timestamp will append time 00:00:00.0 to the date.
The time zone of the current server session will be used.
By default this is the time zone setting of Postgresql server.
You can change the time zone of the current server session like this:
set time zone 'Europe/Sofia';
I do not think that the time zone of the client has any effect. More on this issue here.
Edit
As Adrian Klaver noticed "When dealing with timestamps it's best to assume the worst". Therefore better set the session time zone explicitly.
Given the situation where one needs to change the column type from integer (as epoc seconds) to timzezonetz ->
casting directly from integer to timezonetz is not possible.
but you can do this:
ALTER TABLE table_name ALTER COLUMN column_name TYPE timestamptz
USING column_name::abstime::timestamptz

date_trunc at time zone with original timestamptz

I have a single timestamptz that I want to date_trunc so it removes the hours:
2019-01-01T17:43-03 => 2019-01-01T00:00-03.
However, because date_trunc removes the timezone, I need to do it like this:
date_trunc('day', '2019-01-01T17:43-03'::timestamptz) at time zone '-03'
However, I do not want to hardcode the time zone, since the query is run with timestamptz in many different timezones (these are input to the query and not stored). So I want the timezone to be extracted from the original timestamp. I tried to do something like this, but it does not work:
date_trunc('day', '2019-01-01T17:43-03'::timestamptz) at time zone EXTRACT(...)
Related, I am trying to extract the timezone from a timestamptz, but just getting 0.
SELECT EXTRACT(timezone FROM TIMESTAMP WITH TIME ZONE '2019-01-01T00:00+03')
0
Can anybody help me with this?
I believe you may have a misconception about how timestamps and timezones are stored in PostgreSQL (because you seem to expect the -03 to be preserved when calling date_trunc and that you "want the timezone to be extracted from the original timestamp"). According to the documentation:
When a timestamp with time zone value is output, it is always converted from UTC to the current timezone zone, and displayed as local time in that zone. To see the time in another time zone, either change timezone or use the AT TIME ZONE construct (see Section 9.9.3).
Therefore, the statement that "different clients [that] have timestampts in many different timezones," while true, are all translated to UTC for storage, and then displayed in your local timezone (or the timezone you specify) for output. As such, calling date_trunc() will essentially truncate the UTC timestamp, and if you want it displayed in a specific timezone, you will need to add the AT TIME ZONE clause.
UPDATE: An example is here:
edb=# select date_trunc('day', '2019-01-01T17:43-03'::timestamptz) ;
date_trunc
------------------------
2019-01-01 00:00:00+00
(1 row)
edb=# set timezone to 'US/Pacific';
SET
edb=# select date_trunc('day', '2019-01-01T17:43-03'::timestamptz) ;
date_trunc
------------------------
2019-01-01 00:00:00-08
(1 row)
As you can see, date_trunc will append the timezone that I defineā€”it is not omitted.

How to get the EPOCH of a concatenated timestamp in PostgreSQL

In PostgreSQL, how do I get the epoch (UTC time) of a table date field whose time zone is assumed to be GMT and whose hours, min, seconds value is assumed to be '00:00:00'?
e.g. mytable.DT_GMT has values like '2018-10-16'.
Here's what I've tried so far. Instead of DT_GMT, I've just used '2018-10-16'. I'll replace it with the table field once I have some thing that works.
postgres=> select extract(epoch from TIMESTAMP WITH TIME ZONE concat('2018-10-16',' 00:00:00.00-00') );
ERROR: syntax error at or near "concat"
LINE 1: ...elect extract(epoch from TIMESTAMP WITH TIME ZONE concat('20...
Trying something related:
postgres=> SELECT TIMESTAMP ('2018-10-16' || ' 00:00:00.00') AT TIME ZONE 'GMT';
ERROR: syntax error at or near "'2018-10-16'"
LINE 1: SELECT TIMESTAMP ('2018-10-16' || ' 20:38:40') AT TIME ZONE ...
Note that the hours...sec is necessary because without it those values are not what's required.
postgres=> SELECT TIMESTAMP '2018-10-16' AT TIME ZONE 'GMT';
timezone
------------------------
2018-10-15 17:00:00-07
(1 row)
Instead of a string concatenation, you add them as date and time with time zone.
test=> select extract(epoch from date '2018-10-16' + time with time zone '00:00:00.00-00');
date_part
------------
1539648000
(1 row)
If you're only adding '00:00:00' to make the time zones work, you don't need it with a date.
test=> select date_part('epoch', date '2018-10-16');
date_part
------------
1539648000
$ date --date '#1539648000' --utc
Tue Oct 16 00:00:00 UTC 2018
This is a good reason to store dates and times as their proper type, especially in Postgres which cares a lot about types, rather than as text.

Using current time in UTC as default value in PostgreSQL

I have a column of the TIMESTAMP WITHOUT TIME ZONE type and would like to have that default to the current time in UTC. Getting the current time in UTC is easy:
postgres=# select now() at time zone 'utc';
timezone
----------------------------
2013-05-17 12:52:51.337466
(1 row)
As is using the current timestamp for a column:
postgres=# create temporary table test(id int, ts timestamp without time zone default current_timestamp);
CREATE TABLE
postgres=# insert into test values (1) returning ts;
ts
----------------------------
2013-05-17 14:54:33.072725
(1 row)
But that uses local time. Trying to force that to UTC results in a syntax error:
postgres=# create temporary table test(id int, ts timestamp without time zone default now() at time zone 'utc');
ERROR: syntax error at or near "at"
LINE 1: ...int, ts timestamp without time zone default now() at time zo...
A function is not even needed. Just put parentheses around the default expression:
create temporary table test(
id int,
ts timestamp without time zone default (now() at time zone 'utc')
);
Still another solution:
timezone('utc', now())
Wrap it in a function:
create function now_utc() returns timestamp as $$
select now() at time zone 'utc';
$$ language sql;
create temporary table test(
id int,
ts timestamp without time zone default now_utc()
);
What about
now()::timestamp
If your other timestamp are without time zone then this cast will yield the matching type "timestamp without time zone" for the current time.
I would like to read what others think about that option, though. I still don't trust in my understanding of this "with/without" time zone stuff.
EDIT:
Adding Michael Ekoka's comment here because it clarifies an important point:
Caveat. The question is about generating default timestamp in UTC for
a timestamp column that happens to not store the time zone (perhaps
because there's no need to store the time zone if you know that all
your timestamps share the same). What your solution does is to
generate a local timestamp (which for most people will not necessarily
be set to UTC) and store it as a naive timestamp (one that does not
specify its time zone).
These are 2 equivalent solutions:
(in the following code, you should substitute 'UTC' for zone and now() for timestamp)
timestamp AT TIME ZONE zone - SQL-standard-conforming
timezone(zone, timestamp) - arguably more readable
The function timezone(zone, timestamp) is equivalent to the SQL-conforming construct timestamp AT TIME ZONE zone.
Explanation:
zone can be specified either as a text string (e.g., 'UTC') or as an interval (e.g., INTERVAL '-08:00') - here is a list of all available time zones
timestamp can be any value of type timestamp
now() returns a value of type timestamp (just what we need) with your database's default time zone attached (e.g. 2018-11-11T12:07:22.3+05:00).
timezone('UTC', now()) turns our current time (of type timestamp with time zone) into the timezonless equivalent in UTC.
E.g., SELECT timestamp with time zone '2020-03-16 15:00:00-05' AT TIME ZONE 'UTC' will return 2020-03-16T20:00:00Z.
Docs: timezone()
Function already exists:
timezone('UTC'::text, now())