5.2 lubridate

lubridate包是对Base R中POSIXct类的封装。所以无论从函数名还是功能等方面,lubridate包中的函数功能更加清晰明了。从获取当前日期、时间,解析时间日期中的年、月、日、星期,计算年月间隔天数等常用的时间日期功能,lubridate包中都有相对应的功能函数。

在处理日期时间数据时,我常用lubridate解决,本节将介绍包中部分函数用法。

5.2.1 安装包

install.packages("tidyverse")
# 仅仅只安装lubridate
install.packages('lubridate')
# 开发版
devtools::install_github("tidyverse/lubridate")
# 加载包
library(lubridate,warn.conflicts = FALSE)

5.2.2 当前时间日期

  • now函数

now()函数是当前时间,只有一个参数tzone,默认为系统的timezone。

now()
#> [1] "2021-06-23 18:15:26 CST"
# now(tzone = 'Asia/Shanghai')
# base R
base::Sys.time()
#> [1] "2021-06-23 18:15:26 CST"
  • today函数

时区同样默认为系统的timezone。

today(tzone = 'Asia/Shanghai')
#> [1] "2021-06-23"
#base R
base::Sys.Date()
#> [1] "2021-06-23"

5.2.3 构造日期时间

使用数值直接创建日期时间。

函数make_date()make_datetime()函数默认时区10为“UTC”。

make_date(year = 2021, month = 5, day = 1, tz = "Asia/Shanghai")

make_datetime(
  year = 1970L,
  month = 1L,
  day = 1L,
  hour = 0L,
  min = 0L,
  sec = 0,
  tz = "Asia/Shanghai"
)
  • make_datetime
make_datetime(
  year = year(today()),
  month = month(today()),
  day = day(today()),
  hour = hour(now()),
  min = minute(now()),
  sec = second(now()),
  tz = "asia/shanghai"
)
#> [1] "2021-06-23 18:15:26 CST"
  • as_datetime
as_datetime('2020-01-09 09:15:40',tz='asia/shanghai')
#> [1] "2020-01-09 09:15:40 CST"
as_date('2020-01-09') #ymd格式
#> [1] "2020-01-09"
# same above
#as_date('2020/01/09')
#as_date('20200109')

5.2.4 解析日期时间

数据源中日期列可能是各种的字符形式,需要转换为时间格式方便进行日期计算。商业环境中的数据是混乱的,生产库可能是不同的数据库系统,导致时间日期格式混乱,如果公司没有统一的用户层数据源,我们就需要自己清洗数据,将不同形式的日期格式转化为标准格式。

  • 解析日期
# 整数和字符都可以
ymd(20200604) 
#> [1] "2020-06-04"
ymd('20200604')
#> [1] "2020-06-04"
mdy(06042020)
#> [1] "2020-06-04"
dmy(04062020)
#> [1] "2020-06-04"
  • 解析时间
ymd_hm("20100201 07-01", "20100201 07-1", "20100201 7-01")
#> [1] "2010-02-01 07:01:00 UTC" "2010-02-01 07:01:00 UTC"
#> [3] "2010-02-01 07:01:00 UTC"
ymd_hms("2013-01-24 19:39:07")
#> [1] "2013-01-24 19:39:07 UTC"

当需要处理unix时间戳时应.POSIXct()函数转化.

unix在线转换

.POSIXct(1591709615)
#> [1] "2020-06-09 21:33:35 CST"
ymd_hms(.POSIXct(1591709615))
#> [1] "2020-06-09 21:33:35 UTC"

在使用unix时间戳转换时一定注意R环境和数据系统环境时区是否一直。

曾经我在使用阿里云的RDS数据库时没注意时区差异,导致我清洗出来的时间数据错误。

ymd_hms(.POSIXct(1591709615),tz = 'Asia/Shanghai')
#> [1] "2020-06-09 21:33:35 CST"

5.2.5 提取日期时间成分

#获取年
year(now())  
#> [1] 2021
#获取月
month(now())
#> [1] 6
# 当前时间所在年份天数
yday(now())
#> [1] 174
# 当前时间所在月天数
mday(now())
#> [1] 23
# 周几
wday(now(),label = TRUE,week_start = 1)
#> [1] 周三
#> Levels: 周一 < 周二 < 周三 < 周四 < 周五 < 周六 < 周日
# 所在时刻
hour(now())
#> [1] 18
# 所在时刻
minute(now())
#> [1] 15
# 所在时刻
second(now())
#> [1] 27

5.2.6 处理时区

数据时区与本地R环境一致时,数据中的时区没必要处理,但是当数据是跨时区的或者不同生产系统的时区不一致,我们需要将数据时区处理一致。

1.时区查看

时区和所用系统设置相关

Sys.timezone()
#> [1] "Asia/Taipei"
# windows 系统默认的时区 中国台北
# linux 上是"Asia/Shanghai"
# mac 上是"Asia/Shanghai"

这里还有一个奇怪的点,Windows系统下时区设置为(UTC+08:00)北京,重庆,香港特别行政区,乌鲁木齐,但是R返回的时区是Asia/Taipei

now()
#> [1] "2021-06-23 18:15:27 CST"

