Postgres cast to TIMESTAMPTZ - postgresql

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

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)

Date and time in UTC - how to store them in postgres?

I am getting my data: date and time in UTC, in a csv file format in separate columns. Since I will need to convert this zone to date and time of the place where I live, currently in summer to UTC+2, and maybe some other zones I was wondering what is the best practice to insert data in postgres when we are talking about type of data. Should I place both of my data in a single column or keep them separate as types: date and time, and if not should I use timestamp or timestampz (or something else).
use timestamptz it will store your time stamp in UTC. and will display it to the client according to it's locale.
https://www.postgresql.org/docs/current/static/datatype-datetime.html
For timestamp with time zone, the internally stored value is always in
UTC (Universal Coordinated Time, traditionally known as Greenwich Mean
Time, GMT). An input value that has an explicit time zone specified is
converted to UTC using the appropriate offset for that time zone. If
no time zone is stated in the input string, then it is assumed to be
in the time zone indicated by the system's TimeZone parameter, and is
converted to UTC using the offset for the timezone zone.
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).
updated with another good point from Lukasz, I had to mention:
Also in favor of single column is the fact that if you would store
both date and time in separate columns you would still need to combine
them and convert to timestamp if you wanted to change time zone of
date.
Not doing that would lead to date '2017-12-31' with time '23:01:01' would in other time zone in fact be not only different time, but different date with all YEAR and MONTH and DAY different
another update As per Laurenz notice, don't forget the above docs quote
An input value that has an explicit time zone specified is converted to UTC using the appropriate offset for that time zone. Which means you have to manage the input dates carefully. Eg:
t=# create table t(t timestamptz);
CREATE TABLE
t=# set timezone to 'GMT+5';
SET
t=# insert into t select '2017-01-01 00:00:00';
INSERT 0 1
t=# insert into t select '2017-01-01 00:00:00' at time zone 'UTC';
INSERT 0 1
t=# insert into t select '2017-01-01 00:00:00+02';
INSERT 0 1
t=# select * from t;
t
------------------------
2017-01-01 00:00:00-05
2017-01-01 05:00:00-05
2016-12-31 17:00:00-05
(3 rows)

Date and time as UTC timestamp in Postgres

I have date and time fields in my table. Both are set in local server time.
Is it possible to cast both fields as a single UTC ISO timestamp?
Just add the two:
SELECT date_col + time_col AS timestamp_col
The type timestamp [without time zone] is stored as UTC timestamp internally anyway. Only the display is adjusted to the time zone setting of your session. If you need to display the timestamp as UTC timestamp, use the AT TIME ZONE construct:
SELECT timestamp_col AT TIME ZONE 'UTC';
Note that this returns a timestamp with time zone when applied to a timestamp.
Ample details:
Ignoring timezones altogether in Rails and PostgreSQL
For example, to display the timestamp as timestamptz in Moscow:
SELECT (date_col + time_col) AT TIME ZONE 'Europe/Moscow' AS tstz_col

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())

Difference between timestamps with/without time zone in PostgreSQL

