I'm developing my first postgres database schema and I'm running into a little strangeness with the timezones on the timestamptz data type.
After I create the table and use the now() function to input data into the column, I get 2015-11-17 12:10:05.764088-08 as the result. I don't understand what the "764088-08" means for a time zone marker. The postgres documentation says it should look something like this:
'2004-10-19 10:23:54+02'
I understand the second timezone format, but the first one is a big mystery to me. Am I simply missing something? Is the now() function not what you want to use?
The part 05.764088 refers to the current seconds. It just gives finer precision and returns the exact microseconds(in this case 764088) as well. The -08 in the end refers to the timezone:
The time zone offset from UTC, measured in seconds. Positive values
correspond to time zones east of UTC, negative values to zones west of
UTC. (Technically, PostgreSQL uses UT1 because leap seconds are not
handled.)
The function now() is the traditional PostgreSQL equivalent to CURRENT_TIMESTAMP. You can see exact details in the official documentation.
Related
I am creating a table with timestamp column,I am stuggling with time zone settings, I want to specify the time zone on the column,as follows:
create table t1(a date, b timestamp with time zone 'America/Los_Angeles', c timestamp without time zone)
But the grammar is wrong, I would ask how to specify the time zone on the column, than
👉 You do not specify a time zone when defining the column.
CREATE TABLE t1
(
a DATE ,
b TIMESTAMP WITH TIME ZONE ,
c TIMESTAMP WITHOUT TIME ZONE
)
;
You need to read the documentation carefully. Programming by intuition tends to end badly.
The TIMESTAMP WITH TIME ZONE type in Postgres does not save a time zone. The type uses any offset or time zone info supplied with an input to adjust to an offset of zero hours-minutes-seconds from UTC. 👉 Every value in that column is set to an offset of zero. After adjusting to zero offset, the supplied time zone or offset info is discarded by Postgres.
If you care about the original time zone, you need to write that value into a separate column yourself.
In contrast, the TIMESTAMP WITHOUT TIME ZONE type lacks any concept of a time zone or offset from UTC. A column of this type stores simply a date and a time-of-day. So values in this column cannot represent a moment, cannot refer to a specific point on the timeline. If you write noon on the 23rd of last January, we have no way of knowing if you meant noon in Tokyo Japan, noon in Toulouse France, or noon in Toledo Ohio US. Those would be three different moments, several hours apart.
Some other databases share the same behavior as Postgres. But not all. The SQL standard barely touches on the subject of date-time, just mentioning the types but without much detail regarding prescribed behavior. As a consequence, date-time behavior varies widely across database engines.
I should mention that some tools have an anti-feature where they inject a default time zone, used to adjust a value stored in UTC to that zone. pgAdmin is, unfortunately, one such tool. While well-intentioned as a convenience to the user, this behavior creates the illusion of a time zone having been stored and retrieved. I would rather all tools “tell the truth”, and report retrieved values with an offset of zero. A workaround is to set the current default time zone of your database session to UTC.
All this has been covered many times already here on Stack Overflow, and also on the sister site https://dba.stackexchange.com/. Search to learn more.
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.
My console is PST.
Database server and times stored are GMT.
I'm having to run queries like so:
SELECT x,y,z
FROM tbl_msg
WHERE (msg_datetime AT TIME ZONE 'BST') BETWEEN '2016-11-21'::date and '2016-11-22'::date;
Indexing 101 says that performing this operation on msg_datetime will now avoid the index and this is what I'm seeing.
So I need advice with an indexing solution for this.
Can I index this timezone? or alter this query so that it queries these times in BST, converted to GMT?
You should have msg_datetime column of type timestamp with time zone (or shorter alias timestamptz) with normal index.
Then, to get data for these 2 days, you should:
set timezone 'Europe/London'; -- once, on connection start
SELECT x,y,z
FROM tbl_msg
WHERE
msg_datetime>='2016-11-21 00:00:00'
and
msg_datetime<'2016-11-23 00:00:00';
You should not use ordinary timestamp, as it stores literal date and hour without information about which timezone it actually meant. A timestamp with time zone type will automatically convert your client's configured time to internal representation (which is in UTC) and back. You can also express timestamptz from non-default timezone using for example '2016-11-23 00:00:00 Asia/Tokyo'.
Also you should not use BST - because you'd need to use GMT on winter and remember when to use which. You should use 'Europe/London' or other "city" timezones (list), which are right both in summer and in winter.
We have an application that fetches data from a source and that source present the data with a timestamp in UTC. When our application saves that data to Postgres, it stores that timestamp in a timestamp column without time zone. The default on postgres in our shop is set to our local time, Mountain Time. So that means, I think, that postgres assumes that timestamp is mountain time. How can I query that column so that my result set thinks it's UTC and not the local time zone?
More cleary stated, I need to perform some offsets on that timestamp (moving it to, say EST) and so the math of doing that is different if the resultset thinks it's UTC than my local time
The Answer by Kouber Saparev is mostly correct, though incorrect about storing a time zone.
Wrong data type in Postgres
a timestamp in UTC. When our application saves that data to Postgres, it stores that timestamp in a timestamp column without time zone.
As noted in his Answer, you are using the wrong data type in your Postgres database. When tracking moments, you must use a column of type TIMESTAMP WITH TIME ZONE. When supplying an input during an insert or update, any accompanying info about time zone or offset-from-UTC is used to adjust into UTC. The accompanying zone/offset is then discarded. If you need to remember the original zone/offset, you will need to define a second column and store that info there yourself.
The other type in Postgres, and the SQL standard, is TIMESTAMP WITHOUT TIME ZONE. This type purposely lacks any concept of time zone or offset-from-UTC. So this type cannot represent moments, cannot store points on the timeline. It stores values that represent potential moments along a range of about 26-27 hours, the range of various time zones around the globe. Use this type only when you mean a date with time-of-day everywhere or anywhere, but not specifically somewhere. Also used when you mean appointments far enough out in the future that we run the risk of politicians changing the offset used in any of the time zones we care about.
Always specify time zone
default on postgres in our shop is set to our local time, Mountain Time
Never depend on the current default time zone of your host OS, the database server, or your tools such as the Java Virtual Machine. Always specify the desired/expected time zone in your code.
Tip: Generally best to work in UTC for data storage, data exchange, and most of your business logic. Adjust from UTC to a time zone only for presentation to the user or where business rules require.
As explained above, Postgres always stores date-time values either in UTC or with no zone/offset at all. Beware: Tools used between you and Postgres may apply a time zone to the UTC value retrieved from the database. While well-intentioned, this anti-feature creates the illusion that the time zone was stored when in fact only UTC was stored in TIMESTAMP WITH TIME ZONE or no zone/offset at all in TIMESTAMP WITHOUT TIME ZONE.
Be aware that any zone information accompanying input to a column of TIMESTAMP WITHOUT TIME ZONE is simply ignored, the date and time-of-day taken as-is and stored.
I need to perform some offsets on that timestamp (moving it to, say EST)
Generally best to use your database just for storage, query, and retrieval of data. For massaging the data like adjusting time zone, do such work in your application. For example, in Java use the industry-leading java.time classes, in .NET the Noda Time project (a port of the predecessor of java.time, the Joda-Time project).
Example code in Java using JDBC 4.2 or later.
LocalDateTime
For a value in a column of TIMESTAMP WITHOUT TIME ZONE we use the corresponding type in Java, LocalDateTime, lacking any concept of time zone or offset-from-UTC.
LocalDateTime ldt = myResultSet.getObject( … , LocalDateTime.class ) ; // Retrieve value from database.
String output = ldt.toString() ; // Generate text representing this date-with-time value in standard ISO 8601 format.
2018-01-23T01:23:45.123
If you know for certain that this date and time was meant for UTC but was incorrectly stored without any zone/offset info, you can apply a zone or offset to repair the damage.
OffsetDateTime odt = ldt.atOffset( ZoneOffset.UTC ); // Apply an offset-from-UTC to a `LocalDateTime` lacking such information. Determines a moment.
OffsetDateTime
For a value in a column of TIMESTAMP WITH TIME ZONE we use the corresponding type in Java, OffsetDateTime (or Instant), representing a moment in UTC.
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ; // Retrieve value from database.
String output = odt.toString() ; // Generate text representing this date-with-time value in standard ISO 8601 format. A `Z` on the end indicates UTC, pronounced “Zulu”.
2018-01-23T01:23:45.123Z
ZonedDateTime
To see that OffsetDateTime value set in UTC through the lens of the wall-clock time used by the people of regions within the mid-west of North America, specify a time zone such as America/Edmonton or America/Denver.
Specify a proper time zone name in the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 2-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!).
ZoneId z = ZoneId.of( "America/Denver" ) ;
ZonedDateTime zdt = odt.atZoneSameInstant( z ) ;
See this code run live at IdeOne.com. We see the same moment but with a different wall-clock time.
2018-01-22T18:23:45.123-07:00[America/Denver]
Beware of tools & middleware injecting a time zone
Unfortunately, many tools and middleware will volunteer to apply some default time zone to a moment retrieved from the database. While well-intentioned, this creates the illusion of the zone having been a part of the stored data when in fact the time zone was added after storage, upon retrieval. This anti-feature creates much confusion. I wish all the tools were clear and truthful by reporting the moment in UTC, as it was stored.
If you use Java, with JDBC 4.2 and later, you can exchange java.time (JSR 310) (tutorial) objects with the database and avoid this time zone injection.
There are two data types handling timestamps in PostgreSQL - timestamp, and timestamptz (timestamp with time zone). The latter stores the time zone along with the timestamp itself.
If you are using just a timestamp without time zone, then there is no way for the result set to think whether the timestamp is UTC or not. It is just a timestamp. It is up to the client application to interpret it and give it some time zone meaning.
On the contrary, if you use timestamptz, then PostgreSQL knows the time zone of that timestamp, and then it can calculate time zone offsets properly for you.
db=# select now();
now
-------------------------------
2014-12-04 19:27:06.044703+02
(1 row)
db=# select timezone('est', now());
timezone
----------------------------
2014-12-04 12:27:06.044703
(1 row)
So, back on the problem posed. You need to make sure that first the data is imported properly and then - when needed, it is returned and displayed properly to the end user. You have two options:
Continue using timestamp
In that case both the writing app and the reading app need to know that all the timestamps in the database are UTC and calculate offsets accordingly.
Switch to timestamptz
Then the only thing that the apps need to know is their own time zone, they just have to declare it after connecting to PostgreSQL and leave the rest to the database.
For example, let's connect as a writing app and declare our time zone as UTC.
db=# create table x (data timestamptz);
CREATE TABLE
db=# set timezone='utc';
SET
db=# insert into x values (now());
INSERT 0 1
db=# select * from x;
data
-------------------------------
2014-12-04 20:02:08.692329+00
(1 row)
Now, let's say a reading app connects and is in the EST time zone.
db=# set timezone='est';
SET
db=# select * from x;
data
-------------------------------
2014-12-04 15:02:08.692329-05
(1 row)
Changing the client time zone setting changes the way all the timestamps are returned, but that's the case only if you use timestamptz - timestamp with time zone. If you cannot switch to this data type, then the application will have to take care of all this magic.
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.