Postgresql 在“选择”中显示每个位置的本地时区
我有一个火车乘车表,使用以下示例代码:Postgresql 在“选择”中显示每个位置的本地时区,postgresql,timestamp-with-timezone,Postgresql,Timestamp With Timezone,我有一个火车乘车表,使用以下示例代码: CREATE TABLE train_rides ( trip_id bigserial PRIMARY KEY, origination text NOT NULL, destination text NOT NULL, departure timestamp with time zone NOT NULL, arrival timestamp with time zone NOT NULL ); INSERT
CREATE TABLE train_rides (
trip_id bigserial PRIMARY KEY,
origination text NOT NULL,
destination text NOT NULL,
departure timestamp with time zone NOT NULL,
arrival timestamp with time zone NOT NULL
);
INSERT INTO train_rides (origination, destination, departure, arrival)
VALUES
('Chicago', 'New York', '2017-11-13 21:30 CST', '2017-11-14 18:23 EST'),
('New York', 'New Orleans', '2017-11-15 14:15 EST', '2017-11-16 19:32 CST'),
('New Orleans', 'Los Angeles', '2017-11-17 13:45 CST', '2017-11-18 9:00 PST'),
('Los Angeles', 'San Francisco', '2017-11-19 10:10 PST', '2017-11-19 21:24 PST'),
('San Francisco', 'Denver', '2017-11-20 9:10 PST', '2017-11-21 18:38 MST'),
('Denver', 'Chicago', '2017-11-22 19:10 MST', '2017-11-23 14:50 CST');
当我对该数据运行以下查询时:
SELECT origination || ' to ' || destination AS segment,
to_char(departure, 'YYYY-MM-DD HH12:MI a.m. TZ') AS departure,
to_char(arrival, 'YYYY-MM-DD HH12:MI a.m. TZ') AS arrival
FROM train_rides;
它提供了以下输出:
--------------------------------------------------------------------------------------
| segment | departure | arrival |
--------------------------------------------------------------------------------------
| Chicago to New York | 2017-11-13 09:30 p.m. CST | 2017-11-14 05:23 p.m. CST |
| New York to New Orleans | 2017-11-15 01:15 p.m. CST | 2017-11-16 07:32 p.m. CST |
| New Orleans to Los Angeles | 2017-11-17 01:45 p.m. CST | 2017-11-18 11:00 a.m. CST |
| San Francisco to Denver | 2017-11-20 11:10 a.m. CST | 2017-11-21 07:38 p.m. CST |
| Denver to Chicago | 2017-11-22 08:10 p.m. CST | 2017-11-23 02:50 p.m. CST |
--------------------------------------------------------------------------------------
所有时间都显示在CST时区中,该时区基于服务器时区设置
首选输出
我希望输出反映每个出发地或目的地城市的本地时区,如下所示:
--------------------------------------------------------------------------------------
| segment | departure | arrival |
--------------------------------------------------------------------------------------
| Chicago to New York | 2017-11-13 09:30 p.m. CST | 2017-11-14 06:23 p.m. EST |
| New York to New Orleans | 2017-11-15 02:15 p.m. EST | 2017-11-16 07:32 p.m. CST |
| New Orleans to Los Angeles | 2017-11-17 01:45 p.m. CST | 2017-11-18 09:00 a.m. PST |
| San Francisco to Denver | 2017-11-20 09:10 a.m. PST | 2017-11-21 06:38 p.m. MST |
| Denver to Chicago | 2017-11-22 07:10 p.m. MST | 2017-11-23 02:50 p.m. CST |
--------------------------------------------------------------------------------------
我如何让每个时间都反映当地时区?例如,在上面的第一行中,芝加哥的出发显示为CST,纽约的到达显示为EST
一种可能的解决方案是在时区使用限定符,可能使用如下查找表:
----------------------------
| city | local_tz |
----------------------------
| Chicago | CST |
| Denver | MST |
| Los Angeles | PST |
| New Orleans | CST |
| New York | EST |
| San Francisco | PST |
----------------------------
我曾考虑过使用plpgsql代码块构建动态查询,但那会非常混乱。我真的希望有一种更简单、更优雅的方法来做到这一点。我找到了一种方法来获得想要的结果;这不是最优雅的方法,但确实有效
首先,我在train_rides表中添加了两个附加列:
-- Add columns to hold local times
ALTER TABLE train_rides
ADD COLUMN departure_local TIMESTAMP WITHOUT TIME ZONE,
ADD COLUMN arrival_local TIMESTAMP WITHOUT TIME ZONE;
然后,我使用一堆代码(使用不那么优雅的蛮力方法)填充LocalExample列:
接下来,我以相同的方式填充本地到达者:
-- Update arrivals to reflect local times
UPDATE train_rides
SET arrival_local = arrival AT TIME ZONE 'US/Central'
WHERE origination = 'Chicago';
UPDATE train_rides
SET arrival_local = arrival AT TIME ZONE 'US/Mountain'
WHERE origination = 'Denver';
UPDATE train_rides
SET arrival_local = arrival AT TIME ZONE 'US/Pacific'
WHERE origination = 'Los Angeles';
UPDATE train_rides
SET arrival_local = arrival AT TIME ZONE 'US/Central'
WHERE origination = 'New Orleans';
UPDATE train_rides
SET arrival_local = arrival AT TIME ZONE 'US/Eastern'
WHERE origination = 'New York';
UPDATE train_rides
SET arrival_local = arrival AT TIME ZONE 'US/Pacific'
WHERE origination = 'San Francisco';
最后,我可以使用一些SQL魔术来获得最终结果:
SELECT origination || ' to ' || destination AS segment,
to_char(departure_local, 'YYYY-MM-DD HH12:MI a.m. ') ||
(SELECT local_tz from local_timezones tz
WHERE tr.origination = tz.city) AS departure,
to_char(arrival_local, 'YYYY-MM-DD HH12:MI a.m. ') ||
(SELECT local_tz from local_timezones tz
WHERE tr.destination = tz.city) AS arrival
FROM train_rides tr;
如果有一种方法可以使用查找动态填充AT TIME ZONE参数,那么所有这些UPDATE语句都可以减少到只有两个。这种蛮力方法无法很好地扩展,因为我们必须为每个可能的城市手动编写代码
除非,也就是说,有人知道更好的方法。今天我了解了SQL子查询的威力,我的脑子里突然亮起了灯泡。下面是两个更好的方法
这两种方法都使用以下名为trains\u local\u timezones
的查找表:
-----------------------------------------
| city | timezone | tz_abbr |
-----------------------------------------
| Chicago | US/Central | CST |
| Denver | US/Mountain | MST |
| Los Angeles | US/Pacific | PST |
| New Orleans | US/Central | CST |
| New York | US/Eastern | EST |
| San Francisco | US/Pacific | PST |
-----------------------------------------
--更好的方法(这可以根据需要扩展)
--最佳方式(无需额外列)
不同的是,我会在数据库中使用带有时区的时间戳
,其他一切都可以。然后,查询的结果是一个不带时区的时间戳
,这更有意义,因为它表明时间戳将被理解为位于起始
列指定的时区中。
-----------------------------------------
| city | timezone | tz_abbr |
-----------------------------------------
| Chicago | US/Central | CST |
| Denver | US/Mountain | MST |
| Los Angeles | US/Pacific | PST |
| New Orleans | US/Central | CST |
| New York | US/Eastern | EST |
| San Francisco | US/Pacific | PST |
-----------------------------------------
-- Add columns to hold local times
ALTER TABLE train_rides
ADD COLUMN departure_local TIMESTAMP WITHOUT TIME ZONE,
ADD COLUMN arrival_local TIMESTAMP WITHOUT TIME ZONE;
-- Update departures to reflect local times
UPDATE train_rides
SET departure_local = departure AT TIME ZONE
(SELECT timezone from trains_local_timezones tz
WHERE origination = tz.city);
-- Update arrivals to reflect local times
UPDATE train_rides
SET arrival_local = arrival AT TIME ZONE
(SELECT timezone from trains_local_timezones tz
WHERE destination = tz.city);
SELECT origination || ' to ' || destination AS segment,
to_char(departure_local, 'YYYY-MM-DD HH12:MI AM ') ||
(SELECT tz_abbr from trains_local_timezones tz
WHERE tr.origination = tz.city) AS local_departure,
to_char(arrival_local, 'YYYY-MM-DD HH12:MI AM ') ||
(SELECT tz_abbr from trains_local_timezones tz
WHERE tr.destination = tz.city) AS local_arrival
FROM train_rides tr;
SELECT origination || ' to ' || destination AS segment,
to_char(departure AT TIME ZONE
(SELECT timezone from trains_local_timezones tz
WHERE origination = tz.city), 'YYYY-MM-DD HH12:MI AM ') ||
(SELECT tz_abbr from trains_local_timezones tz
WHERE tr.origination = tz.city) AS local_departure,
to_char(arrival AT TIME ZONE
(SELECT timezone from trains_local_timezones tz
WHERE destination = tz.city), 'YYYY-MM-DD HH12:MI AM ') ||
(SELECT tz_abbr from trains_local_timezones tz
WHERE tr.destination = tz.city) AS local_arrival
FROM train_rides tr;