Postgresql TIMESTAMP and TIMESTAMPTZ: is there any difference in data stored? - postgresql

If I inherit a Postgresql database filled with TIMESTAMP type, and feel the need to convert them all to TIMESTAMPTZ type, is this a simple process requiring only:
ALTER TABLE mytable ALTER COLUMN the_time_stamp TYPE TIMESTAMPTZ;
I have such a database, and all the timestamps within it, created in a timezone different to mine (and the server's) behave such that they seem to be stored internally as TIMESTAMPTZ anyway. That is, I have two clients (one write, one read, neither of which is in UTC zone), and when I read, the timestamps written in zone1 are correct when read in zone2, implying (to me) they have been converted and stored in UTC format between the write and read. It makes me wonder if:
Are all timestamps stored in identical format in the database, and the only differences occur in the way input/output is handled, according to the timezone(s) of the connected client(s)?
This seems like such a clear and simple thing to state if it were true, but I cannot find it stated clearly and/or simply...

Internally, both timestamp and timestamp with time zone are stored in the same fashion: an 8-byte integer that is the offset from 2000-01-01 00:00:00 in microseconds.
The difference is the semantics: while timestamp with time zone is stored as offset from midnight 2000-01-01 in UTC, no time zone conversion is made for timestamp.
That means that the table has to be rewritten if you change the data type, because the point of reference changes. If the timezone parameter is set to UTC, the values won't change, but there is no optimization in PostgreSQL that avoids the table rewrite in this case.

Related

PostgreSQL how to convert date with timezone offset to UTC?

I have a PostgreSQL table with date field in the following format
2017-09-07T17:24:33+08:00
and I want to convert it to UTC time.
I've looked around but found no way to do that with this specific time format. What am I missing?
Thanks
timezone definition (https://www.postgresql.org/docs/9.1/functions-datetime.html) : The function timezone(zone, timestamp) is equivalent to the SQL-conforming construct timestamp AT TIME ZONE zone
SELECT timezone('UTC','2017-09-07T17:24:33+08:00');
if selecting from column,
with t as (
SELECT '2017-09-07T17:24:33+08:00' as tm
)
SELECT timezone('UTC',tm::timestamptz) as ts
from t;
I have a PostgreSQL table with date field in the following format: 2017-09-07T17:24:33+08:00
This is incorrect. Per Postgres documentation,
All timezone-aware dates and times are stored internally in UTC. They are converted to local time in the zone specified by the TimeZone configuration parameter before being displayed to the client.
To display a TIMESTAMP WITH TIME ZONE attribute (which, again, is stored internally in UTC) in a specific time zone, you have a few options:
By default, “They are converted to local time in the zone specified by the TimeZone configuration parameter before being displayed to the client.” This can be set in postgresql.conf, using the SQL command SET TIME ZONE, or using a number of other options.
Use the AT TIME ZONE construct.
So, in regards to your original question: You don't have to do anything to your timestamptz for Postgres to store it in UTC. To display your attribute in UTC, you can either change the TimeZone configuration paramter, or use the construct below:
SELECT dt_with_timezone AT TIME ZONE 'UTC' FROM my_table

Understanding Time Zone in PostgreSQL >= 9.3

I am struggling in understanding how PostgreSQL is working with TIMESTAMP and TIME ZONE. I have a Debian Server where timezone is set to UTC running PostgreSQL 9.3 where cluster's postgresql.conf file is also set to UTC. My client is PgAdmin and my computer locale is also set to UTC. Therefore, all timezones are equals, there are no offsets and no DST in storage or display.
I have read many posts over the internet and I cannot understand the following output of my queries.
I assume as true the following affirmations:
Default data-type TIMESTAMP is WITHOUT TIME ZONE;
PostgreSQL stores all TIMESTAMP and TIMESTAMPTZ in UTC;
When using TIMESTAMP type, timezone conversion is not performed;
When using TIMESTAMPTZ type timezone conversion is performed;
POSIX and ISO standards define offset in a opposite fashion;
PostgreSQL display in ISO format but abbreviation are POSIX compliant.
First question: are all those statements right?
Then I checked it out, issuing:
SELECT '2000-01-01 00:00:00' AT TIME ZONE 'UTC-1';
-- Result: 2000-01-01 01:00:00
-- UTC --> LT
-- TIMESTAMP
SELECT '2000-01-01 00:00:00'::TIMESTAMP AT TIME ZONE 'UTC-1';
-- Result: 1999-12-31 23:00:00+00
-- LT --> UTC
-- TIMESTAMPTZ
SELECT '2000-01-01 00:00:00'::TIMESTAMPTZ AT TIME ZONE 'UTC-1';
-- Result: 2000-01-01 01:00:00
-- UTC --> LT
-- TIMESTAMP
This is very confusing to me because:
it looks like default data-type infered from string without cast is TIMESTAMPTZ instead of TIMESTAMP;
conversion is performed in different way when using different data-type;
when specifying data-type with casting ::operator it is the opposite type that is retured.
Second question: How must I interpret the result of those three queries?
Bonus question: I have read there that it is better to always store data with TIMESTAMPTZ. Is this a good practice?
Thank you further for your help!

Transform timestamp to local time for a given timezone during 'COPY .. TO ..'

I have a log table in a PostgreSQL database with an event column of type timestamp without time zone.
Now I have a bash script, which creates a CSV file from the log database:
...
psql .. -c "COPY (SELECT event, ... FROM logtable order by event desc) TO STDOUT WITH CSV" logdb > log.csv
...
This is executed on the cloud server on which the DB is hosted and therefore, the timestamp strings in log.csv are in local time of the timezone of the server.
However, I like to have the timestamp strings to represent the time of my own time zone. So I shall be able to let psql transform the timestamp -> string to a given timezone. How can I achieve this?
First of all, you should use timestamptz instead of timestamp whenever working with multiple times zones. Would avoid the problem completely.
Details:
Ignoring timezones altogether in Rails and PostgreSQL
You can use the AT TIME ZONE construct like #NuLo suggests, it may even work, but not exactly as described.
AT TIME ZONE converts the type timestamp (timestamp without time zone) to timestamptz (timestamp with time zone) and vice versa. The text representation of a timestamptz value depends on the current setting of the time zone in the session in which you run the command. These two timestamptz values are 100 % identical (denote the same point in time):
'2015-09-02 15:55:00+02'::timestamptz
'2015-09-02 14:55:00+01'::timestamptz
But the text representation is not. The display is for different time zones. If you take this string literal and feed it to a timestamp type, the time zone part is just ignored and you end up with different values. Hence, if you run your COPY statement in a session with the same time zone setting as your original timestamp values are for, the suggested operation happens to work.
The clean way, however, is to produce correct timestamp values to begin with by applying AT TIME ZONE twice:
SELECT event AT TIME ZONE 'my_target_tz' AT TIME ZONE 'my_source_tz', ...
FROM logtable
ORDER BY event desc;
'my_target_tz' is "your own time zone" and 'my_source_tz' the time zone of the of the cloud server in the example. To make sure that DST is respected use time zone names, not time zone abbreviations. The documentation:
A time zone abbreviation, for example PST. Such a specification merely
defines a particular offset from UTC, in contrast to full time zone names
which can imply a set of daylight savings transition-date rules as well.
Related:
Accounting for DST in Postgres, when selecting scheduled items
Time zone names with identical properties yield different result when applied to timestamp
Or, much better yet, use timestamptz everywhere and it works correctly automatically.

PostgreSQL timestamptz and timetz functions

I really need to store local date and time (aircraft wheels up date and time) and the utc offset so I can convert to utc to calculate intervals (flight duration). People travel on local time and you need utc for cross time zone computations. I had hoped to use timestamptz but it absolutely does not work for this purpose. It converts everything to a function of the postgres time zone. In my case this is yyyy-dd-mm hh:mi:ss-07.
However, I investigated timetz just to cover all the bases. It stores exactly what I need. It preserves the local time while providing the offset to utc. Except that now I need two columns instead of one to store the information.
My questions are:
Why do the timestamptz and the timetz functions give different results?
Is there a way to make the timestamptz include the local time zone offset rather than the system time zone offset?
Here are the queries that illustrate the difference:
select cast('2015-05-01 11:25:00 america/caracas' as timestamptz)
-- 2015-05-01 08:55:00-07
;
select cast('2015-05-01 11:25:00 america/caracas' as timetz)
-- 11:25:00-04:30
;
I personally find the wording timestamp with time zone confusing when trying to understand PostgreSQL's timestamptz, because it doesn't store any time zone. According to the docs:
All timezone-aware dates and times are stored internally in UTC. They are converted to local time in the zone specified by the TimeZone configuration parameter before being displayed to the client.
Notice, on that page, that the storage characteristics and limits of timestamp and timestamptz are identical.
In my head, to keep things straight, I translate timestamptz to "timestamp in the real world", and plain timestamp to "you probably didn't mean to use this type" (because, so far, I've only found myself needing to store timestamps that are related to the real world.)
So:
Why do the timestamptz and the timetz functions give different results?
It appears to be because PostgreSQL didn't feel they were allowed to make timetz work like timestamptz does:
The type time with time zone is defined by the SQL standard, but the definition exhibits properties which lead to questionable usefulness.
My guess is that some of these "properties" are the ones that they don't like, and you do.
And:
Is there a way to make the timestamptz include the local time zone offset rather than the system time zone offset?
There's no offset being stored for timestamptz values. They're just real-world timestamps. So the way to store the local time zone is the way you've already thought of: store it separately.
create table my_table (
happened timestamptz,
local_time_zone varchar
);
select happened at time zone 'UTC', happened at time zone local_time_zone
from my_table;

How to save and retrieve these timestamps with the specified timezone using Joda Time, JPA, EclipseLink and Postgres

This seems like it would be a common problem so perhaps I am missing something obvious. Using Joda Time I want to persist dates with a timezone. If I wasn't using JPA I would want to do something like this to save and retrieve the timestamps:
create table test_dates
(
zone varchar(50),
ts_with_time_zone timestamp with time zone
);
insert into test_dates values ('UTC', '2012-08-12 12:34:56 UTC');
insert into test_dates values ('MST', '2012-08-12 05:34:56 MST');
select 'UTC in MST', ts_with_time_zone at time zone 'MST'
from test_dates where zone = 'UTC';
How then can I save and retrieve these timestamps with the specified timezone using Joda Time, JPA, EclipseLink and Postgres?
I have seen answers using converters but I am not sure how you would specify or use time zones. Sure I could get the UTC time and convert it myself but that defeats the purpose of having the "with time zone" column. Is this possible?
The ideal solution would be if EclipseLink handled JodaTime similar to hibernate.
You may be able to customize your PostgreSQLPlatform to add support for storing the time-zone through JDBC. Your JDBC driver will have to support the time-zone (most likely through a custom type, or a Calendar API).
EclipseLink does have support for time-zones on Oracle, so it should be possible to extend the PostgreSQLPlatform for time-zones as well if the database and JDBC drivers support it.
You don't save the time zone. You just save the timestamp with the time zone specified in the input string. The time zone will not be saved.
select '2012-08-12 12:34:56 UTC'::timestamp with time zone;
timestamptz
------------------------
2012-08-12 09:34:56-03
select '2012-08-12 05:34:56 MST'::timestamp with time zone;
timestamptz
------------------------
2012-08-12 09:34:56-03
The timezone will be the database timezone.
select now()::timestamp with time zone;
now
-------------------------------
2012-12-10 12:26:16.318484-02