Representing a future time in PostgreSQL

妖精的绣舞 提交于 2019-12-08 13:58:06

问题


I’ve been conditioned to store past dates as UTC in a database since that is in fact when the event occurred. For future dates, I would store it with a specific timezone, to avoid changes such as leap seconds or timezone rule changes.

Postgres has timestamp with timezone, but under the covers, it stores it as UTC, inferring that the specified timezone is an offset of UTC. If the timezone rules were to change, that would not be reflected in the column.

What is recommended in this case?


回答1:


Think [of it] like a calendar event. UTC doesn’t make sense for that

It sounds like you want to store a localtime with respect to a certain timezone. In that case, store a timestamp (without timezone) and the timezone in a separate column.

For example, suppose you want to record an event which will occur at 10 am on Feb 26, 2030 in Chicago and it must be at 10 am localtime regardless of the timezone rule in effect on that date.

If the database stores the timestamp without timezone:

unutbu=# select '2030-02-26 10:00:00'::timestamp as localtime, 'America/Chicago' AS tzone;
+---------------------+-----------------+
|      localtime      |      tzone      |
+---------------------+-----------------+
| 2030-02-26 10:00:00 | America/Chicago |
+---------------------+-----------------+

Then later, you can find the UTC datetime of the event using

unutbu=# select '2030-02-26 10:00:00'::timestamp AT TIME ZONE 'America/Chicago' AT TIME ZONE 'UTC';
+---------------------+
|      timezone       |
+---------------------+
| 2030-02-26 16:00:00 |
+---------------------+

The query returns the UTC datetime, 2030-02-26 16:00:00, which corresponds to 2030-02-26 10:00:00 localtime in Chicago.

Using AT TIME ZONE delays the application of the timezone rules to when the query is made instead of when the timestamptz was inserted.


Using AT TIME ZONE on a timestamp localizes the datetime to the given timezone, but reports the datetime in the user's timezone. Using AT TIME ZONE on a timestamptz converts the datetime to the given timezone, then drops the offset, thus returning a timestamp. Above, AT TIME ZONE is used twice: first to localize a timestamp and next to convert the returned timestamptz to a new timezone (UTC). The result is a timestamp in UTC.

Here is an example, demonstrating AT TIME ZONE's behavior on timestamps:

unutbu=# SET timezone = 'America/Chicago';
unutbu=# SELECT '2030-02-26 10:00:00'::timestamp AT TIME ZONE 'America/Chicago';
+------------------------+
|        timezone        |
+------------------------+
| 2030-02-26 10:00:00-06 |
+------------------------+

unutbu=# SET timezone = 'America/Los_Angeles';
unutbu=# SELECT '2030-02-26 10:00:00'::timestamp AT TIME ZONE 'America/Chicago';
+------------------------+
|        timezone        |
+------------------------+
| 2030-02-26 08:00:00-08 |
+------------------------+

2030-02-26 10:00:00-06 and 2030-02-26 08:00:00-08 are the same datetimes but reported in different user timezones. This shows 10am in Chicago is 8am in Los Angeles (using current timezone definitions):

unutbu=# SELECT '2030-02-26 10:00:00-06'::timestamptz AT TIME ZONE 'America/Los_Angeles';
+---------------------+
|      timezone       |
+---------------------+
| 2030-02-26 08:00:00 |
+---------------------+

An alternative to using AT TIME ZONE twice is to set the user timezone to UTC. Then you could use

select localtime AT TIME ZONE tzone

Note that when done this way, a timestamptz is returned instead of a timestamp.


Beware that storing localtimes can be problematic because there can be nonexistent times and ambiguous times. For example, 2018-03-11 02:30:00 is a nonexistent localtime in America/Chicago. Postgresql normalizes nonexistent localtimes by assuming it refers to the corresponding time after Daylight Savings Time (DST) has begun (as though someone forgot to set their clock forward):

unutbu=# select '2018-03-11 02:30:00'::timestamp AT TIME ZONE 'America/Chicago' AT TIME ZONE 'UTC';
+---------------------+
|      timezone       |
+---------------------+
| 2018-03-11 08:30:00 |
+---------------------+
(1 row)

unutbu=# select '2018-03-11 03:30:00'::timestamp AT TIME ZONE 'America/Chicago' AT TIME ZONE 'UTC';
+---------------------+
|      timezone       |
+---------------------+
| 2018-03-11 08:30:00 |
+---------------------+
(1 row)

An example of an ambiguous localtime is 2018-11-04 01:00:00 in America/Chicago. It occurs twice due to DST. Postgresql resolves this ambiguity by choosing the later time, after DST has ended:

unutbu=# select '2018-11-04 01:00:00'::timestamp AT TIME ZONE 'America/Chicago' AT TIME ZONE 'UTC';
+---------------------+
|      timezone       |
+---------------------+
| 2018-11-04 07:00:00 |
+---------------------+

Notice that this means there is no way to refer to 2018-11-04 06:00:00 UTC by storing localtimes in the America/Chicago timezone:

unutbu=# select '2018-11-04 00:59:59'::timestamp AT TIME ZONE 'America/Chicago' AT TIME ZONE 'UTC';
+---------------------+
|      timezone       |
+---------------------+
| 2018-11-04 05:59:59 |
+---------------------+



回答2:


Particularly if you want to protect yourself from future time zune changes you should use timestamp with time zone.

PostgreSQL internally stores the number of microseconds since 2000-01-01 00:00:00, which is safe from time zone changes. If you keep updating your PostgreSQL, it will always display that absolute value correctly for your session time zone.

There is no provision for leap seconds in PostgreSQL.




回答3:


For future dates, I would store it with a specific timezone, to avoid changes such as leap seconds or timezone rule changes.

That seems backward. The main advantage of UTC over other time zones is that it is less prone to unexpected future changes: UTC only introduces leap seconds at known, limited points of the calendar year; and has none of the frequent politically-mandated offset changes.

Storing values in some locally-managed timezone leaves those values more prone (compared with UTC) to arbitrary, unpredictable future changes in meaning.

So, the general recommendation is: Store all time values (whether date, or date+time) as UTC in the database, process them internally as UTC values; and convert to/from a local timezone only at external interfaces.

For PostgreSQL, that means prefer TIMESTAMP WITH TIME ZONE.



来源:https://stackoverflow.com/questions/54897691/representing-a-future-time-in-postgresql

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!