TIMESTAMP vs TIMESTAMPTZ correct value when taking time difference - postgresql

For the query:
SELECT '2018-03-01'::TIMESTAMP - '2018-09-01'::TIMESTAMP,
'2018-03-01'::TIMESTAMPTZ - '2018-09-01'::TIMESTAMPTZ;
the values -184 days for TIMESTAMP and -183 days -23:00:00 for TIMESTAMPTZ is returned.
To me, -184 days "seems" correct but I was always told to use TIMESTAMPTZ. Which is the correct answer?

Answer posted as complement to comment discussion under OP's question.
Data type timestamptz is actually timestamp with time zone, while timestamp is timestamp without time zone.
The result that you get from substracting these specific dates like below is correct with 1 hour difference
SELECT '2018-03-01'::TIMESTAMPTZ - '2018-09-01'::TIMESTAMPTZ;
this is because 2018-03-01::timestamptz has +01 whereas 2018-09-01 has +02.
See it for yourself with this query
SELECT '2018-03-01'::TIMESTAMPTZ, '2018-09-01'::TIMESTAMPTZ
1 hour difference most likely comes from daylight savings and changing timezone over time.
In general I would always choose to include time zones within my system to properly handle time differences across the world and avoid issues that may arise from not having this. Client and server may have different timezones which can lead to problems.

Related

Why do I get different values in postgresql when using UCT values vs timezone names?

If I run this query:
SELECT ('2021-11-02 08:00:00+00' AT TIME ZONE 'UCT') at TIME ZONE 'UCT+2';
It gives this result:
2021-11-02 10:00:00+00
Which seems correct to me...
However, my timezone data comes in string format, for example: "Africa/Johannesburg", which I know is on the UCT+2 timezone.
But running this query:
SELECT ('2021-11-02 08:00:00+00' AT TIME ZONE 'UCT') at TIME ZONE 'Africa/Johannesburg';
Gives this result:
2021-11-02 06:00:00+00
Which is the opposite of what I expect to happen. I know I can write code to convert the strings to a UCT value but I don't understand why the string values seems to do the opposite of using UCT values. Could someone please explain why this happens?
Wow, there's lots to unpack here.
The main thing is that (to mis-quote Inigo Montoya) I do not think that UTC+2 means what you think it means.
One important thing here is the precise format.
You say you get this result:
2021-11-02 06:00:00+00
But What I get is:
2021-11-02 06:00:00
And that's really important because what I get doesn't include that +00. And that's because it's still 08:00 in the +00 time zone.
The AT TIME ZONE syntax says something like "What would a local at this place see the time being at this specific UTC time?". And someone in Johannesburg would see it as 10:00. Someone in London (in winter time) would see it as 06:00. But it's the same instant in time.
So the output of AT TIME ZONE doesn't usually have that time zone offset from UTC (+00) included in it.
Your conversions with two AT TIME ZONE clauses are redundant. Because it knows it's at that time zone, and hence what the UCT time is. You'll get the same result with one AT TIME ZONE clause (try it).
The reason why you get different answers appears to be because PostgreSQL does not interpret UCT+2 as the TimeZone that is 2 hours ahead of UCT. It interprets that as you specifying a POSIX time zone definition, which could also include daylight savings time rules etc.
This page: https://www.postgresql.org/docs/current/datatype-datetime.html says PostgreSQL will accept time zones specified in 3 ways:
Full name - e.g. 'Africa/Johannesburg'
Abbreviation - e.g. 'SAST'
Posix format, e.g. 'CET-1CEST,M3.5.0,M10.5.0/3' (This is, apparently, Paris)
Actually, it doesn't include the Posix format example, that's on this page: https://www.postgresql.org/docs/current/datetime-posix-timezone-specs.html
Basically, the posix format is a way of specifying the time zone rules.
Something like
[abbreviation][offset][daylight savings abbrev][DST offset][dst rules]
And so saying 'UCT+2' is probably "bad form" because you're trying to change the meaning of the abbreviation 'UTC' which has a standard usage.
You can verify that it isn't using the "UCT" part of your time zone to refer to the actual UCT time zone. If you do this:
SELECT ('2021-11-02 08:00:00+00' AT TIME ZONE 'BOB')
PostgreSQL will say it doesn't recognise time zone "BOB".
But if you do
SELECT ('2021-11-02 08:00:00+00' AT TIME ZONE 'BOB+2')
It'll happily give you 06:00:00. In fact, if you like, you can stick (almost) anything in there instead of "UCT". It doesn't get used in the calculation.
Note that the posix page I linked says
POSIX time zone specifications are inadequate to deal with the
complexity of real-world time zone history, but there are sometimes
reasons to use them.
Finally, 'UCT' is perfectly valid but it's doing my head in. I've only seen it as 'UTC', so TIL.

