什么是“POSIX"
R语言中有两个时间类对象,POSIXct和POSIXlt。对于我这种非专业的人员来说,看到这个名称是懵的,不知道这是什么单词的首字母缩写。在网上搜了一下才知道。“POSIX”的全称为“Portable Operating System Interface of UNIX” 翻译成中文是“可移植操作系统接口”,POSIX标准定义了操作系统应该为应用程序提供的接口标准。
“POSIX”是一个IEEE的标准。UNIX系统出来后,出现了各种不同的版本。而计算机程序在不同版本的系统见运行时,出现不兼容的问题。为了解决计算机程序在不同UNIX系统之间的可移植性问题,在1980s诞生了“POSIX”。POSIX规定了一个计算机程序与UNIX系统之间交互的标准。
在R中,POSIXct和POSIXlt这两个时间类的名称中,POSIX可以看做是一个标签。也就是符合POSIX标准的时间。可以称为“POSIX时间”或者“UNIX时间”。它的定义是从格林威治时间的1970年01月01日00时00分00秒起至现在的总秒数。所以,在R中,如果我们输入
as.numeric(as.POSIXlt("2022-5-1 00:00:00"))
将会得到一个数值
1651334400
如果我们增加1秒,输入
as.numeric(as.POSIXlt("2022-5-1 00:00:01"))
得到的数值变为
1651334401
POSIXct和POSIXlt的区别
POSIXct和POSIXlt的lt和ct分别表示“local time”和“calendar time”,是什么意思呢?
- calendar time是指我们通常意义理解的时间,即公历的多少年多少月多少日
举个例子看一下:
在R中,输入a <- as.POSIXlt("2022-5-1 00:00:00")
,然后查看a的类型mode(a)
,得到的输出是
"list"
输入b <- as.POSIXct("2022-5-1 00:00:00")
,然后查看a的类型mode(b)
,得到的输出是
"numeric"
也就是说,如果时间是一个POSIXlt类,它的数据结构是list(列表),如果时间是一个POSIXct类,它的数据结构是数字向量。
POSIXlt
POSIXlt类型的日期含有以下列表元素:
sec
0–61: seconds.
min
0–59: minutes.
hour
0–23: hours.
mday
1–31: day of the month
mon
0–11: months after the first of the year.
year
years since 1900.
wday
0–6 day of the week, starting on Sunday.
yday
0–365: day of the year (365 only in leap years).
isdst
Daylight Saving Time flag. Positive if in force, zero if not, negative if unknown.
zone
(Optional.) The abbreviation for the time zone in force at that time: “” if unknown (but “” might also be used for UTC).
gmtoff
(Optional.) The offset in seconds from GMT: positive values are East of the meridian. Usually NA if unknown, but 0 could mean unknown.
所以对于一个POSIXlt的时间,可以通过$来引用它的某个属性,比如:
R命令 | 输出结果 | 意义 |
---|---|---|
a$yday | 120 | 是一年中的第120天。 |
a$zone | “CST” | 时区是“中国标准时间” |
a$isdst | 0 | 不使用夏令时 |
a$gmtoff | NA | 该时区与GMT时区的时间差未知 |
注:CST可以为如下4个不同的时区的缩写:
- 美国中部时间:Central Standard Time (USA) UT-6:00
- 澳大利亚中部时间:Central Standard Time (Australia) UT+9:30
- 中国标准时间:China Standard Time UT+8:00
- 古巴标准时间:Cuba Standard Time UT-4:00
在这里应该是指中国标准时间。
POSIXlt类的属性
> attributes(a)
$names
[1] "sec" "min" "hour" "mday" "mon" "year" "wday" "yday" "isdst"
[10] "zone" "gmtoff"
$class
[1] "POSIXlt" "POSIXt"
$tzone
[1] "Asia/Shanghai"
可见,POSIXlt类有3个属性,分别是names
、class
和tzone
。
关于时区
查看自己计算机R运行环境的时区,可以
Sys.timezone()
或Sys.getenv("TZ")
如果返回值是空值,则说明你还没有设置时区。通过Sys.setenv(TZ ="Asia/Shanghai")
设置时区,再Sys.getenv("TZ")
则得到
"Asia/Shanghai"
。其中,Asia/Shanghai是时区的ID,不同时区的ID可以搜“国际时区ID列表”查到。
此时,我们再看
a <- as.POSIXlt("2022-5-1 00:00:00")
POSIXct
POSIXct就是一个长度为1的向量,存储的是自原点以来的秒数。但POSIXct虽然可以进行加减运算,但不能进行乘除运算。这提醒我们,POSIXct存储的仍然不是一个数值型数据,而是POSIXct型。
> a <- as.POSIXct("2022-5-1 00:00:00")
> attributes(a)
$class
[1] "POSIXct" "POSIXt"
$tzone
[1] ""
可见,POSIXct类型的时间只有class
和tzone
两个属性。
有趣的实验
运行下列命令以及得到的结果
> a <- as.POSIXlt("2022-5-1 00:00:00")
> a$gmtoff
[1] NA
> a <- as.POSIXlt(as.POSIXct("2022-5-1 00:00:00"))
> a$gmtoff
[1] 28800
> attr(a,"tzone")
[1] "Asia/Shanghai" "CST" "CDT" #不知道这个"CDT" 是什么
可以看到,将时间字符串转换为时间类时,如果直接转换为POSIXlt类型,则其gmtoff内容为NA;如果先转换为POSIXct,再转换为POSIXlt则其gmtoff内容不是空的。28800 秒正好就是8小时,也就是时区Asia/Shanghai时间与UTC的时间差。
事实上,正是这个问题导致我要更深入了解POSIXct和POSIXlt的区别。在比较两个POSIXlt类型的时间元素时(假设是a和b),我发现a==b
返回的结果是TRUE
,但setequal(a,b)
得到的结果就是FALSE
,这给我带来了极大的困惑。我想,正在表面上a和b完全一样的背后肯定有隐藏的东西我还没看到。现在我才知道,原来是其中一个时间是经过了POSIXct转换到POSIXlt格式的,而另外一个时间是直接转换为POSIXlt的。他们两个的差就在gmtoff这里。通过在网上搜索学习,解决这个问题,我还是挺欣慰的。当然,这种无知在R高手看来可能有点儿可笑。
问题解决了,我也不想再深入了,浅尝辄止。遇到新的问题再说吧。
更多参考
https://wrangle-r.rsquaredacademy.com/date-and-time-in-r.html