now()输出结果中,CST是时区概念。

CST可以同时代表四个时间

  • 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

2.时区调整

lubridate中用with_tz()force_tz()处理时区问题

time <- ymd_hms("2020-12-13 15:30:30")
time
#> [1] "2020-12-13 15:30:30 UTC"

# Changes printing
with_tz(time, "Asia/Shanghai")
#> [1] "2020-12-13 23:30:30 CST"
# Changes time
force_tz(time, "Asia/Shanghai")
#> [1] "2020-12-13 15:30:30 CST"
  1. 时区差异

从下面三个时间观察时区,CST时间:中央标准时间;UTC时间:世界协调时间(UTC)是世界上不同国家用来调节时钟和时间的主要时间标准。

如:当UTC时间为0点时,中国CST时间为8点,因为零时区和中国北京时区相差8个时区.

lubridate::now() # now函数调用系统默认时区
#> [1] "2021-06-23 18:15:27 CST"
as_datetime(now()) #as_datetime默认是UTC
#> [1] "2021-06-23 10:15:27 UTC"
as_datetime(now(),tz = 'asia/shanghai')
#> [1] "2021-06-23 18:15:27 CST"

5.2.7 时间间隔

lubridate中将时间间隔保存为interveal类对象。

arrive <- ymd_hms("2020-12-04 12:00:00", tz = "asia/shanghai")
arrive
#> [1] "2020-12-04 12:00:00 CST"

leave <- ymd_hms("2020-12-10 14:00:00", tz = "asia/shanghai")
leave
#> [1] "2020-12-10 14:00:00 CST"

res <- interval(arrive, leave) 
# same above
res <- arrive %--% leave

查看类

class(res)
#> [1] "Interval"
#> attr(,"package")
#> [1] "lubridate"

两个时间间隔是否重复

jsm <- interval(ymd(20201020, tz = "asia/shanghai"), ymd(20201231, tz = "asia/shanghai"))
jsm
#> [1] 2020-10-20 CST--2020-12-31 CST
int_overlaps(jsm, res)
#> [1] TRUE

更多详细用法?interveal

interval(start = NULL, end = NULL, tzone = tz(start))

start %--% end

is.interval(x)

int_start(int)

int_start(int) <- value

int_end(int)

int_end(int) <- value

int_length(int)

int_flip(int)

int_shift(int, by)

int_overlaps(int1, int2)

int_standardize(int)

int_aligns(int1, int2)

int_diff(times)

5.2.8 时间日期计算

时间日期计算以number line为依据计算。原文是Because the timeline is not as reliable as the number line,我没理解这句话。

minutes(2)
#> [1] "2M 0S"
dminutes(2)
#> [1] "120s (~2 minutes)"
dhours(2)
#> [1] "7200s (~2 hours)"

注意闰年时计算年份的差异

leap_year(2019)
#> [1] FALSE
ymd(20190101) + dyears(1)
#> [1] "2020-01-01 06:00:00 UTC"
ymd(20190101) + years(1)
#> [1] "2020-01-01"

leap_year(2020)
#> [1] TRUE
ymd(20200101) + dyears(1)  # 注意查看闰年时的差异
#> [1] "2020-12-31 06:00:00 UTC"
ymd(20200101) + years(1)
#> [1] "2021-01-01"

lubridate中的函数都已向量化

meeting <- ymd_hms("2020-12-01 09:00:00", tz = "asia/shanghai")
meeting <- meeting + weeks(0:5)
meeting %within% jsm
#> [1]  TRUE  TRUE  TRUE  TRUE  TRUE FALSE

除法计算

res / ddays(1)
#> [1] 6.08
res / dminutes(1)
#> [1] 8760


res %/% months(1)
#> [1] 0
res %% months(1)
#> [1] 2020-12-04 12:00:00 CST--2020-12-10 14:00:00 CST

as.period用法

as.period(res %% months(1))
#> [1] "6d 2H 0M 0S"

对于日期而言,因为月天数、年天数不一致,导致不能直接加减天数,如下:

jan31 <- ymd("2020-01-31")
jan31 + months(0:11)
#>  [1] "2020-01-31" NA           "2020-03-31" NA           "2020-05-31"
#>  [6] NA           "2020-07-31" "2020-08-31" NA           "2020-10-31"
#> [11] NA           "2020-12-31"

lubridate中不存在的日期返回NA

解决方案是:%m+%%m-%

jan31 %m+% months(0:11)
#>  [1] "2020-01-31" "2020-02-29" "2020-03-31" "2020-04-30" "2020-05-31"
#>  [6] "2020-06-30" "2020-07-31" "2020-08-31" "2020-09-30" "2020-10-31"
#> [11] "2020-11-30" "2020-12-31"
jan31 %m-% months(0:11)
#>  [1] "2020-01-31" "2019-12-31" "2019-11-30" "2019-10-31" "2019-09-30"
#>  [6] "2019-08-31" "2019-07-31" "2019-06-30" "2019-05-31" "2019-04-30"
#> [11] "2019-03-31" "2019-02-28"

  1. lubridate包中大部分函数默认时区为“UTC”,在涉及时间处理时需要注意时区。↩︎