如何存储时区不可知的日期,以便使用拼花地板在R和Python之间共享?

2024-10-03 13:19:47 发布

您现在位置:Python中文网/ 问答频道 /正文

现在我有以下问题。我想在R和Python之间共享包含日期时间的数据帧,但希望它们是时区无关的

例如,我从R中保存一个data.table(或data.frame),第一列Date=as.POSIXct('2021-10-22')。这是一个将显示为在本地时区中的日期,但默认情况下时区已设置为“”

当我将其写入拼花地板文件并读入熊猫数据帧时,它被视为已调整为UTC时间,但没有时区信息。我希望能够将其理解为没有时区信息的原始2021-10-22值

下面是一些代码来说明这个问题。我可以接收文件和.tz_localize(None)中间日期列,但如果能够从一个不可知论者转到另一个不可知论者,那就更好了

rm(list=ls())

library(arrow)
library(data.table)
library(lubridate)

outputFile <- 'D:/Temp/Test.parquet'
outputFileFeather <- 'D:/Temp/Test.feather'

dt <- data.table(Row=1:10, 
                 NoTZ = as.POSIXct('2021-10-22'), 
                 TZBris = as.POSIXct('2021-10-22', tz = 'Australia/Brisbane'),
                 TZNY = as.POSIXct('2021-10-22', tz = 'America/New_York'))

dt[, `:=`(NoTZ = NoTZ + hours(Row-1),
          TZBris = TZBris + hours(Row-1),
          TZNY = TZNY + hours(Row-1))]

write_parquet(dt, outputFile)
write_feather(dt, outputFileFeather)
import pandas as pd

dfp = pd.read_parquet('d:/Temp/Test.parquet')
dff = pd.read_feather('d:/Temp/Test.feather')

print(dfp)
print(dff)

可以看到,第一列是时区不可知的,但已调整为UTC。第二个和第三个演示了正确的值可以通过,但只能通过使用时区感知/设置值。我想实现的是第一个专栏是不可知论的,未经调整的。我也希望能够去R->;Python或Python->;以这种方式重新定义

   Row                NoTZ                    TZBris                      TZNY
0    1 2021-10-21 14:00:00 2021-10-22 00:00:00+10:00 2021-10-22 00:00:00-04:00
1    2 2021-10-21 15:00:00 2021-10-22 01:00:00+10:00 2021-10-22 01:00:00-04:00
2    3 2021-10-21 16:00:00 2021-10-22 02:00:00+10:00 2021-10-22 02:00:00-04:00
3    4 2021-10-21 17:00:00 2021-10-22 03:00:00+10:00 2021-10-22 03:00:00-04:00
4    5 2021-10-21 18:00:00 2021-10-22 04:00:00+10:00 2021-10-22 04:00:00-04:00
5    6 2021-10-21 19:00:00 2021-10-22 05:00:00+10:00 2021-10-22 05:00:00-04:00
6    7 2021-10-21 20:00:00 2021-10-22 06:00:00+10:00 2021-10-22 06:00:00-04:00
7    8 2021-10-21 21:00:00 2021-10-22 07:00:00+10:00 2021-10-22 07:00:00-04:00
8    9 2021-10-21 22:00:00 2021-10-22 08:00:00+10:00 2021-10-22 08:00:00-04:00
9   10 2021-10-21 23:00:00 2021-10-22 09:00:00+10:00 2021-10-22 09:00:00-04:00
   Row                NoTZ                    TZBris                      TZNY
0    1 2021-10-21 14:00:00 2021-10-22 00:00:00+10:00 2021-10-22 00:00:00-04:00
1    2 2021-10-21 15:00:00 2021-10-22 01:00:00+10:00 2021-10-22 01:00:00-04:00
2    3 2021-10-21 16:00:00 2021-10-22 02:00:00+10:00 2021-10-22 02:00:00-04:00
3    4 2021-10-21 17:00:00 2021-10-22 03:00:00+10:00 2021-10-22 03:00:00-04:00
4    5 2021-10-21 18:00:00 2021-10-22 04:00:00+10:00 2021-10-22 04:00:00-04:00
5    6 2021-10-21 19:00:00 2021-10-22 05:00:00+10:00 2021-10-22 05:00:00-04:00
6    7 2021-10-21 20:00:00 2021-10-22 06:00:00+10:00 2021-10-22 06:00:00-04:00
7    8 2021-10-21 21:00:00 2021-10-22 07:00:00+10:00 2021-10-22 07:00:00-04:00
8    9 2021-10-21 22:00:00 2021-10-22 08:00:00+10:00 2021-10-22 08:00:00-04:00
9   10 2021-10-21 23:00:00 2021-10-22 09:00:00+10:00 2021-10-22 09:00:00-04:00

Tags: testdataaslibrarydttabletemptz
1条回答
网友
1楼 · 发布于 2024-10-03 13:19:47

正如您所遇到的,处理时区原始时间戳以及如何解释、存储和显示这些时间戳存在复杂性

(基本)R对""时区的处理

这里的一个问题是,(base)R以有趣的方式处理""时区。正如您所经历的“这是一个将显示为在您的本地时区中的日期,但默认情况下时区已设置为“””。R不仅以这种方式显示时间戳,而且考虑到R会话本地时区(在从字符串转换为POSIXct时),它实际上将其存储为整数(从历元算起的秒数):

> as.POSIXct("2021-10-22")
[1] "2021-10-22 CDT"
> as.integer(as.POSIXct("2021-10-22"))
[1] 1634878800
>
> Sys.setenv(TZ="UTC")
>
> as.POSIXct("2021-10-22")
[1] "2021-10-22 UTC"
> as.integer(as.POSIXct("2021-10-22"))
[1] 1634860800
> 

