我正在努力了解使用POSIXct
在R和Postgres之间传递RPostgreSQL
个对象时幕后发生了什么。在以下示例中,我定义了两个时间戳字段:一个 一个时区,另一个没有。但是,通过POSIXct
和dbWriteTable
传递dbReadTable
个对象时,它们的处理方式完全相同。
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"
问题:
答案 0 :(得分:2)
我认为你已经在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在这里看到的那样
这表明postgres没有从R
获得时区请注意,如果我们手动更改test_table中的一个时区 (例如,pgadmin中的第一条记录)
并获取
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,与上面相同。
E.g:
#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"
答案 1 :(得分:0)
首先,让我说我不是R处理这种方式的粉丝。让我们以UTC的时间值:
dttm.utc <- as.POSIXct("2016-01-01 10:20:10", tz="UTC")
dttm.utc
[1] "2016-01-01 10:20:10 UTC"
现在我们可以将它转换为CET时区相对容易:
dttm.cet <- format( dttm.utc, tz = "CET", usetz = T )
dttm.cet
[1] "2016-01-01 11:20:10 CET"
但是检查一下,每个值都是不同的类。第一种是POSIX
格式,但第二种格式已被character
函数转换为format
类。
class( dttm.utc )
[1] "POSIXct" "POSIXt"
class( dttm.cet )
[1] "character"
这不好,因为这意味着我们不能再向另一个方向转换相同的转换,我们首先要将后一个值转换为POSIX
类,非常好小心不要让R用时区瞎逛:
dttm.cet <- as.POSIXct( dttm.cet, tz = "CET" )
dttm.cet
[1] "2016-01-01 11:20:10 CET"
class( dttm.cet )
[1] "POSIXct" "POSIXt"
现在我们可以转换它:
format( dttm.cet, tz = "UTC", usetz = TRUE )
[1] "2016-01-01 10:20:10 UTC"
但这让我们回到了character
班。很烦人。这是一个解决方法。将两步转换构建为一个函数,并从现在开始使用它。
convert.tz <- function( x, tz ) {
new <- format( x, tz = tz, usetz = T )
return( as.POSIXct( new, tz = tz ) )
}
尝试一下:
dttm.utc <- as.POSIXct("2016-01-01 10:20:10", tz="UTC")
dttm.utc
[1] "2016-01-01 10:20:10 UTC"
dttm.cet <- convert.tz( dttm.utc, "CET" )
dttm.cet
[1] "2016-01-01 11:20:10 CET"
class( dttm.utc )
[1] "POSIXct" "POSIXt"
class( dttm.cet )
[1] "POSIXct" "POSIXt"
所以现在转换并没有改变格式,这意味着我们可以在转换中使用任何一种方式,而无需更改方法:
convert.tz( dttm.cet, "UTC" )
[1] "2016-01-01 10:20:10 UTC"
唉唉。好多了。
当然,您可以坚持使用基础R,并在每次转换时执行此操作。
dttm.cet <- as.POSIXct( format( dttm.utc, tz = "CET", usetz = T ), tz = "CET" )
但就个人而言,我更喜欢这个功能。