sql - 选择预定项目时在 Postgres 中考虑 DST

标签 sql ruby postgresql timezone dst

我有一个Postgres时钟闹钟表(不是真的,但这是类似的,而且更容易解释)。警报由用户设置,分辨率为 1 小时,用户可以来自许多不同的时区。警报每天都在重复。我想可靠地获取应该在一天中的特定时间响起的警报,并且我遇到夏令时问题。如何以最佳方式做到这一点?

例子

Alfred and Lotta both live in Stockholm (+1 hour from UTC, but +2h when it's DST).
Sharon lives in Singapore (+8 hours from UTC, no DST)

During winter, Alfred sets an alarm for 4 AM. The alarm should go off at 4 AM local time, all year.
During summer, Lotta sets an alarm for 5 AM. Again, it should go off at 5 AM all year round.
Meanwhile, Sharon has set an alarm for 11 AM.

All of these can be stored in the database as 03:00 UTC.

If I query the database in the winter for alarms that should go off at 03:00 UTC, I want Alfred's and Sharon's alarms. Singapore is now +7h from Sweden, so 11 AM in Singapore is 4 AM in Sweden. Lotta's alarm should not go off for another hour.

Conversely, if I query the database in the summer for alarms that should go off at 03:00 UTC, I want Lotta's and Sharon's alarms. Singapore is +6h from Sweden now, so 11 AM in Singapore is 5 AM in Sweden now. Sven's alarm went off an hour ago.

我如何存储它并查询数据库?

如有必要,我可以更改数据库架构。目前,我们根本不针对 DST 进行调整,实际上只有一个“小时”整数字段(这看起来很愚蠢,时间字段会更好)。

看来我需要同时存储 UTC 时间和时区信息,但我不知道如何在 Postgres 中最好地实现这一点。我发现 Postgres 有某种时区概念,但据我所知没有时区字段类型。此外,我想我需要在 SQL 中进行一些计算,以确定如何根据时区数据和创建日期在选择中偏移 UTC 时间。我对 SQL 不是很好……

我确实想在 Postgres 中解决这个问题,因为可能会有很多“警报”,而且我想避免将所有警报都提取到 Ruby 中并在那里进行过滤所带来的性能问题。 (是的,这是一个 Rails 应用程序。)

最佳答案

使用timestamp with time zone (timestamptz) 进行计算。
闹钟时间可以是time [without time zone]
但是您必须为每一行显式保存时区

永远不要使用time with time zone (timetz) 它是一个逻辑错误的类型,PostgreSQL 不鼓励使用它。 The manual:

The type time with time zone is defined by the SQL standard, but the definition exhibits properties which lead to questionable usefulness. In most cases, a combination of date, time, timestamp without timezone, and timestamp with time zone should provide a complete range of date/time functionality required by any application.

演示设置:

CREATE TABLE alarm(name text, t time, tz text);
INSERT INTO alarm VALUES
  ('Alfred', '04:00', 'Europe/Stockholm') -- Alfred sets an alarm for 4 AM.
, ('Lotta',  '05:00', 'Europe/Stockholm') -- Lotta sets an alarm for 5 AM. 
, ('Sharon', '11:00', 'Asia/Singapore');  -- Sharon has set an alarm for 11 AM.

它必须是时区名称(不是缩写)以说明 DST。相关:

获取“今天”的匹配闹钟:

SELECT *
FROM   alarm
WHERE  ((<b>'2012-07-01'::date + t</b>) AT TIME ZONE <b>tz</b> AT TIME ZONE <b>'UTC'</b>)::time
       = '03:00'::time
  • ('2012-7-1'::date + t) ... 组装 timestamp [without time zone] 也可以只是 now()::date + t 表示“今天”。
  • AT WITH TIME ZONE tz ...在保存的时区放置时间戳,结果为 timestamptz
  • AT WITH TIME ZONE 'UTC' ... 根据 UTC timestamp
  • ::time ...提取时间分量的最简单方法。

这里可以查询time zone names :

SELECT *
FROM   pg_timezone_names
WHERE  name ~~* '%sing%'
LIMIT  10;

db<> fiddle here - 展示夏季/冬季
<子>旧sqlfiddle

关于sql - 选择预定项目时在 Postgres 中考虑 DST,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13239534/

相关文章:

sql - 字符串聚合输出大 CSV 行溢出 `replace`

sql - 在 Oracle SQL 中什么时候需要使用分号和斜线?

mysql - 选择相关的元素属性作为字符串

ruby-on-rails - 使用 Passenger 部署 Rails "the page you were looking for doesn' t 存在”

postgresql - 时区感知 date_trunc 函数

json - PostgreSQL 将 column_1 text[] 类型转换为 column_2 json 类型

mysql - 按多列创建从最小金额到最大金额的排名

ruby - 以鞋子为中心的流程

ruby-on-rails - 使用 Ruby Searchkick 的多个搜索词

javascript - 如何在 pg-promise 中设置模式