07使用 lubridate 处理日期与时间

理解 ISO 8601,并在 R 中解析、构造、提取、对齐和计算日期时间。

2026-03-01
RlubridateDateDatetimeISO 8601
本章目录 · 10

日期时间处理包含两个彼此关联的问题:数据应如何表示,以及 R 应如何解析和运算。ISO 8601 提供统一的表示方式,lubridate 则提供一组直观的日期时间工具。

ISO 8601 日期时间

ISO 8601 使用从大到小的顺序表示日期和时间,可以避免 04/05/2025 究竟是 4 月 5 日还是 5 月 4 日的歧义。

类型 示例 含义
日期 2025-04-25 年、月、日
本地日期时间 2025-04-25T14:30:00 T 分隔日期与时间
UTC 时间戳 2025-04-25T14:30:00Z Z 表示 UTC
带偏移的时间戳 2025-04-25T14:30:00+08:00 时间相对 UTC 的偏移量

存储和交换数据时,应明确保留时区信息。缺少 Z 或偏移量的日期时间只表达墙上时间,无法独立确定全球时间线上的具体时刻。

R 中的两种基本类型

只包含年月日的信息使用 Date;需要时、分、秒及时区的信息使用 POSIXct

library(lubridate)

date <- ymd("2025-04-25")
datetime <- ymd_hms("2025-04-25T14:30:00Z", tz = "UTC")

class(date)
# [1] "Date"

class(datetime)
# [1] "POSIXct" "POSIXt"

生日、采样日期和自然日通常用 Date;日志时间、事件发生时刻和跨时区数据通常用 POSIXct

解析已有文本

lubridate 的解析函数名直接表达年月日的排列顺序:

ymd("2024-04-25")
mdy("04/25/2024")
dmy("25-04-2024")

ymd_hms("2024-04-25 18:00:00")
parse_date_time("25-Apr-2024", orders = "d-b-Y")

当输入是 ISO 8601 时间戳时,显式提供时区:

ymd_hms("2025-04-25T14:30:00Z", tz = "UTC")

readr 也可以按照格式字符串解析日期时间:

library(readr)

parse_date("2025-04-25", format = "%Y-%m-%d")
parse_datetime("2025-04-25T14:30:00+08:00")

常用格式代码如下:

代码 含义 示例
%Y / %y 四位年 / 两位年 2025 / 25
%m 04
%b / %B 月份英文缩写 / 全称 Apr / April
%d 25
%H / %I 24 小时制 / 12 小时制小时 14 / 02
%p 上午或下午 PM
%M 分钟 30
%S / %OS 秒 / 带小数的秒 45 / 45.35
%Z 时区名称 UTC
%z UTC 偏移 +0800

从分列字段构造日期

数据库导出、传感器日志和整理后的数据框经常将年、月、日、小时等存成独立列。make_date() 用这些字段构造 Date

make_date(2024, 4, 25)
# [1] "2024-04-25"

make_datetime() 构造 POSIXct。未提供的小时、分钟和秒默认为零:

make_datetime(2024, 4, 25, 14, 30, tz = "UTC")
# [1] "2024-04-25 14:30:00 UTC"

在数据框中,可以一次处理整列:

library(dplyr)
library(lubridate)

df <- tibble(
  year = c(2024, 2024),
  month = c(4, 5),
  day = c(25, 10),
  hour = c(14, 9),
  minute = c(30, 15)
)

df <- df |>
  mutate(
    date = make_date(year, month, day),
    datetime = make_datetime(
      year, month, day, hour, minute, tz = "UTC"
    )
  )

只关心自然日时使用 make_date();只有确实需要一天内的时刻时才构造日期时间。

获取当前日期与时间

today()
now()

today() 返回 Datenow() 返回带时区的当前日期时间。需要可重复的分析时,不要让关键结果隐式依赖当前时间,应将分析日期作为明确输入保存下来。

提取时间组件

x <- ymd_hms("2024-04-25 18:30:45", tz = "UTC")

year(x)
month(x)
day(x)
hour(x)
minute(x)
second(x)

wday(x)
yday(x)
week(x)
quarter(x)

month(x, label = TRUE)wday(x, label = TRUE) 可以返回月份或星期标签。提取后的组件常用于分组汇总和绘图,但原始时间列仍应保留。

日期时间运算

days()hours()minutes() 等函数使固定长度的时间偏移更容易阅读:

ymd("2024-01-01") + days(3)
now() - hours(2) + minutes(30)

月份和年份并非固定秒数。涉及月末、闰年或日历规则时,应先明确想表达的是日历周期还是精确持续时间。

对齐到统计周期

按日、周或月聚合前,可以先将时间对齐:

floor_date(x, "month")
ceiling_date(x, "week")
round_date(x, "day")
  • floor_date() 向当前周期的起点取整;
  • ceiling_date() 向下一个周期边界取整;
  • round_date() 取最近的周期边界。

例如按月汇总:

events |>
  mutate(month = floor_date(event_time, "month")) |>
  count(month)

计算时间差与区间

start <- ymd_hms("2024-04-25 08:00:00", tz = "UTC")
end <- ymd_hms("2024-04-26 14:30:00", tz = "UTC")

difftime(end, start, units = "hours")
interval(start, end)
duration(end - start)

difftime() 适合直接取得指定单位的差值;interval() 保留起点和终点,适合判断包含关系和处理日历运算;duration() 表达以秒为基础的精确时长。

时区与夏令时

时区不是显示格式,而是日期时间含义的一部分。跨系统存储时,可以使用 UTC;展示给用户时,再转换到需要的本地时区。

本地时区还可能受夏令时影响:某些墙上时间会重复出现或根本不存在。因此日志和事件数据应在进入分析流程时明确时区,不能只在导出结果时补一个时区标签。

把日期表示、解析顺序、时区和数据类型在入口处确定下来,后续的分组、连接和时间差计算才会保持一致。