2016-11-10 50 views
2

我很努力地理解在R和Postgres之間使用RPostgreSQL傳遞POSIXct對象時在幕後發生了什麼。在下面的例子中,我定義了兩個時間戳字段:一個一個時區另一個沒有。但是,看起來在通過dbWriteTabledbReadTable傳遞POSIXct對象時,它們的處理方式完全相同。如何在R和Postgres DBMS之間傳遞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的時區?

回答

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格式,但第二種格式已由format函數轉換爲character類。

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") 

但個人而言,我更喜歡這個功能。

+0

感謝rosscova。但是,我不確定您是否解決了我和R和Postgres之間的日期時間問題,而不僅僅是更改POSIXct對象的時區。對於後者,有可用的功能,例如, 'lubridate :: with_tz(dttm.utc,「CET」)'。所以不需要寫另一個包裝。 –

+0

對,對不起。我認爲你遇到的問題是R轉換本身。解決R tz轉換問題不解決問題的其餘部分?並感謝'lubridate'解決方案,我需要更熟悉它methinks :) – rosscova

1

我想你已經指出了RPostgreSQL中的一個錯誤:它似乎沒有從R的POSIXct對象獲取時區。

修改代碼:

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分十秒EST」

等於 時區
df2$dt2 

「2016-01-01 10:20:10 EST」

df2$dt3 

「2016年1月1日十點二十分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年1月1日5時二十分10秒EST」

df2$dt2 

「2016-01-01 10:20:10 E ST」

df2$dt3 

「2016年1月1日10時20分一十秒EST」

所以這suggets即RPostgreSQL是正確地傳遞時區信息的Postgres但RPostgreSQL正確越來越來自postgres的時區信息。