3

POSIXct使用RPostgreSQL. 在下面的示例中,我定义了两个时间戳字段:一个时区,另一个不带. 但是,在通过和传递POSIXct对象时,它们的处理方式似乎完全相同。dbWriteTabledbReadTable

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 的时区?
4

2 回答 2

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 年 1 月 1 日 10:20:10”

df2$dt2

“美国东部时间 2016 年 1 月 1 日 10:20:10”

df2$dt3

“美国东部时间 2016 年 1 月 1 日 10:20:10”

在 postgres 中也是如此 - 如在 pgadmin 中所见 在此处输入图像描述

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

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

例如, 在此处输入图像描述

并获取

df2 <- dbReadTable(con, "test_table")

然后正确处理时区

df2$dt1

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

df2$dt2

“美国东部时间 2016 年 1 月 1 日 10:20:10”

df2$dt3

“美国东部时间 2016 年 1 月 1 日 10:20:10”

因此,这表明 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"
于 2016-11-30T21:59:19.780 回答
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"

啊。好多了。

当然,您可以坚持使用 base R,并在每次转换时都这样做。

dttm.cet <- as.POSIXct( format( dttm.utc, tz = "CET", usetz = T ), tz = "CET" )

但就个人而言,我更喜欢这个功能。

于 2016-11-10T10:24:48.243 回答