Postgresql:将日期字符串'2016-01-01 00:00:00'转换为具有特定时区的日期时间

时间:2016-03-15 22:55:46

标签: postgresql

我处境很棘手。客户端的数据库时区已配置为America / Chicago而不是UTC。

从应用程序中,我们要求客户输入有用日期,遗憾的是这些日期按“原样”存储,因此如果他们在文本输入中输入“2001-01-01 00:00:00”,则相同值将存储在数据库中,我们忽略了客户的时区。我们单独保存这些信息。

表格列的类型为TIMEZONETZ。所以Postgresql会在最后添加美国/芝加哥时区偏移:例如'2001-01-01 00:00:00-02'。 当然,大多数客户都不在芝加哥。

困难的部分是,即使知道客户的时区,由于在将日期时间存储到数据库之前未正确预处理日期时间,因此很难对数据库进行计算。

我尝试过的解决方案是找到一种从列值中提取日期时间字符串的方法,并将其重新转换为具有正确时区的日期。例如(伪代码):

// This is psuedo code
SELECT DATETIME((SELECT date_string(mycolumn) FROM mytable),  
TIMEZONE('America/Managua'));

Which would be equivalent in PHP:
$customerInput = '2016-01-01 00:00:00';
$format = 'Y-m-d H:i:s';
$wrongDateStoredInDb = DateTime::createFromFormat($format, $customerInput, new DateTimezone('America/Chicago'));

// In order to fix that date, I'd extract the dateString and create a new DateTime but passing the correct timezone info.

$customerTimezone = new Timezone('America/Bogota');
$customerInput = $wrongDateStoredInDb->format($format); // Assuming we didn't have it already.
$actualDateTime = DateTime::createFromFormat($format, $customerInput, $customerTimezone);

通过这种信息,我可以使用正确的值运行日期范围的计算,例如:

// Pseudo-code
SELECT * FROM myTable WHERE fix_date_time(columnWithInvalidDate, `correctTimezone`)::timestamp > `sometimestamp`;

我已经阅读了Postgresql文档,并且我已尽力而为,但似乎没有任何工作。

任何建议都非常受欢迎!

1 个答案:

答案 0 :(得分:0)

所以你说你有一个timestamptz列。它不是存储为字符串,而是存储为"瞬间"自纪元以来的几微秒。但是当你执行INSERT并给出一个字符串时,Postgres会在存储之前自动将你的字符串转换为时间值。它一直在假设你给它的字符串是在芝加哥时间,因为它是默认的时区。

现在,您希望将这些时间重新解释为位于用户的时区。要做到这一点,你可以将它们放回到字符串中(在芝加哥时间),然后再次解析它们但是使用不同的时区。

假设您有这样的数据:

CREATE TABLE t (id int primary key, ts timestamptz, tz text);

SET TIMEZONE='America/Chicago';
INSERT INTO t
VALUES
(1, '2015-01-01 12:00:00', 'America/Managua'),
(2, '2015-01-01 12:00:00', 'America/Los_Angeles')
;

然后,这将为您提供用户真正意义的新时代:

SET TIMEZONE='America/Chicago';
SELECT  ts::text::timestamp AT TIME ZONE tz FROM t;

要分解它:ts::text将值字符串化为芝加哥时间,然后我们重新解析它,但是进入一个没有时区信息的裸timestamp。然后我们附上一个时区---不是芝加哥时间,而是用户自己的时区。

从这里你应该能够处理坏行(而不是新行,如果你已经改变了服务器的默认时区)。

有一点需要注意的是,如果用户输入时间然后更改了他们的时区,则无法恢复时区,因此这将错误地解释旧时间。