First, I have read a lot about date types in Postgres using timezone or not. It seems that managing dates (timestamps) with timezone is the best to do in 99% of cases.
Currently I have built a sync engine using a Rails app set to UTC time by default (very important) and MariaDB managing dates (so UTC dates are written in the database but without seeing anything except the date: 2017-04-19 17:45:09). The devices that syncs to the server engine use local SQLite database with dates in UTC too. So for the same document, I can see the same date for a created_at field in the SQLite field and in the MariaDB field. It's working well.
Note that users never have to set a custom timezone in the server app and in the device apps. I want all UTC.
As I plan to migrate to PostgreSQL for many reasons, I'm quite sure I need to use Timestamp without timezone too, but I have some doubts after all readings.
What are your suggestions about this use case ?
EDIT:
So I made 3 tests using pgloader:
Test1: MariaDB => PostgreSQL using computer default timezone
Dates are the same but as you can see, sometimes there is +01, sometimes +02 ???
Test2: MariaDB => PostgreSQL using timezone after setting timezone to UTC in Postgres
Here there is +00, logical for UTC, but my dates were changed.
Test3: MariaDB => PostgreSQL using timestamp WITHOUT timezone
All the same there.
"With Timezone" simply tells postgres to convert from local time to UTC before storing the timestamp, and to convert back to local time when querying. The underlying data is stored in UTC and no timezone information is stored in the actual data, it's simply a flag the DB uses to determine how to convert the data for human use.
If your client connection is setting the PG timezone to UTC upon DB connection, there will be pretty much no difference. Conventional wisdom says to always use timestamps with timezone, as it'll make queries in other systems (postgresql client) a lot more flexible and easy to use in the future.
try with one of these in your config file
SET timezone= 'UTC'
SET client_timezone TO '+00:00'
my full settings were:
LOAD DATABASE
FROM mysql://usr#localhost/kk
INTO postgresql://usr:pwd#localhost/kk
WITH include drop,
create tables,
create indexes,
reset sequences,
workers = 8, concurrency = 1,
multiple readers per thread, rows per range = 50000
SET PostgreSQL PARAMETERS
maintenance_work_mem to '512MB',
work_mem to '64MB',
timezone to 'UTC'
SET MySQL PARAMETERS
net_read_timeout = '220',
net_write_timeout = '220'
SET timezone= 'UTC'
SET client_timezone TO '+00:00'
ALTER SCHEMA 'kk' RENAME TO 'public'
;
Related
The entities in my application have a lot of Instant fields. I don't care about time zones, everything is in UTC. I am using Postgresql 13.2. Hibernate 5 maps those fields in the CREATE TABLE statements to timestamp, which Postgresql interprets as "timestamp without time zone". This is the desired behavior.
However, after upgrading the app to use Hibernate 6, the fields in the CREATE TABLE statements are now "timestamp(6) with time zone". Liquibase then generates a diff with lot of false changes.
Is there a way to tell Hibernate 6 to continue mapping Instant fields to timestamp (without time zone)?
I tried setting the timezone of the postgresql server to "Etc/UTC", it did not help. Neither did setting
spring.jpa.properties.hibernate.jdbc.time_zone=UTC
in application.properties.
First: the correct data type to use to represent a timestamp in UTC in Postgres is timestamp with time zone, which does not actually store any time zone information, it just stores timestamps normalized to UTC. From the Postgres docs:
For timestamp with time zone, the internally stored value is always in UTC (Universal Coordinated Time...). 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.
On the other hand, the SQL type timestamp is more like a LocalDateTime in Java, it is not by nature a UTC datetime.
Second: if you don't like the SQL column type that Hibernate uses by default, then you can of course change it, either in the JPA standard way:
#Column(columnDefinition="timestamp")
or in the IMO much better native Hibernate way:
#JdbcTypeCode(TIMESTAMP)
or:
#JdbcType(TimestampJdbcType.class)
There is even an (incubating) global setting defined by AvailableSettings.PREFERRED_INSTANT_JDBC_TYPE:
hibernate.type.preferred_instant_jdbc_type=TIMESTAMP
However, I do not recommend you use any of these settings, since, as I said, the correct SQL type to use on Postgres is the one Hibernate uses by default.
Does anyone know what this actually does?
There appears to be two different timezones, a Session timezone and a Database time zone. The former is clear, it causes timezones with timestamp to be converted from a text representation in psql to UTC.
But what does the Database time zone do?
It can be changed with
ALTER DATABASE database_name SET TIMEZONE='zone';
Is Database Timezone just some sort of default for the Session Timezone? Or does it affect how timestamps are stored? My understanding is that the psql session timezone defaults to the client computer timezone.
There is also the question of the 99.9% of usages that do not use psql. Say JDBC. When and how are offsets added. But that is not this question.
Timezones are tricky, and never well documented.
This is covered in the documentation. In particular:
For timestamp with time zone, the internally stored value is
always in UTC […].
Admittedly mentioning UTC is a bit misleading, I'd prefer to say that a timestamptz represents an instant, an exact fixed point in time, without regard to calendar or location (timezone). It's just an offset since an epoch. Very much like a Date in Java or JavaScript, or better: like an Instant (Java, JavaScript).
[For literal timestamptz values, 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.
The TimeZone configuration
parameter
can be set in the file postgresql.conf, or in any of the other
standard ways described in Chapter
20.
TimeZone (string):
Sets the time zone for displaying and interpreting time stamps. The
built-in default is GMT, but that is typically overridden in
postgresql.conf; initdb will install a setting there corresponding
to its system environment.
So the timezone setting affects conversions of timestamps from and to text, both via casting and the via to_char/to_timestamp function calls, in SQL execution.
It does not affect storage.
Looking at chapter 20, in particular Setting Parameters, shows that there are many more than just two places to set the timezone configuration variable.
The most fundamental way to set these parameters is to edit the file postgresql.conf. […]
Parameters set in this way provide default values for the cluster. The
settings seen by active sessions will be these values unless they are
overridden. The following sections describe ways in which the
administrator or user can override these defaults.
[The file postgresql.auto.conf] is intended to be edited
automatically, not manually. This file holds settings provided through
the ALTER SYSTEM
command.
[…] Settings in postgresql.auto.conf override those in
postgresql.conf.
[T]here are two commands that allow setting of defaults on a
per-database or per-role basis:
The ALTER DATABASE command
allows global settings to be overridden on a per-database basis.
The ALTER ROLE command
allows both global and per-database settings to be overridden with
user-specific values.
Values set with ALTER DATABASE and ALTER ROLE are applied only when
starting a fresh database session. They override values obtained from
the configuration files or server command line, and constitute
defaults for the rest of the session.
Once a client is connected to the database, PostgreSQL provides […]
SQL commands […] to interact with session-local configuration
settings:
The SET command allows
modification of the current value of those parameters that can be set
locally to a session; it has no effect on other sessions.
The documentation of the SET command details how this may be limited to the current session, the current transaction, or the currently executing function.
A client would have to explicitly set this to use the client computer's system timezone for the session. JDBC does this, for example.
(another post answering exactly the questions from the OP, taken from the comments of my other answer)
What does the Database time zone do?
It's a configuration setting that is applied when starting a fresh session (connection), overrides the server defaults, and constitutes the default for the rest of the session.
Is Database Timezone just some sort of default for the Session Timezone?"
Yes.
Or does it affect how timestamps are stored?
No. timestamp with timezone represents an instant, a fixed point in time, without regard to calendar or location (timezone). It's just an offset since an epoch.
My understanding is that the psql session timezone defaults to the client computer timezone.
No: only when your client explicitly sets it - like JDBC does.
What happens for JDBC, for cols timestamptz, values Date vs String?
I've not used it myself and don't know how it does conversion of raw serialisation to java objects. But I would presume that when you query timestamptz as a Date, the timezone setting doesn't matter. When you query timestamptz as a String, the timezone would apply ("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.").
When you query timestamp as a String, it would not apply. And you shouldn't query a timestamp as a Date (JDBC might assume UTC? Postgres does not!)
How do you show the current Database Timezone?
See How do you view the configuration settings of a PostgreSQL database? or Query for all the Postgres configuration parameters‘ current values?
Manual ALTER DATABASE part.
The remaining forms change the session default for a run-time
configuration variable for a PostgreSQL database. Whenever a new
session is subsequently started in that database, the specified value
becomes the session default value. The database-specific default
overrides whatever setting is present in postgresql.conf or has been
received from the postgres command line. Only the database owner or a
superuser can change the session defaults for a database. Certain
variables cannot be set this way, or can only be set by a superuser.
meaning if the new connection not explicitly override the value, then the new value will become session default.
Like ALTER DATABASE test15 SET TIMEZONE='Singapore'; if you are still in session, you will still have the previous timezone value, if you quit the session, reenter agagin, then the new TIMEZONE is Singapore.
SELECT
name,
category,
short_desc,
extra_desc,
context
FROM
pg_settings
WHERE
name = 'TimeZone' gx
return.
-[ RECORD 1 ]---------------------------------------------------------------
name | TimeZone
category | Client Connection Defaults / Locale and Formatting
short_desc | Sets the time zone for displaying and interpreting time stamps.
extra_desc |
context | user
The context is user level, which means any connection can use set command change it.
So when query the interval value like timestamptz column > now(). the return values will become different for connections in differenent timezone.
psql session timezone defaults to the client computer timezone.
if you not explicitly set it, the default will be database level timezone parameter value.
There is also the question of the 99.9% of usages that do not use
psql. Say JDBC. When and how are offsets added. But that is not this
question.
psql is same as JDBC, both are client. they can change timezone use set command, if the connection is superuser or owner, then they can change database default, which means other connection will follow the new default.
However each connection can still use set command to change the timezone paramter.
does it affect how timestamps are stored?
If your timestamp from string literal,then no. if your timezone from timestamptz then yes. First query result is the same, second is not.
begin;
set time zone 'Singapore';
select '2022-01-01 11:30'::timestamp;
reset time zone;
select '2022-01-01 11:30'::timestamp;
commit;
begin;
set time zone 'Singapore';
select now()::timestamp;
reset time zone; --default not 'Singapore'
select now()::timestamp;
commit;
I have a table that I am using to store iso dates with timezones. I realize that dates should "always" be stored as utc but I have an exception to that rule. The timestamps aren't in any way related to the server they are running on. I want to be able to store an iso date like this:
2016-03-06T01:15:52-06:00
And regardless of the time zone of the server or anything else I want the timestamp returned as:
2016-03-06T01:15:52-06:00
Currently if I insert an iso date it automatically converts it to whatever the server timezone is. My above date gets converted to:
2016-03-06 07:15:52+00 (server is utc)
The only thing I can think of is storing the timezone offset in a separate column, storing my date as utc and then converting using the offset column, horribly messy. Surely there is a way to store my date in one column and get it out the way it was originally created?
Your proposed solution is correct. Or more precisely, it is one of several correct implementations. Any of the following would work:
Store the UTC timestamp in one field, store the offset in another.
Store the local timestamp in one field, store the offset in another.
Store the local date in one field, and store a time with time zone in another. (though time with time zone is generally discouraged...)
Store the UTC timestamps in one field and the local timestamp in another.
The easiest by far is the first one, which you already proposed.
I'd avoid against storing timestamps in text fields, as they tend not to be very efficiently searchable.
Also note - if you're coming from a SQL Server background, you might recall its datetimeoffset type, which stores the local datetime and offset in the field, and uses the UTC equivalent during indexing. It's common to think that Postgres and MySQL's timestamp with time zone would have the same behavior, but they don't. They simply use the session time zone to convert to/from UTC. SQL Server has no concept of a session time zone, and thus the discrepancy.
Be sure to read this part of the Postgres docs.
PostgreSQL 9.3 / postgresql-9.3-1100-jdbc41.jar
I have a table with a column of type timestamp without time zone, this generates my Object with the applicable java.util.Timestamp property.
What I'm seeing, during insert, is jOOQ's binding process converting a java.util.Timestamp into a date with local timezone offset.
eg for a unix timestamp 1421109419 (13 Jan 2015 00:36:59 GMT) the property is set with new Timestamp(1421109419 * 1000).
from the jOOQ logger I see:
2015-01-13 14:14:31,482 DEBUG [http-bio-8002-exec-4] org.jooq.tools.LoggerListener#debug:255 - -> with bind values : insert into "foo"."bar" ("start_date") values (timestamp '2015-01-13 13:36:59.0') returning "foo"."bar"."id"
2015-01-13 14:14:31,483 TRACE [http-bio-8002-exec-4] org.jooq.impl.DefaultBinding#trace:179 - Binding variable 3 : 2015-01-13 13:36:59.0 (class java.sql.Timestamp)
and sure enough in the record is the value "2015-01-13 13:36:59".
The software is running on a machine in NZDT which explains the +13 offset.
Given the time is being supplied in a TimeZone agnostic container (Timestamp) I would have expected that to be honoured when creating the insert statement.
How can I have jOOQ create timestamps NOT in local time?
Unfortunately you have a few things working against you:
The PostgreSQL JDBC driver sets the timezone to your JVM timezone in the Postgres session. So even if your Database Server is running in UTC a TIMESTAMP field will be inserted using the time zone of your JVM. When you insert or query data the database server will always use the JVM time zone.
You are using TIMESTAMP instead of TIMESTAMPTZ. The description of these types do not reflect their actually usage. TIMESTAMPTZ actually means time zone agnostic. Whatever value you insert it will be adjusted to UTC using the session timezone.
Because of these two issues, if you have two different JVMs -- one using Los Angeles time and the other using New York time -- whenever you write a TIMESTAMP with one JVM it will be a different "UTC time" in the other JVM. TIMESTAMP takes the adjusted value and just uses it as given. If you change your TIMESTAMP columns to be TIMESTAMPTZ then the same time in both JVMs will always be the same UTC time.
If you look at the Postgres JDBC Driver's ConnectionFactoryImpl#openConnectionImp you can see where it sets your local JVM's time zone as the time zone for the database server's session zone.
So the only sane way to deal with this is to only ever use TIMESTAMPTZ instead of TIMESTAMP. Here's some more information on this:
PostgreSQL/JDBC and TIMESTAMP vs. TIMESTAMPTZ
http://justatheory.com/computers/databases/postgresql/use-timestamptz.html
The following (very nasty) code works for me:
eventsRecord.setCreatedOn(new Timestamp(System.currentTimeMillis()
- TimeZone.getDefault().getOffset(new Date().getTime())));
Alas jOOQ simply uses the local timezone when saving into PostgreSQL "timestamp without timezone" or MySQL "datetime" fields. The source code evidence for this travesty is here, it does not specify the timezone nor have any facility for the user to override this functionality and specify a timezone. This renders usage of this very basic datatype from jOOQ completely useless, with a multitude of clients all with different timezones writing data to the same field without recording their timezone nor normalizing the data to UTC.
JDBC provides an extra three-argument setTimestamp where the user can specify what timezone is desired (UTC is basically the only value that makes sense). However jOOQ "abstracts" away from JDBC and does not offer this facility.
I have two databases running on different servers in different timezones. There is a couple of tables which contains timestamp with timezone.
I need to dump data from one database and to import it to another with correct timestamp and correct timezone.
I use following command to dump data:
pg_dump -a DB_NAME > dump.sql
I see data are in old timestamp and timezone format: 2013-11-29 14:30:00+02
Then I use command to another server to restore dump:
psql -d DB_NAME -f dump.sql
And I see that timestamps and timezone is from old server - which I think is normal.
Then I tried to put following command in the beginning of dump.sql
SET timezone ...
But still does not work. :(
This is one time operation. Once transferred data will not need sync. Is there a way to do such conversion using pg_dump and pg_restore or similar?
The Postgres the data type timstamptz ( = timestamp with time zone) stores values as UTC timestamp internally (integer value counting microseconds since 2000), which is independent of the time zone those values are displayed for. It does not store any time zone information like some might think, misjudging the name. You can dump and restore as much as you want.
What you see in your client depends on the time zone setting of the session.
Run (in the same session to avoid artifacts):
SHOW timezone;
If you see localtime then Postgres uses the default setting of your server OS.
Set a different time zone (in your session with SET timezone = ...;, for your user or globally in the config files) to see your timestamps formatted differently. Be aware that the time zone setting in your dump file does not have any effect in this case, only the setting of your current session has.
Detailed explanation in this related answer:
Ignoring timezones altogether in Rails and PostgreSQL
About the various ways to set environment variables:
How does the search_path influence identifier resolution and the "current schema"