100

Hi I'm trying to get a sequence of dates with lubridate

This doesn't work

seq(ymd('2012-04-07'),ymd('2013-03-22'),by=week(1))

the base command

seq(as.Date('2012-04-7'),as.Date('2013-03-22'),'weeks')

does, but I'd like to know if there is an elegant way to do this with lubridate.

EDIT

Please ignore : solved myself so leaving up for posterity only. Happy to have this deleted if necessary.

seq(ymd('2012-04-07'),ymd('2013-03-22'),by='weeks')

Does the trick

Tahnoon Pasha
  • 5,848
  • 14
  • 49
  • 75

3 Answers3

111

ymd is a wrapper to parse date strings and returns a POSIXct object.

You simply need to use standard terminology described in ?seq.POSIXt (not lubridate) to define weeks

seq(ymd('2012-04-07'),ymd('2013-03-22'), by = '1 week')
seq(ymd('2012-04-07'),ymd('2013-03-22'), by = 'weeks')

will works

as will

seq(ymd('2012-04-07'),ymd('2013-03-22'), by = '2 week')

You could coerce the lubridate Period class object to a difftime, but that seems rather unnecessary

seq(ymd('2012-04-07'),ymd('2013-03-22'), by = as.difftime(weeks(1)))
mnel
  • 113,303
  • 27
  • 265
  • 254
  • Is there any way to get a sequence of `POSIXct` formatted objects? I want to use my date-sequence for a custom x-axis, and `POSIXt` doesn't match the plot's data (it's all POSIXct from lubridate output). – rrr Aug 01 '18 at 03:54
  • 2
    A caution that the base R approach doesn't always do the same as lubridate's Period. `seq(as.Date("2001-01-31"), as.Date("2001-12-31"), "months")` gives surprising results, for instance. – dash2 Mar 09 '20 at 20:05
12

This is a way to stick within the POSIXct universe of lubridate and not change date formats to base R's POSIXt. I avoid changing the date format in my scripts because I find it is a common place where bugs (for example time-zone changes or losing timestamps) are introduced. It follows this advice to use %m+%: R: adding 1 month to a date

# example date is a leap day for a "worst case scenario"
library("lubridate")
posixct.in <- parse_date_time(x = "2016-02-29", orders = "ymd")
# [1] "2016-02-29 UTC"

posixct.seq <- posixct.in %m+% years(x = seq.int(from = 0, to = 3, by = 1))
# [1] "2016-02-29 UTC" "2017-02-28 UTC" "2018-02-28 UTC" "2019-02-28 UTC"

posixct.seq <- posixct.in %m+% months(x = seq.int(from = 0, to = 3, by = 1))
# [1] "2016-02-29 UTC" "2016-03-29 UTC" "2016-04-29 UTC" "2016-05-29 UTC"

posixct.seq <- posixct.in %m+% days(x = seq.int(from = 0, to = 3, by = 1))
# [1] "2016-02-29 UTC" "2016-03-01 UTC" "2016-03-02 UTC" "2016-03-03 UTC"

posixct.seq <- posixct.in %m+% weeks(x = seq.int(from = 0, to = 3, by = 1))
# [1] "2016-02-29 UTC" "2016-03-07 UTC" "2016-03-14 UTC" "2016-03-21 UTC"

A regular + also works sometimes, but the %m+% prevents errors like this:

posixct.seq <- posixct.in + years(x = seq.int(from = 0, to = 3, by = 1))
# [1] "2016-02-29 UTC" NA               NA               NA

At first I was confused because I thought %m+ was just a way to add months, and similar lubridate commands like %y+% etc. do not exist. But, turns out the "m" doesn't stand for "month addition". My best guess is "magic" =)

rrr
  • 1,914
  • 2
  • 21
  • 24
  • Thank you for this post. The comment regarding `%m+%` was particularly helpful. Lubridate is extremely powerful, but the documentation isn't great for things like the `%m_%` operator. I too had been assuming that it was constrained to shifting dates by month(s) – icj Nov 23 '22 at 21:31
0

This may or may not be useful to others but I've written a more general function for calculating date sequences using a combination of lubridate, base and timechange. It allows for either period or duration (base) arithmetic.

# remotes::install_github("NicChr/timeplyr")
library(timeplyr)
library(lubridate)
#> 
#> Attaching package: 'lubridate'
#> The following objects are masked from 'package:base':
#> 
#>     date, intersect, setdiff, union
seq(ymd('2012-04-07'),ymd('2013-03-22'),by='weeks')
#>  [1] "2012-04-07" "2012-04-14" "2012-04-21" "2012-04-28" "2012-05-05"
#>  [6] "2012-05-12" "2012-05-19" "2012-05-26" "2012-06-02" "2012-06-09"
#> [11] "2012-06-16" "2012-06-23" "2012-06-30" "2012-07-07" "2012-07-14"
#> [16] "2012-07-21" "2012-07-28" "2012-08-04" "2012-08-11" "2012-08-18"
#> [21] "2012-08-25" "2012-09-01" "2012-09-08" "2012-09-15" "2012-09-22"
#> [26] "2012-09-29" "2012-10-06" "2012-10-13" "2012-10-20" "2012-10-27"
#> [31] "2012-11-03" "2012-11-10" "2012-11-17" "2012-11-24" "2012-12-01"
#> [36] "2012-12-08" "2012-12-15" "2012-12-22" "2012-12-29" "2013-01-05"
#> [41] "2013-01-12" "2013-01-19" "2013-01-26" "2013-02-02" "2013-02-09"
#> [46] "2013-02-16" "2013-02-23" "2013-03-02" "2013-03-09" "2013-03-16"
time_seq(ymd('2012-04-07'),ymd('2013-03-22'),by='weeks')
#>  [1] "2012-04-07" "2012-04-14" "2012-04-21" "2012-04-28" "2012-05-05"
#>  [6] "2012-05-12" "2012-05-19" "2012-05-26" "2012-06-02" "2012-06-09"
#> [11] "2012-06-16" "2012-06-23" "2012-06-30" "2012-07-07" "2012-07-14"
#> [16] "2012-07-21" "2012-07-28" "2012-08-04" "2012-08-11" "2012-08-18"
#> [21] "2012-08-25" "2012-09-01" "2012-09-08" "2012-09-15" "2012-09-22"
#> [26] "2012-09-29" "2012-10-06" "2012-10-13" "2012-10-20" "2012-10-27"
#> [31] "2012-11-03" "2012-11-10" "2012-11-17" "2012-11-24" "2012-12-01"
#> [36] "2012-12-08" "2012-12-15" "2012-12-22" "2012-12-29" "2013-01-05"
#> [41] "2013-01-12" "2013-01-19" "2013-01-26" "2013-02-02" "2013-02-09"
#> [46] "2013-02-16" "2013-02-23" "2013-03-02" "2013-03-09" "2013-03-16"

# Months example

# Notice the difference between seq and time_seq
time_seq(dmy("31-01-2012"), dmy("31-01-2013"), by = "month")
#>  [1] "2012-01-31" "2012-02-29" "2012-03-31" "2012-04-30" "2012-05-31"
#>  [6] "2012-06-30" "2012-07-31" "2012-08-31" "2012-09-30" "2012-10-31"
#> [11] "2012-11-30" "2012-12-31" "2013-01-31"
seq(dmy("31-01-2012"), dmy("31-01-2013"), by = "month")
#>  [1] "2012-01-31" "2012-03-02" "2012-03-31" "2012-05-01" "2012-05-31"
#>  [6] "2012-07-01" "2012-07-31" "2012-08-31" "2012-10-01" "2012-10-31"
#> [11] "2012-12-01" "2012-12-31" "2013-01-31"

# DST example
dst <- dmy_hms("26-03-2023 00:00:00", tz = "GB")

# Base R
seq(dst, length.out = 5,
         by = "hour")
#> [1] "2023-03-26 00:00:00 GMT" "2023-03-26 02:00:00 BST"
#> [3] "2023-03-26 03:00:00 BST" "2023-03-26 04:00:00 BST"
#> [5] "2023-03-26 05:00:00 BST"
time_seq(dst, length.out = 5,
         by = "hour",
         seq_type = "duration")
#> [1] "2023-03-26 00:00:00 GMT" "2023-03-26 02:00:00 BST"
#> [3] "2023-03-26 03:00:00 BST" "2023-03-26 04:00:00 BST"
#> [5] "2023-03-26 05:00:00 BST"
# Period arithmetic
time_seq(dst, length.out = 5,
         roll_dst = "NA",
         by = "hour",
         seq_type = "period")
#> [1] "2023-03-26 00:00:00 GMT" NA                       
#> [3] "2023-03-26 02:00:00 BST" "2023-03-26 03:00:00 BST"
#> [5] "2023-03-26 04:00:00 BST"
time_seq(dst, length.out = 5,
         roll_dst = "pre",
         by = "hour",
         seq_type = "period")
#> [1] "2023-03-26 00:00:00 GMT" "2023-03-26 00:00:00 GMT"
#> [3] "2023-03-26 02:00:00 BST" "2023-03-26 03:00:00 BST"
#> [5] "2023-03-26 04:00:00 BST"
time_seq(dst, length.out = 5,
         roll_dst = "post",
         by = "hour",
         seq_type = "period")
#> [1] "2023-03-26 00:00:00 GMT" "2023-03-26 02:00:00 BST"
#> [3] "2023-03-26 02:00:00 BST" "2023-03-26 03:00:00 BST"
#> [5] "2023-03-26 04:00:00 BST"

# You can also mix dates and datetimes

seq(ymd('2012-04-07'), now(), by = "years")
#> Error in seq.Date(ymd("2012-04-07"), now(), by = "years"): 'to' must be a "Date" object
time_seq(ymd('2012-04-07'), now(), by = "years")
#>  [1] "2012-04-07 UTC" "2013-04-07 UTC" "2014-04-07 UTC" "2015-04-07 UTC"
#>  [5] "2016-04-07 UTC" "2017-04-07 UTC" "2018-04-07 UTC" "2019-04-07 UTC"
#>  [9] "2020-04-07 UTC" "2021-04-07 UTC" "2022-04-07 UTC"

Created on 2023-03-28 with reprex v2.0.2

NicChr
  • 858
  • 1
  • 9