通过 Lubridate 转换日期字符串,月、日、年小时分钟 am/pm 和时区变化

Transform string in date through Lubridate with variation in month, day, year hour min am/pm and time zone

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

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 中的 S 代表太平洋时间的标准时区 (https://www.timeanddate.com/time/zones/pdt and https://www.timeanddate.com/time/zones/pst) and similarly for CET (https://time.is/CET) and CEST (https://time.is/CEST)。你能帮帮我吗?

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

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' 位)来整理日期时间。我们还提取时区代码并将其放在单独的列中;时区列允许我们 left_join() my_tz ,给我们 UTC 偏移量。

我们使用 stringr package, and regex expressions to find, extract and replace the components. A very handy tool for testing regex patterns can be found here 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()date1date2转换为dttm(日期时间)格式。 as_datetime() 采用字符格式的日期时间并将其转换为日期时间格式。您必须使用符号和缩写说明 here 指定字符串的格式。例如,这里我们使用%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 个不同的日期时间格式向量,则值得考虑更复杂的操作,将数据从宽格式转换为长格式。这将避免为每一列重复 same/similar 代码,就像我们在本例中为 date1date2 所做的那样。