首页 > 解决方案 > 通过 Lubridate 转换日期中的字符串,月、日、年、小时、分钟、上午/下午和时区的变化

问题描述

我需要一些关于不同时区的润滑功能的帮助。我有两个这样的向量:

date1 = c("February 11th 2017, 6:05am PST", "April 24th 2018, 4:09pm PDT") 
date2 = c("2013-12-14 00:58:00 CET", "2013-06-19 18:00:00 CEST")

我想使用 lubridate 函数(我尝试过 mdy_hm)将这些字符串转换为日期格式,然后在考虑时区差异的同时计算两个字符串之间的差异(以天为单位),其中 PDT 中的 D 代表天PST 中的 Light 和 S 代表太平洋时间的标准时区(https://www.timeanddate.com/time/zones/pdthttps://www.timeanddate.com/time/zones/pst),同样代表 CET ( https://time.is/CET ) 和 CEST ( https://time.is/CEST )。请你帮助我好吗?

标签: rstringtimezonelubridate

解决方案


我做的第一件事是用你的 2 个日期向量设置一个小标题

tibble(
  date1 = c("February 11th 2017, 6:05am PST", "April 24th 2018, 4:09pm PDT"),
  date2 = c("2013-12-14 00:58:00 CET", "2013-06-19 18:00:00 CEST"),
) %>%
  {. ->> my_dates}

my_dates

# # A tibble: 2 x 2
# date1                          date2                   
# <chr>                          <chr>                   
# February 11th 2017, 6:05am PST 2013-12-14 00:58:00 CET 
# April 24th 2018, 4:09pm PDT    2013-06-19 18:00:00 CEST

然后,制作时区缩写及其与 UTC 的偏移量

# setup timezones and UTC offsets
tribble(
  ~tz, ~offset,
  'PST', -8,
  'PDT', -7,
  'CET', +1,
  'CEST', +2
) %>%
  {. ->> my_tz}

my_tz

# # A tibble: 4 x 2
# tz    offset
# <chr>  <dbl>
# PST       -8
# PDT       -7
# CET        1
# CEST       2

date1然后,我们通过删除日期数字后的字符后缀(“11th”之后的“th”位)来整理日期时间。我们还提取了时区代码并将其放在单独的列中;timezone 列允许我们进入left_join() my_tz,给我们 UTC 偏移量。

我们使用stringr包中的字符串处理函数和正则表达式来查找、提取和替换组件。可以在这里找到一个非常方便的测试正则表达式模式的工具https://regex101.com/r/5pr3LL/1/

my_dates %>%
  mutate(
    # remove the character suffix after the day number (eg 11th)
    day_suffix = str_extract(date1, '[0-9]+[a-z]+') %>% str_extract('[a-z]+'),
    date1 = str_replace(date1, day_suffix, ''),
    day_suffix = NULL,

    # extract timezone info
    date1_tz = str_extract(date1, '[a-zA-Z]+$'),
    date2_tz = str_extract(date2, '[a-zA-Z]+$'),
  ) %>%

  # join in timezones for date1
  left_join(my_tz, by = c('date1_tz' = 'tz')) %>%
  rename(
    offset_date1 = offset
  ) %>%

  # join in timezones for date2
  left_join(my_tz, by = c('date2_tz' = 'tz')) %>%
  rename(
    offset_date2 = offset
  ) %>% 
  
  {. ->> my_dates_info}

my_dates_info

# # A tibble: 2 x 6
# date1                        date2                    date1_tz date2_tz offset_date1 offset_date2
# <chr>                        <chr>                    <chr>    <chr>           <dbl>        <dbl>
# February 11 2017, 6:05am PST 2013-12-14 00:58:00 CET  PST      CET                -8            1
# April 24 2018, 4:09pm PDT    2013-06-19 18:00:00 CEST PDT      CEST               -7            2

所以现在,我们可以使用lubridate::as_datetime()todate1date2to dttm(datetime) 格式进行转换。as_datetime()采用字符格式的日期时间并将其转换为日期时间格式。您必须使用此处说明的符号和缩写指定字符串的格式。例如,这里我们%B用来指月份的全名,%d是天数,%Y是(4位)年份数等。

注意:因为我们没有在 内部指定时区as_datetime(),所以与这些日期时间一起存储的底层时区默认为UTC(如使用 所见tz())。这就是我们称这些列的原因date*_orig,以提醒我们时区是原始日期时间的时区。然后我们将偏移量添加到 datetime 对象,所以我们现在有了这些 UTC 时间(并且这些值的基础时区签名是UTC,所以这是理想的)。

# now define datetimes in local and UTC timezones (note: technically the tz is UTC for both)
my_dates_info %>% 
  mutate(
    date1_orig = as_datetime(date1, format = '%B %d %Y, %I:%M%p '),
    date1_utc = date1_orig + hours(offset_date1),
    date2_orig = as_datetime(date2, format = '%Y-%m-%d %H:%M:%S'),
    date2_utc = date2_orig + hours(offset_date2),
  ) %>% 
  {. ->> my_dates_utc}

my_dates_utc

# # A tibble: 2 x 10
# date1                        date2                    date1_tz date2_tz offset_date1 offset_date2 date1_orig          date1_utc           date2_orig          date2_utc          
# <chr>                        <chr>                    <chr>    <chr>           <dbl>        <dbl> <dttm>              <dttm>              <dttm>              <dttm>             
# February 11 2017, 6:05am PST 2013-12-14 00:58:00 CET  PST      CET                -8            1 2017-02-11 06:05:00 2017-02-10 22:05:00 2013-12-14 00:58:00 2013-12-14 01:58:00
# April 24 2018, 4:09pm PDT    2013-06-19 18:00:00 CEST PDT      CEST               -7            2 2018-04-24 16:09:00 2018-04-24 09:09:00 2013-06-19 18:00:00 2013-06-19 20:00:00

现在我们有两组日期时间格式的日期,并且在同一个时区,我们可以计算它们之间的时间差。

# now calculate difference between them
my_dates_utc %>% 
  select(date1_utc, date2_utc) %>% 
  mutate(
    difference_days = interval(start = date1_utc, end = date2_utc) %>% time_length(unit = 'days')
  )

# # A tibble: 2 x 3
# date1_utc           date2_utc           difference_days
# <dttm>              <dttm>                        <dbl>
# 2017-02-10 22:05:00 2013-12-14 01:58:00          -1155.
# 2018-04-24 09:09:00 2013-06-19 20:00:00          -1770.

这对于小规模操作应该没问题。如果您有超过 2 个不同的日期时间格式向量,则值得考虑更复杂的操作,将数据从宽格式转换为长格式。这将节省为每一列重复相同/相似的代码,就像我们在这个例子中所做的那样date1date2


推荐阅读