11.6 lubridate
如果你对日期感到有些灰心,正想跳过本章的其余部分,不要害怕,因为救星来了!lubridate
,正如其名,它为日期处理的过程增加了急需的润滑剂。它并没有为R添加许多新功能,但它使代码更具可读性,并免去你很多不必要的烦恼。
为了取代strptime
,lubridate
有多种使用了预设格式的解析函数。ymd
接受年、月、日的日期形式。规范上有一定的灵活性:一些基本的分隔符都能使用,如连字符、正反斜杠、冒号和空格4;月份可用数字、完整的英文名或缩写名称来标注;也可以使用星期几,但不是强制的。它真正的优点在于,相同向量中的不同元素可用不同的格式(只要年月日的先后顺序不变):
4事实上,大部分的标点符号都是允许的。
library(lubridate)
## Attaching package: 'lubridate'
## The following object is masked from 'package:chron':
##
## days, hours, minutes, seconds, years
john_harrison_birth_date <- c( # 它发明了潜艇中的航海天文钟
"1693-03 24",
"1693/03\\24",
"Tuesday+1693.03*24"
)
ymd(john_harrison_birth_date) #所有都一样
## [1] "1693-03-24 UTC" "1693-03-24 UTC" "1693-03-24 UTC"
非常重要的是,请记住ymd
要以正确的顺序获取日期。如果你的日期数据的形式有所不同,可以使用lubridate
提供的其他函数(ydm
、mdy
、myd
、dmy
和dym
)。这些函数都有相关的函数用于指定特定的时间格式,如ymd_h
、ymd_hm
和ymd_hms
,以及另五个以其他顺序出现的日期函数。如果你的日期不在以上任何一种格式中,则使用更低级的函数parse_date_time
来实现。
lubridate
中的所有解析函数都会返回POSIXct
日期,默认都使用UTC时区。警告:这些行为与R中的基本函数strptime
不同!(尽管通常使用起来会更方便。)在lubridate
的术语中,这些个别日期是“瞬间”(instant)。
lubridate
提供了stamp
函数来格式化日期,使你以更可读的方式指定格式。当指定一个日期,它会返回一个用于日期格式化的函数:
date_format_function <-
stamp("A moon landing occurred on Monday 01 January 1900 at 18:00:00.")
## Multiple formats matched: "A moon landing occurred on %A %m January %d%y
## at %H:%M:%OS"(1), "A moon landing occurred on %A %m January %Y at
## %d:%H:%M."(1), "A moon landing occurred on %A %d %B %Y at %H:%M:%S."(1)
## Using: "A moon landing occurred on %A %d %B %Y at %H:%M:%S."
date_format_function(moon_landings_lt)
## [1] "A moon landing occurred on Sunday 20 July 1969 at 20:17:40."
lubridate
有三种不同类型的变量可用于时间范围的处理。“持续时间”(Duration)指定的时间跨度为秒的倍数,所以一天的总时间是86400秒(60×60×24),一年的总时间是3156000秒(86400×365)。对于间隔均匀的时间来说,指定日期范围会很容易,但闰年和夏令时把问题复杂化了。在下例中,请注意每个闰年会使日期向后倒退一天。today
将给出今天的日期:
(duration_one_to_ten_years <- dyears(1:10))
## [1] "31536000s (~365 days)" "63072000s (~730 days)"
## [3] "94608000s (~1095 days)" "126144000s (~1460 days)"
## [5] "157680000s (~1825 days)" "189216000s (~2190 days)"
## [7] "220752000s (~2555 days)" "252288000s (~2920 days)"
## [9] "283824000s (~3285 days)" "315360000s (~3650 days)"
today() + duration_one_to_ten_years
## [1] "2014-07-17" "2015-07-17" "2016-07-16" "2017-07-16" "2018-07-16"
## [6] "2019-07-16" "2020-07-15" "2021-07-15" "2022-07-15" "2023-07-15"
其他用于创建持续时间的函数为dseconds
、dminutes
等,对于混合组分的规范(mixed-component specification)则使用new_duration
。
“周期”(period)根据时钟上的时间来指定时间跨度。这意味着,在把它们添加到一个瞬间之前,它们确切的时间跨度是看不出来的。例如,一年的周期可以是365或366天,这取决于它是否是闰年。在下例中,请注意日期在闰年中保持不变:
(period_one_to_ten_years <- years(1:10))
## [1] "1y 0m 0d 0H 0M 0S" "2y 0m 0d 0H 0M 0S" "3y 0m 0d 0H 0M 0S"
## [4] "4y 0m 0d 0H 0M 0S" "5y 0m 0d 0H 0M 0S" "6y 0m 0d 0H 0M 0S"
## [7] "7y 0m 0d 0H 0M 0S" "8y 0m 0d 0H 0M 0S" "9y 0m 0d 0H 0M 0S"
## [10] "10y 0m 0d 0H 0M 0S"
today() + period_one_to_ten_years
## [1] "2014-07-17" "2015-07-17" "2016-07-17" "2017-07-17" "2018-07-17"
## [6] "2019-07-17" "2020-07-17" "2021-07-17" "2022-07-17" "2023-07-17"
除了years
,还可以使用seconds
、minutes
以及 new_period
(对混合组分规范)等来创建周期。
“间隔”(interval)定义为某段具有开始和结束的时间。它们本身用处不大,最常用于指定持续时间和周期,当你已知开始和结束日期(而非持续的时间)时 。它们也可用于持续时间和周期之间的转换。例如,如果要将一年的持续时间直接转换为周期只能大约估计,因为一年可能是365或366天(可能再加上一些闰秒,如果是夏令时变化则要加减一、两个小时):
a_year <- dyears(1) # 刚好是60*60*24*365秒
as.period(a_year) # 只是一个估计
## estimate only: convert durations to intervals for accuracy
## [1] "1y 0m 0d 0H 0M 0S"
当起始(或结束)时间的日期已知时,我们可以使用interval
和一个媒介把持续时间转换为周期,例如:
start_date <- ymd("2016-02-28")
(interval_over_leap_year <- new_interval(
start_date,
start_date + a_year
))
## [1] 2016-02-28 UTC--2017-02-27 UTC
as.period(interval_over_leap_year)
## [1] "11m 30d 0H 0M 0S"
间隔也有一些便于使用的操作符,即用于定义间隔的%--%
,以及用于检查日期是否包含在间隔之内的%within%
:
ymd("2016-02-28") %--% ymd("2016-03-01") # 另一种指定间隔的方式
## [1] 2016-02-28 UTC--2016-03-01 UTC
ymd("2016-02-29") %within% interval_over_leap_year
## [1] TRUE
对于时区的处理,with_tz
允许你改变日期的时区而无须把它打印出来(这与strftime
不同)。它还能够正确地处理POSIXlt
日期(这也与strftime
不同):
with_tz(now_lt, tz = "America/Los_Angeles")
## [1] "2013-07-17 14:47:01 PDT"
with_tz(now_lt, tz = "Africa/Brazzaville")
## [1] "2013-07-17 22:47:01 WAT"
with_tz(now_lt, tz = "Asia/Kolkata")
## [1] "2013-07-18 03:17:01 IST"
with_tz(now_lt, tz = "Australia/Adelaide")
## [1] "2013-07-18 07:17:01 CST"
force_tz
是with_tz
的一个变种,用于更新不正确的时区。
olson_time_zones
将以字母或经度顺序返回R所知道的所有Olson风格的时区名字列表:
head(olson_time_zones())
## [1] "Africa/Abidjan" "Africa/Accra" "Africa/Addis_Ababa"
## [4] "Africa/Algiers" "Africa/Asmara" "Africa/Bamako"
head(olson_time_zones("longitude"))
## [1] "Pacific/Midway" "America/Adak" "Pacific/Chatham"
## [4] "Pacific/Wallis" "Pacific/Tongatapu" "Pacific/Enderbury"
还一些其他的实用函数可用于日期的算术运算,尤其是floor_date
和ceiling_date
:
floor_date(today(), "year")
## [1] "2013-01-01"
ceiling_date(today(), "year")
## [1] "2014-01-01"