r - 在 R 和 Postgres DBMS 之间传递 POSIXct 对象时如何正确处理时区?

标签 r posixct rpostgresql

当使用 RPostgreSQL 在 R 和 Postgres 之间传递 POSIXct 对象时,我很难理解幕后究竟发生了什么。在下面的示例中,我定义了两个时间戳字段:一个带有时区,另一个没有。但是,当通过 dbWriteTabledbReadTable 传递 POSIXct 对象时,它们似乎受到完全相同的对待。

library(RPostgreSQL)

drv <- dbDriver("PostgreSQL")
con <- dbConnect(drv, host = "127.0.0.1", port = "5432", user= "postgres",
                 dbname = "test_db")

q <- "
CREATE TABLE test_table
(
  dttm timestamp without time zone,
  dttmtz timestamp with time zone
)"
dbSendQuery(con, q)

# using timezone CET
dttm <- as.POSIXct("2016-01-01 10:20:10", tz="CET")
df <- data.frame(dttm = dttm, dttmtz = dttm)
dbWriteTable(con, "test_table", df, overwrite=FALSE, append=T, row.names=0)

# using timezone UTC    
dttm <- as.POSIXct("2016-01-01 14:20:10", tz="UTC")
df <- data.frame(dttm = dttm, dttmtz = dttm)
dbWriteTable(con, "test_table", df, overwrite=FALSE, append=T, row.names=0)

df2 <- dbReadTable(con, "test_table")

两个字段的结果完全一样。似乎时区已完全丢弃。

df2$dttm
[1] "2016-01-01 10:20:10 CET" "2016-01-01 14:20:10 CET"

df2$dttmtz
"2016-01-01 10:20:10 CET" "2016-01-01 14:20:10 CET"

问题:

  1. 幕后究竟发生了什么?
  2. 如何正确地来回传递 POSIXct 的时区?

最佳答案

我认为您已经指出了 RPostgreSQL 中的一个错误:它似乎没有从 R 获取 POSIXct 对象的时区。通过将时间戳格式化为具有 UTC 偏移量的字符,可以将时区信息正确传递给 PostgreSQL(请参阅此答案底部的示例;2018-09-21 添加)。但首先,这是一个明显错误的说明:

修改代码:

library(RPostgreSQL)

drv <- dbDriver("PostgreSQL")
con <- dbConnect(drv,  port = "5432", user= "postgres",
                 dbname = "test")

# timestamps in three different time zones
dt1 <- as.POSIXct("2016-01-01 10:20:10", tz="US/Eastern")
dt2 <- as.POSIXct("2016-01-01 10:20:10", tz="UTC")
dt3 <- as.POSIXct("2016-01-01 10:20:10", tz="Asia/Tokyo")
df <- data.frame(dt1=dt1, dt2=dt2, dt3=dt3)

q <- "
CREATE TABLE test_table
(
  dt1 timestamp with time zone,
  dt2 timestamp with time zone,
  dt3 timestamp with time zone,
  PRIMARY KEY (dt1)
)"
dbSendQuery(con, q)

dbWriteTable(con, "test_table", df, overwrite=FALSE, append=T, row.names=0)

df2 <- dbReadTable(con, "test_table")

注意三个时间戳都是相等的 时区处理不正确

df2$dt1

“2016-01-01 10:20:10 EST”

df2$dt2

“2016-01-01 10:20:10 EST”

df2$dt3

“2016-01-01 10:20:10 EST”

在 postgres 中也是如此 - 如此处的 pgadmin 所示 enter image description here

这表明 postgres 没有从 R 获取时区

注意如果我们在test_table中手动更改一个时区 (例如,pgadmin 中的第一条记录)

例如, enter image description here

并获取

df2 <- dbReadTable(con, "test_table")

然后正确处理了时区

df2$dt1

“2016-01-01 05:20:10 EST”

df2$dt2

“2016-01-01 10:20:10 EST”

df2$dt3

“2016-01-01 10:20:10 EST”

因此这表明 RPostgreSQL 没有正确地将时区信息传递给 postgres 但 RPostgreSQL 正确地从 postgres 获取时区信息。


原始问题的答案

要使用 RPostgreSQL 将带时区的时间戳从 R 传递到 Postgres,只需将其格式化为具有 UTC 偏移量的字符串(例如,“2016-01-01 10:20:10- 0500"; 例如,使用 format 然后将其传递给 Postgres,同上。

例如:

#convert POSIXct to character with offset from UTC
df$dt1 <- format(df$dt1, format = "%Y-%m-%d %H:%M:%OS%z")
df$dt2 <- format(df$dt2, format = "%Y-%m-%d %H:%M:%OS%z")
df$dt3 <- format(df$dt3, format = "%Y-%m-%d %H:%M:%OS%z")

##> df
##                       dt1                      dt2                      dt3
##1 2016-01-01 10:20:10-0500 2016-01-01 10:20:10+0000 2016-01-01 10:20:10+0900

q <- "
CREATE TABLE test_table2
(
  dt1 timestamp with time zone,
  dt2 timestamp with time zone,
  dt3 timestamp with time zone,
  PRIMARY KEY (dt1)
)"
dbSendQuery(con, q)

dbWriteTable(con, "test_table2", df, overwrite=FALSE, append=T, row.names=0)

df3 <- dbReadTable(con, "test_table2")

#Note that times are now correct (in local time zone)
##> df3$dt1
##[1] "2016-01-01 10:20:10 EST"
##> df3$dt2
##[1] "2016-01-01 05:20:10 EST"
##> df3$dt3
##[1] "2015-12-31 20:20:10 EST"

关于r - 在 R 和 Postgres DBMS 之间传递 POSIXct 对象时如何正确处理时区?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40524225/

相关文章:

r - 使用 R 在 SQL 中查询混合大小写列

r - 如何从R中的数据库获取列名?

linux - 让 Linux 下的 R 访问 DLL

r - 在 ubuntu 上安装以前版本的 R

r markdown 未编译

r - 将日期转换为 POSIXct

r - 从 POSIXct 中提取时间和日期

r - 将数据帧中的字符转换为时间戳

RPostgreSQL 访问数据库,错误 : unable to find an inherited method for function "show", 签名 "PostgreSQLConnection"

sql - dbplyr、dplyr 和没有 SQL 等效项的函数 [例如 `slice()`]