Are timestamp values stored differently in PostgreSQL when the data type is WITH TIME ZONE versus WITHOUT TIME ZONE? Can the differences be illustrated with simple test cases?
The differences are covered at the PostgreSQL documentation for date/time types. Yes, the treatment of TIME or TIMESTAMP differs between one WITH TIME ZONE or WITHOUT TIME ZONE. It doesn't affect how the values are stored; it affects how they are interpreted.
The effects of time zones on these data types is covered specifically in the docs. The difference arises from what the system can reasonably know about the value:
With a time zone as part of the value, the value can be rendered as a local time in the client.
Without a time zone as part of the value, the obvious default time zone is UTC, so it is rendered for that time zone.
The behaviour differs depending on at least three factors:
The timezone setting in the client.
The data type (i.e. WITH TIME ZONE or WITHOUT TIME ZONE) of the value.
Whether the value is specified with a particular time zone.
Here are examples covering the combinations of those factors:
foo=> SET TIMEZONE TO 'Japan';
SET
foo=> SELECT '2011-01-01 00:00:00'::TIMESTAMP;
timestamp
---------------------
2011-01-01 00:00:00
(1 row)
foo=> SELECT '2011-01-01 00:00:00'::TIMESTAMP WITH TIME ZONE;
timestamptz
------------------------
2011-01-01 00:00:00+09
(1 row)
foo=> SELECT '2011-01-01 00:00:00+03'::TIMESTAMP;
timestamp
---------------------
2011-01-01 00:00:00
(1 row)
foo=> SELECT '2011-01-01 00:00:00+03'::TIMESTAMP WITH TIME ZONE;
timestamptz
------------------------
2011-01-01 06:00:00+09
(1 row)
foo=> SET TIMEZONE TO 'Australia/Melbourne';
SET
foo=> SELECT '2011-01-01 00:00:00'::TIMESTAMP;
timestamp
---------------------
2011-01-01 00:00:00
(1 row)
foo=> SELECT '2011-01-01 00:00:00'::TIMESTAMP WITH TIME ZONE;
timestamptz
------------------------
2011-01-01 00:00:00+11
(1 row)
foo=> SELECT '2011-01-01 00:00:00+03'::TIMESTAMP;
timestamp
---------------------
2011-01-01 00:00:00
(1 row)
foo=> SELECT '2011-01-01 00:00:00+03'::TIMESTAMP WITH TIME ZONE;
timestamptz
------------------------
2011-01-01 08:00:00+11
(1 row)
I try to explain it more understandably than the referred PostgreSQL documentation.
Neither TIMESTAMP variants store a time zone (or an offset), despite what the names suggest. The difference is in the interpretation of the stored data (and in the intended application), not in the storage format itself:
TIMESTAMP WITHOUT TIME ZONE stores local date-time (aka. wall calendar date and wall clock time). Its time zone is unspecified as far as PostgreSQL can tell (though your application may knows what it is). Hence, PostgreSQL does no time zone related conversion on input or output. If the value was entered into the database as '2011-07-01 06:30:30', then no mater in what time zone you display it later, it will still say year 2011, month 07, day 01, 06 hours, 30 minutes, and 30 seconds (in some format). Also, any offset or time zone you specify in the input is ignored by PostgreSQL, so '2011-07-01 06:30:30+00' and '2011-07-01 06:30:30+05' are the same as just '2011-07-01 06:30:30'.
For Java developers: it's analogous to java.time.LocalDateTime.
TIMESTAMP WITH TIME ZONE stores a point on the UTC time line. How it looks (how many hours, minutes, etc.) depends on your time zone, but it always refers to the same "physical" instant (like the moment of an actual physical event). The
input is internally converted to UTC, and that's how it's stored. For that, the offset of the input must be known, so when the input contains no explicit offset or time zone (like '2011-07-01 06:30:30') it's assumed to be in the current time zone of the PostgreSQL session, otherwise the explicitly specified offset or time zone is used (as in '2011-07-01 06:30:30+05'). The output is displayed converted to the current time zone of the PostgreSQL session.
For Java developers: It's analogous to java.time.Instant (with lower resolution though), but with JDBC and JPA 2.2 you are supposed to map it to java.time.OffsetDateTime (or to java.util.Date or java.sql.Timestamp of course).
Some say that both TIMESTAMP variations store UTC date-time. Kind of, but it's confusing to put it that way in my opinion. TIMESTAMP WITHOUT TIME ZONE is stored like a TIMESTAMP WITH TIME ZONE, which rendered with UTC time zone happens to give the same year, month, day, hours, minutes, seconds, and microseconds as they are in the local date-time. But it's not meant to represent the point on the time line that the UTC interpretation says, it's just the way the local date-time fields are encoded. (It's some cluster of dots on the time line, as the real time zone is not UTC; we don't know what it is.)
Here is an example that should help. If you have a timestamp with a timezone, you can convert that timestamp into any other timezone. If you haven't got a base timezone it won't be converted correctly.
SELECT now(),
now()::timestamp,
now() AT TIME ZONE 'CST',
now()::timestamp AT TIME ZONE 'CST'
Output:
-[ RECORD 1 ]---------------------------
now | 2018-09-15 17:01:36.399357+03
now | 2018-09-15 17:01:36.399357
timezone | 2018-09-15 08:01:36.399357
timezone | 2018-09-16 02:01:36.399357+03
Timestamptz vs Timestamp
The timestamptz field in Postgres is basically just the timestamp field where Postgres actually just stores the “normalised” UTC time, even if the timestamp given in the input string has a timezone.
If your input string is: 2018-08-28T12:30:00+05:30 , when this timestamp is stored in the database, it will be stored as 2018-08-28T07:00:00.
The advantage of this over the simple timestamp field is that your input to the database will be timezone independent, and will not be inaccurate when apps from different timezones insert timestamps, or when you move your database server location to a different timezone.
To quote from the docs:
For timestamp with time zone, the internally stored value is always in
UTC (Universal Coordinated Time, traditionally known as Greenwich Mean
Time, GMT). An input value that has an explicit time zone specified is
converted to UTC using the appropriate offset for that time zone. If
no time zone is stated in the input string, then it is assumed to be
in the time zone indicated by the system’s TimeZone parameter, and is
converted to UTC using the offset for the timezone zone. To give a
simple analogy, a timestamptz value represents an instant in time, the
same instant for anyone viewing it. But a timestamp value just
represents a particular orientation of a clock, which will represent
different instances of time based on your timezone.
For pretty much any use case, timestamptz is almost always a better choice. This choice is made easier with the fact that both timestamptz and timestamp take up the same 8 bytes of data.
source:
https://hasura.io/blog/postgres-date-time-data-types-on-graphql-fd926e86ee87/
The diffrences are shown in PostgreSQL official docs. Please refer the docs for deep digging.
In a nutshell TIMESTAMP WITHOUT TIME ZONE doesn't save any timezone related informations if you give date time with timezone info,it takes date & time only and ignores timezone
For example
When I save this 12:13, 11 June 2021 IST to PostgreSQL TIMESTAMP WITHOUT TIME ZONE will reject the timezone information and saves the date time 12:13,11 June 2021
But the the case of TIMESTAMP WITH TIME ZONE it saves the timezone info in UTC format.
For example
When I save this 12:13, 11 June 2021 IST to PostgreSQL TIMESTAMP WITH TIME ZONE type variable it will interpret this time to UTC value and
stored as shown in below 6:43,11 June 2021 UTC
NB : UTC + 5.30 is IST
During the time conversion time returned by TIMESTAMP WITH TIME ZONE will be stored in UTC format and we can convert it to the required timezone like IST or PST etc.
So the recommented timestamp type in PostgreSQL is TIMESTAMP WITH TIME ZONE or TIMESTAMPZ
Run the following to see diff in pgAdmin:
create table public.testts (tz timestamp with time zone, tnz timestamp without time zone);
insert into public.testts values(now(), now());
select * from public.testts;
If you have similar issues I had of timestamp precision in Angular / Typescript / Node API / PostgreSql environment, hope my complete answer and solution will help you out.