11.6 lubridate

如果你对日期感到有些灰心,正想跳过本章的其余部分,不要害怕,因为救星来了!lubridate,正如其名,它为日期处理的过程增加了急需的润滑剂。它并没有为R添加许多新功能,但它使代码更具可读性,并免去你很多不必要的烦恼。

为了取代strptimelubridate有多种使用了预设格式的解析函数。ymd接受年、月、日的日期形式。规范上有一定的灵活性:一些基本的分隔符都能使用,如连字符、正反斜杠、冒号和空格4;月份可用数字、完整的英文名或缩写名称来标注;也可以使用星期几,但不是强制的。它真正的优点在于,相同向量中的不同元素可用不同的格式(只要年月日的先后顺序不变):

4事实上,大部分的标点符号都是允许的。

  1. library(lubridate)
  2. ## Attaching package: 'lubridate'
  3. ## The following object is masked from 'package:chron':
  4. ##
  5. ## days, hours, minutes, seconds, years
  6. john_harrison_birth_date <- c( # 它发明了潜艇中的航海天文钟
  7. "1693-03 24",
  8. "1693/03\\24",
  9. "Tuesday+1693.03*24"
  10. )
  11. ymd(john_harrison_birth_date) #所有都一样
  12. ## [1] "1693-03-24 UTC" "1693-03-24 UTC" "1693-03-24 UTC"

非常重要的是,请记住ymd要以正确的顺序获取日期。如果你的日期数据的形式有所不同,可以使用lubridate提供的其他函数(ydmmdymyddmydym)。这些函数都有相关的函数用于指定特定的时间格式,如ymd_hymd_hmymd_hms,以及另五个以其他顺序出现的日期函数。如果你的日期不在以上任何一种格式中,则使用更低级的函数parse_date_time来实现。

lubridate中的所有解析函数都会返回POSIXct日期,默认都使用UTC时区。警告:这些行为与R中的基本函数strptime不同!(尽管通常使用起来会更方便。)在lubridate的术语中,这些个别日期是“瞬间”(instant)。

lubridate提供了stamp函数来格式化日期,使你以更可读的方式指定格式。当指定一个日期,它会返回一个用于日期格式化的函数:

  1. date_format_function <-
  2. stamp("A moon landing occurred on Monday 01 January 1900 at 18:00:00.")
  3. ## Multiple formats matched: "A moon landing occurred on %A %m January %d%y
  4. ## at %H:%M:%OS"(1), "A moon landing occurred on %A %m January %Y at
  5. ## %d:%H:%M."(1), "A moon landing occurred on %A %d %B %Y at %H:%M:%S."(1)
  6. ## Using: "A moon landing occurred on %A %d %B %Y at %H:%M:%S."
  7. date_format_function(moon_landings_lt)
  8. ## [1] "A moon landing occurred on Sunday 20 July 1969 at 20:17:40."

lubridate有三种不同类型的变量可用于时间范围的处理。“持续时间”(Duration)指定的时间跨度为秒的倍数,所以一天的总时间是86400秒(60×60×24),一年的总时间是3156000秒(86400×365)。对于间隔均匀的时间来说,指定日期范围会很容易,但闰年和夏令时把问题复杂化了。在下例中,请注意每个闰年会使日期向后倒退一天。today将给出今天的日期:

  1. (duration_one_to_ten_years <- dyears(1:10))
  2. ## [1] "31536000s (~365 days)" "63072000s (~730 days)"
  3. ## [3] "94608000s (~1095 days)" "126144000s (~1460 days)"
  4. ## [5] "157680000s (~1825 days)" "189216000s (~2190 days)"
  5. ## [7] "220752000s (~2555 days)" "252288000s (~2920 days)"
  6. ## [9] "283824000s (~3285 days)" "315360000s (~3650 days)"
  7. today() + duration_one_to_ten_years
  8. ## [1] "2014-07-17" "2015-07-17" "2016-07-16" "2017-07-16" "2018-07-16"
  9. ## [6] "2019-07-16" "2020-07-15" "2021-07-15" "2022-07-15" "2023-07-15"

其他用于创建持续时间的函数为dsecondsdminutes等,对于混合组分的规范(mixed-component specification)则使用new_duration