PostgreSQL handling of full time zones for timetz data type

I'm trying to model opening hours of shops in a PostgreSQL table. They should be time zone aware but independent of daylight time savings. In other words 9am to 5pm should be the same in summer and winter time.
My approach is to use two timetz columns to store the opening and closing hours. However, I can't insert data using the full time zone specifications, e.g. '04:05:06 America/New_York'. It seems to work only for timestamptz columns with a full date, e.g. '2003-04-12 04:05:06 America/New_York'.
Am I missing something or is there another way to specify times that depend on a time zone and ignore daylight time savings?
Avoid the data type time with time zone, as you see, it is problematic.
The documentation states:
The type time with time zone is defined by the SQL standard, but the definition exhibits properties which lead to questionable usefulness.
Maybe you would be better off with two fields, a time without time zone and a time zone field.

PostgreSQL/JDBC and TIMESTAMP vs. TIMESTAMPTZ

I've been going through a lot of pain dealing with Timestamps lately with JPA. I have found that a lot of my issues have been cleared up by using TIMESTAMPTZ for my fields instead of TIMESTAMP. My server is in UTC while my JVM is in PST. It seems almost impossible with JPA to normalize on UTC values in the database when using TIMESTAMP WITHOUT TIMEZONE.
For me I use these fields for stuff like "when was the user created", "when did they last use their device", "when was the last time they got an alert", etc. These are typically events so they are instance in time sorts of values. And because they will now by TIMESTAMPTZ I can always query them for a particular zone if I don't want them UTC.
So my question is, for a Java/JPA/PostgreSQL server, when WOULD I want to use TIMESTAMP over TIMESTAMPTZ? What are the use cases for it? Right now I have a hard time seeing why I'd ever want to use TIMESTAMP and because of that I'm concerned that I'm not grasping its value.
Generally use TIMESTAMPTZ
Here's advice from David E. Wheeler, a Postgres expert, in a blog post whose title says it all:Always Use TIMESTAMP WITH TIME ZONE (TIMESTAMPTZ)
If you are tracking actual moments, specific points on the timeline, use TIMESTAMP WITH TIME ZONE.
One Exception: Partitioning
Wheeler’s sole exception is when partitioning on timestamps, because of technical limitations. A rare exception for most of us.
For information about partitioning, see doc and see the Wiki.
Misnomer
The data types names timestamp with time zone and timestamp without time zone are misnomers. In both cases the date-time value is stored in UTC (no time zone offset). Read that previous sentence again. UTC, always. The "with time zone" phrase means "with attention paid to time zone", not "store the time zone alongside this value". The difference between the types is whether any time zone should be applied either during storage (INSERT or UPDATE) or retrieval (SELECT query). (This behavior is described for Postgres -- Other databases vary widely in this regard.)
More precisely, one should say that TIMESTAMP WITHOUT TIME ZONE stores date-time values with no time zone. But without any time frame reference, anyone looking at that data would have to assume (hope, pray?) that the values are UTC. But again, moot as you should almost never use this type.
Read the doc carefully, and experiment a bit to clarify your understanding.
Unzoned
If you want to store the general idea of a possible time rather than a specific moment, use the other type, TIMESTAMP WITHOUT TIME ZONE.
For example, Christmas starts this year at the first moment of December 25th, 2017. That would be 2017-12-25T
00:00:00 with no indicator of time zone nor offset-from-UTC. This value is only a vague idea about possible moments. It has no meaning until we apply a time zone (or offset). So we store this using TIMESTAMP WITHOUT TIME ZONE.
The elves staffing Santa’s Special Events Logistics Department apply the time zones as part of their planning process. The earliest time zone is currently Pacific/Kiribati, 14 hours ahead of UTC. The elves schedule Santa’s first arrival there. The elves schedule a flight plan taking the reindeer on to other time zones where midnight comes shortly after, such as Pacific/Auckland. They continue going westward as each zone’s midnight arrives. Hours later in Asia/Kolkata, still later in Europe/Paris, still more hours later in America/Montreal and so on.
Each of these specific delivery moments would be recorded by the elves using WITH TIME ZONE, while that general idea of Christmas would by stored as WITHOUT TIME ZONE.
Another use in business apps for WITHOUT TIME ZONE is scheduling appointments farther out than several weeks. Politicians around the world have an inexplicable predilection for messing with the clock and redefining time zone rules. They join Daylight Saving Time (DST), leave DST, start DST on a different date, or end DST on a different date, or shift their clocks by 15 minutes or half-hour. All of these have been done in last several years by Turkey, United States, Russia, Venezuela, and others.
The politicians often make these changes with little forewarning. So if you are scheduling a dental appointment for six months out at 13:00, that should probably be stored as TIMESTAMP WITHOUT TIME ZONE or otherwise the politicians may effectively be changing you appointment to noon, or 2 PM, or 13:30.
You could use it to represent what Joda-Time and the new Java 8 time APIs call a LocalDateTime. A LocalDateTime doesn't represent a precise point on the timeline. It's just a set of fields, from year to nanoseconds. It is "a description of the date, as used for birthdays, combined with the local time as seen on a wall clock".
You could use it to represent, for example, the fact that your precise birth date is 1975-07-19 at 6 PM. Or that, all across the world, the next new year is celebrated on 2015-01-01 at 00:00.
To represent precise moments, like the moment Armstrong walked on the moon, a timestamp with timezone is indeed more appropriate. Regardless of the timezone of the JVM and the timezone of the database, it should return you the correct moment.
Update for the answers above: partitioning is no longer an exceptional case in PG11 thanks to pruning.
https://www.postgresql.org/docs/11/ddl-partitioning.html#DDL-PARTITION-PRUNING
Personally successfully tested queries against PG11 AWS RDS. Also the official PG wiki states the use of timestamp without timezone is a bad idea:
https://wiki.postgresql.org/wiki/Don%27t_Do_This#Don.27t_use_timestamp_.28without_time_zone.29_to_store_UTC_times
With the Java 8 date & time API I wouldn't blindly jump into a timestamptz camp.
If you map timestamp <=> LocalDateTime you always get the same value regardless default Java application timezone. Regardless how many calls TimeZone.setDefault(TimeZone.getTimeZone("TZ")) mixing different TZ you put in between SELECT/INSERT you will get the same LocalDateTime in Java at any time and date/time components will be the same as in Postgresql TO_CHAR(ts, 'YYYY-MM-DD HH24:MI:SS').
If you map timestamptz <=> LocalDateTime Postgresql JDBC driver (supporting JDBC 4.2 spec) converts LocalDateTime to UTC using default Java timezone when saving value to DB. If you save it in one default TZ and read in another you get different "local" results.
Airplane departure time is local to an airport. If you don't need to compare departure time between different cities timestamptz & UTC doesn't make sense, you just print exact city local time in a ticket. With timestamp it is possible to keep date/time as is, avoiding double TZ correction due to Java app default TZ + city specific TZ (business logic).
timestamptz is useful when you heavily convert TZ in SQL. With only timestamp you write:
date_trunc('day', x.datecol AT TIME ZONE 'UTC' AT TIME ZONE x.timezone)
AT TIME ZONE x.timezone AT TIME ZONE 'UTC'
while with timestamptz there is no need to mention that time is in UTC (if you follow such convention, probably you should xD):
date_trunc('day', x.datecol AT TIME ZONE x.timezone)
AT TIME ZONE x.timezone
Operator AT TIME ZONE is overloaded:
timestamp AT TIME ZONE 'X' => timestamptz
timestamptz AT TIME ZONE 'X' => timestamp
Postgresql JDBC + Java 8 date&time API spec.