如果您像您所显示的那样,主动设置带有as.POSIXct()的时区,则存储的整数不会根据您的本地会话时区而更改(即,存储的整数值在更改会话时区之前和之后相同):

> as.POSIXct("2021-10-22", tz = "UTC")
[1] "2021-10-22 UTC"
> as.integer(as.POSIXct("2021-10-22", tz = "UTC"))
[1] 1634860800
> as.POSIXct("2021-10-22", tz = "Austrlaia/Brisbane")
[1] "2021-10-22 GMT"
There were 13 warnings (use warnings() to see them)
> as.POSIXct("2021-10-22", tz = "Australia/Brisbane")
[1] "2021-10-22 AEST"
> as.integer(as.POSIXct("2021-10-22", tz = "Australia/Brisbane"))
[1] 1634824800
>
> Sys.setenv(TZ="UTC")
>
> as.POSIXct("2021-10-22", tz = "UTC")
[1] "2021-10-22 UTC"
> as.integer(as.POSIXct("2021-10-22", tz = "UTC"))
[1] 1634860800
> as.POSIXct("2021-10-22", tz = "Australia/Brisbane")
[1] "2021-10-22 AEST"
> as.integer(as.POSIXct("2021-10-22", tz = "Australia/Brisbane"))
[1] 1634824800

事实上,这甚至会导致一些有趣的行为,即在不使用时区的情况下保存时间戳并更改会话时区会返回不同的值:

> ts <- as.POSIXct("2021-10-22")
> ts
[1] "2021-10-22 CDT"
> Sys.setenv(TZ = "UTC")
> ts
[1] "2021-10-22 05:00:00 UTC"
> Sys.setenv(TZ = "Australia/Brisbane")
> ts
[1] "2021-10-22 15:00:00 AEST"

因此,您可以考虑R使用本地时区从字符串转换为历元,以及when ^{} is used, it uses the local timezone(尽管它没有将其设置为tzone属性中的时区!)

与Arrow的交互

当arrow包将这些列转换为arrow格式时,它会识别出没有指定时区,因此会写入时区初始时间戳(并像往常一样使用整数值)。从the C++ docs which both the R package and Python packages use under the hood:

If a TimestampType is constructed without a timezone (or, equivalently, if the timezone supplied is an empty string) then the resulting Arrow field (column) is considered “timezone-naive”. The producer of a timezone-naive column may populate its constituent integer arrays with datetime values from any timezone; the consumer of a timezone-naive column should make no assumptions about the interoperability or comparability of the values of such a column with those of any other timestamp column or datetime value.

还有the pyarrow docs also describe something similar虽然有人指出实际措辞令人困惑,所以我不在这里发布。我们正在努力改进这一点

请注意,在R中查看箭头数组本身时,您将看到Python中的内容(即,时间戳将包括基于本地时区的2021-10-22的偏移量,因为R中的值被解释为UTC,即使调用as.POSIXct()时它实际上是由R与本地时区转换的)没有时区):

> library(arrow, warn.conflicts = FALSE)
> df <- data.frame(NoTZ = as.POSIXct("2021-10-22"))
> tab <- Table$create(df)
> tab$NoTZ
ChunkedArray
[
  [
    2021-10-22 05:00:00.000000
  ]
]

如何解决这个问题

  1. 您可以将R会话时区设置为UTC,这样当R将字符串转换为时间戳时,它将在UTC进行转换(箭头将隐式解释时区原始时间戳):
> library(arrow, warn.conflicts = FALSE)
> Sys.setenv(TZ = "UTC")
> df <- data.frame(NoTZ = as.POSIXct("2021-10-22"))
> tab <- Table$create(df)
> tab$NoTZ
ChunkedArray
[
  [
    2021-10-22 00:00:00.000000
  ]
]
  1. 使用实际日期类型存储数据。问题标题指的是日期,但代码使用的是时间戳。如果您只关心日期而不关心时间戳,那么这个问题就会消失:
> library(arrow, warn.conflicts = FALSE)
> df <- data.frame(NoTZ = as.Date("2021-10-22"))
> tab <- Table$create(df)
> tab$NoTZ
ChunkedArray
[
  [
    2021-10-22
  ]
]
  1. 如果您以日期开始,但确实需要时间戳部分(在00:00:00),您还可以执行as.POSIXct(as.Date("2021-10-22")),这将返回一个时间戳值,就像字符串在UTC时被解释一样:
> as.POSIXct(as.Date("2021-10-22"))
[1] "2021-10-21 19:00:00 CDT"
> as.integer(as.POSIXct(as.Date("2021-10-22")))
[1] 1634860800
> Sys.setenv(TZ = "UTC")
> as.POSIXct(as.Date("2021-10-22"))
[1] "2021-10-22 UTC"
> as.integer(as.POSIXct(as.Date("2021-10-22")))
[1] 1634860800

并使用箭头:

> library(arrow, warn.conflicts = FALSE)
> df <- data.frame(NoTZ = as.POSIXct(as.Date("2021-10-22")))
> tab <- Table$create(df)
> tab$NoTZ
ChunkedArray
[
  [
    2021-10-22 00:00:00.000000
  ]
]
  1. 如果您知道时区,您可以设置它们(尽管我承认这不利于存储时区不可知的时间戳)。如果数据确实包含时间戳,并且这些时间戳具有已知的时区,那么由于这样的假设和解释的差异,这将导致最小的意外

相关问题 更多 >