“周期”(period)根据时钟上的时间来指定时间跨度。这意味着,在把它们添加到一个瞬间之前,它们确切的时间跨度是看不出来的。例如,一年的周期可以是365或366天,这取决于它是否是闰年。在下例中,请注意日期在闰年中保持不变:

  1. (period_one_to_ten_years <- years(1:10))
  2. ## [1] "1y 0m 0d 0H 0M 0S" "2y 0m 0d 0H 0M 0S" "3y 0m 0d 0H 0M 0S"
  3. ## [4] "4y 0m 0d 0H 0M 0S" "5y 0m 0d 0H 0M 0S" "6y 0m 0d 0H 0M 0S"
  4. ## [7] "7y 0m 0d 0H 0M 0S" "8y 0m 0d 0H 0M 0S" "9y 0m 0d 0H 0M 0S"
  5. ## [10] "10y 0m 0d 0H 0M 0S"
  6. today() + period_one_to_ten_years
  7. ## [1] "2014-07-17" "2015-07-17" "2016-07-17" "2017-07-17" "2018-07-17"
  8. ## [6] "2019-07-17" "2020-07-17" "2021-07-17" "2022-07-17" "2023-07-17"

除了years,还可以使用secondsminutes以及 new_period(对混合组分规范)等来创建周期。

“间隔”(interval)定义为某段具有开始和结束的时间。它们本身用处不大,最常用于指定持续时间和周期,当你已知开始和结束日期(而非持续的时间)时 。它们也可用于持续时间和周期之间的转换。例如,如果要将一年的持续时间直接转换为周期只能大约估计,因为一年可能是365或366天(可能再加上一些闰秒,如果是夏令时变化则要加减一、两个小时):

  1. a_year <- dyears(1) # 刚好是60*60*24*365秒
  2. as.period(a_year) # 只是一个估计
  3. ## estimate only: convert durations to intervals for accuracy
  4. ## [1] "1y 0m 0d 0H 0M 0S"

当起始(或结束)时间的日期已知时,我们可以使用interval和一个媒介把持续时间转换为周期,例如:

  1. start_date <- ymd("2016-02-28")
  2. (interval_over_leap_year <- new_interval(
  3. start_date,
  4. start_date + a_year
  5. ))
  6. ## [1] 2016-02-28 UTC--2017-02-27 UTC
  7. as.period(interval_over_leap_year)
  8. ## [1] "11m 30d 0H 0M 0S"

间隔也有一些便于使用的操作符,即用于定义间隔的%--%,以及用于检查日期是否包含在间隔之内的%within%

  1. ymd("2016-02-28") %--% ymd("2016-03-01") # 另一种指定间隔的方式
  2. ## [1] 2016-02-28 UTC--2016-03-01 UTC
  3. ymd("2016-02-29") %within% interval_over_leap_year
  4. ## [1] TRUE

对于时区的处理,with_tz允许你改变日期的时区而无须把它打印出来(这与strftime不同)。它还能够正确地处理POSIXlt日期(这也与strftime不同):

  1. with_tz(now_lt, tz = "America/Los_Angeles")
  2. ## [1] "2013-07-17 14:47:01 PDT"
  3. with_tz(now_lt, tz = "Africa/Brazzaville")
  4. ## [1] "2013-07-17 22:47:01 WAT"
  5. with_tz(now_lt, tz = "Asia/Kolkata")
  6. ## [1] "2013-07-18 03:17:01 IST"
  7. with_tz(now_lt, tz = "Australia/Adelaide")
  8. ## [1] "2013-07-18 07:17:01 CST"

force_tzwith_tz的一个变种,用于更新不正确的时区。

olson_time_zones将以字母或经度顺序返回R所知道的所有Olson风格的时区名字列表:

  1. head(olson_time_zones())
  2. ## [1] "Africa/Abidjan" "Africa/Accra" "Africa/Addis_Ababa"
  3. ## [4] "Africa/Algiers" "Africa/Asmara" "Africa/Bamako"
  4. head(olson_time_zones("longitude"))
  5. ## [1] "Pacific/Midway" "America/Adak" "Pacific/Chatham"
  6. ## [4] "Pacific/Wallis" "Pacific/Tongatapu" "Pacific/Enderbury"

还一些其他的实用函数可用于日期的算术运算,尤其是floor_dateceiling_date

  1. floor_date(today(), "year")
  2. ## [1] "2013-01-01"
  3. ceiling_date(today(), "year")
  4. ## [1] "2014-01-01"