converting db2 datestamp to Unix timestamp

How can i convert 2012-04-12 00:00:00 to a unix timestamp in DB2. is there any inbuild function available in sql s
Thank you.
Using the DAYS and MIDNIGHT_SECONDS is much more precise than TIMESTAMPDIFF:
SELECT
86400*(DAYS(CURRENT TIMESTAMP - CURRENT TIMEZONE)-DAYS('1970-01-01'))
+ MIDNIGHT_SECONDS(CURRENT TIMESTAMP - CURRENT TIMEZONE)
"UNIX_TIMESTAMP"
FROM SYSIBM.SYSDUMMY1
By Unix timestamp I assume you mean the number of seconds (or whatever) since 1970-01-01 00:00:00 UTC.
There is no built in functionality for this in DB2 (as of V6R1).
You're also up against the following issues:
All timestamps in DB2 are 'local time' - they contain no timezone information, and all CURRENT_TIMESTAMP writes are based on what time the requesting system thinks it is, not the host.
Daylight savings time changes frequently. You would need to add overhead to manage this for your conversion.
The TIMESTAMPDIFF function returns an estimate, not an exact value. You could probably survive for the years/months durations, over sufficient differences, but days aren't likely to cut it.
Timestamp arithmetic is imprecise (among other things, months are assumed to be always 30 days in length...)
Your best bet will be to start using DAYS (which returns the number of days since 0001-01-01). Keep in mind you better do everything in UTC, because it will not take DST into account.

