Ignoring time zones altogether in Rails and PostgreSQL
我正在处理Rails和Postgres中的日期和时间,并遇到了这个问题:
数据库采用UTC格式。
用户在Rails应用程序中设置了一个可选时区,但它仅在获取用户本地时间进行时间比较时使用。
用户存储时间,比如2012年3月17日下午7点。我不希望存储时区转换或时区。我只想节省那个日期和时间。这样,如果用户更改了时区,它仍然会显示2012年3月17日下午7点。
我只使用用户指定的时区在用户本地时区中获取"早于"或"晚于"当前时间的记录。
我目前正在使用"没有时区的时间戳",但是当我检索记录时,rails(?)将它们转换为应用程序中的时区,这是我不想要的。
1 2 | Appointment.first.time => Fri, 02 Mar 2012 19:00:00 UTC +00:00 |
因为数据库中的记录似乎是以UTC的形式出现的,所以我的方法是利用当前时间,删除带有"date.strptime(str,"%m/%d/%y")的时区,然后执行查询:
1 | .where("time >= ?", date_start) |
似乎必须有一种简单的方法来忽略周围的时区。有什么想法吗?
数据类型
从字面上看,
- 在PostgreSQL中生成两个日期之间的时间序列
时代
在内部,时间戳存储为来自某个纪元的计数。Postgres使用的是2000年第一天的第一个时刻,即2000-01-01T00:00:00Z。八位字节用于存储计数数字。根据编译时选项的不同,该数字可以是:好的。
- 8字节整数(默认值),0到6位小数秒
一个浮点数字(已弃用),0到10位小数秒,其中精度会因远离epoch的值而迅速降低。现代Postgres安装使用8字节整数。
请注意,Postgres不使用Unix时间。Postgres的时代是2000-01-01的第一个时刻,而不是Unix的1970-01-01。虽然Unix时间的分辨率为整秒,但Postgres保留了几秒的分数。好的。
如果您定义了数据类型
当您稍后显示
For
timestamp with time zone , the internally stored value is always in UTC (Universal Coordinated Time ...)Ok.
大胆强调我的。时区本身从不存储。它是一个输入修饰符,用于根据存储的UTC时间戳计算,或者是用于计算要显示的本地时间的输出修饰符,带有附加的时区偏移量。如果在输入时不附加
像psql或pgadmin这样的客户机或任何通过libpq通信的应用程序(比如ruby和pg gem)都会显示当前时区的时间戳加偏移量,或者根据请求的时区(见下文)。它总是在同一时间点,只有显示格式不同。或者,正如手册所说:好的。
All timezone-aware dates and times are stored internally in UTC. They
are converted to local time in the zone specified by the TimeZone
configuration parameter before being displayed to the client.Ok.
考虑这个简单的例子(在psql中):好的。
1 2 3 4 | db=# SELECT timestamptz '2012-03-05 20:00+03'; timestamptz ------------------------ 2012-03-05 18:00:00+01 |
大胆强调我的。这里发生了什么?我为输入文字选择了一个任意的时区偏移量
Postgres已经忘记了这个值是如何输入的。它只记住值和数据类型。就像一个十进制数字。
一旦你掌握了这个逻辑,你就可以做任何你想做的事情。现在所缺少的只是一个工具,用于根据特定时区解释或表示时间戳文本。这就是
输入UTC
1 | SELECT TIMESTAMP '2012-03-05 17:00:00' AT TIME ZONE 'UTC' |
…相当于:好的。
1 | SELECT timestamptz '2012-03-05 17:00:00 UTC' |
显示与东部标准时间(东部标准时间)相同的时间点:好的。
1 | SELECT TIMESTAMP '2012-03-05 17:00:00' AT TIME ZONE 'UTC' AT TIME ZONE 'EST' |
是的,两次。第一个将
1 2 3 4 5 6 7 8 9 10 11 12 13 | SELECT ts AT TIME ZONE 'UTC' FROM ( VALUES (1, timestamptz '2012-03-05 17:00:00+0') , (2, timestamptz '2012-03-05 18:00:00+1') , (3, timestamptz '2012-03-05 17:00:00 UTC') , (4, TIMESTAMP '2012-03-05 11:00:00' AT TIME ZONE '+6') , (5, TIMESTAMP '2012-03-05 17:00:00' AT TIME ZONE 'UTC') , (6, TIMESTAMP '2012-03-05 07:00:00' AT TIME ZONE 'US/Hawaii') -- ① , (7, timestamptz '2012-03-05 07:00:00 US/Hawaii') -- ① , (8, TIMESTAMP '2012-03-05 07:00:00' AT TIME ZONE 'HST') -- ① <strike>, (9, TIMESTAMP '2012-03-05 18:00:00+1')</strike> -- ② loaded footgun! ) t(id, ts); |
返回8(或9)个相同的行,其中时间戳列包含相同的UTC时间戳
①带有时区名称和夏威夷时间时区缩写的第6-8行以DST(夏令时)为准,可能有所不同,但目前没有。像
- 具有相同属性的时区名称在应用于时间戳时产生不同的结果
夏令时不是人类提出的最聪明的想法之一。好的。 ②第9排,标记为"负重"的步兵为我工作,但只是巧合。如果您显式地将文本强制转换为
User stores a time, say March 17, 2012, 7pm. I don't want timezone Ok. 时区本身从不存储。使用上面的方法之一输入UTC时间戳。好的。
I only use the users specified time zone to get records 'before' or Ok. 您可以对不同时区的所有客户机使用一个查询。对于绝对全局时间:好的。 对于本地时钟的时间:好的。 还不厌倦背景信息吗?手册中还有更多内容。好的。好啊。 如果您希望默认使用UTC进行交易: 在
conversions or the timezone to be stored.
'after' the current time in the users local time zone.
1 | config.time_zone = 'UTC' |
然后,如果您存储的当前用户时区名称是
1 | post.created_at.in_time_zone(CURRENT_USER.timezone) |