Selecting records between two timestamps

I am converting an Unix script with a SQL transact command to a PostgreSQL command.
I have a table with records that have a field last_update_time(xtime) and I want to select every record in the table that has been updated within a selected period.
Say, the current time it 05/01/2012 10:00:00 and the selected time is 04/01/2012 23:55:00. How do I select all the records from a table that have been updated between these dates. I have converted the 2 times to seconds in the Unix script prior to issuing the psql command, and have calculated the interval in seconds between the 2 periods.
I thought something like
SELECT A,B,C FROM table
WHERE xtime BETWEEN now() - interval '$selectedtimeParm(in secs)' AND now();
I am having trouble evaluating the Parm for the selectedtimeParm - it doesn't resolve properly.
Editor's note: I did not change the inaccurate use of the terms period, time frame, time and date for the datetime type timestamp because I discuss that in my answer.
What's wrong with:
SELECT a,b,c
FROM table
WHERE xtime BETWEEN '2012-04-01 23:55:00'::timestamp
AND now()::timestamp;
If you want to operate with a count of seconds as interval:
...
WHERE xtime BETWEEN now()::timestamp - (interval '1s') * $selectedtimeParm
AND now()::timestamp;
Note the standard ISO 8601 date format YYYY-MM-DD h24:mi:ss which is unambiguous with any locale or DateStyle setting.
The first value for the BETWEEN construct must be the smaller one. If you don't know which value is smaller use BETWEEN SYMMETRIC instead.
In your question you refer to the datetime type timestamp as "date", "time" and "period". In the title you used the term "time frames", which I changed to "timestamps". All of these terms are wrong. Freely interchanging them makes the question even harder to understand.
That, and the fact that you only tagged the question psql (the problem hardly concerns the command line terminal) might help to explain why nobody answered for days. Normally, it's a matter of minutes around here. I had a hard time understanding your question.
Understand the data types date, interval, time and timestamp - with or without time zone. Start by reading the chapter "Date/Time Types" in the manual.
Error message would have gone a long way, too.
For anyone who is looking for the fix to this. You need to remove timestamp from the where clause and use BETWEEN!
TABLENAME.COL-NAME-FOR-TIMESTAMP BETWEEN '2020-01-29 04:18:00-06' AND CURRENT_TIMESTAMP