From 3b64ce98ce367e0e24dbc1de6fca79d071e905cf Mon Sep 17 00:00:00 2001 From: maechler Date: Tue, 7 Jan 2025 21:20:25 +0000 Subject: [PATCH] seq.Date(*, by = ); docu+checks also for seq.POSIX git-svn-id: https://svn.r-project.org/R/trunk@87537 00db46b3-68df-0310-9c12-caf00c1e9a41 --- doc/NEWS.Rd | 3 +++ src/library/base/R/dates.R | 12 +++++++----- src/library/base/R/datetime.R | 1 + src/library/base/man/seq.Date.Rd | 11 ++++++++--- src/library/base/man/seq.POSIXt.Rd | 3 ++- tests/datetime3.R | 9 ++++++++- 6 files changed, 29 insertions(+), 10 deletions(-) diff --git a/doc/NEWS.Rd b/doc/NEWS.Rd index 3e98165812..5889d93c55 100644 --- a/doc/NEWS.Rd +++ b/doc/NEWS.Rd @@ -156,6 +156,9 @@ The \code{Date} method also works for \code{seq(from, to)}, when \code{by} is missing and now defaults to \code{"1 days"}. + It is now documented (and tested) that the \code{by} string may be + \emph{abbreviated} in both \code{seq} methods. + Both methods return or keep internal type \code{"integer"} more consistently now. Also, \code{as.POSIXct({})} is internally integer. } diff --git a/src/library/base/R/dates.R b/src/library/base/R/dates.R index c2fe788fb9..80e07491c3 100644 --- a/src/library/base/R/dates.R +++ b/src/library/base/R/dates.R @@ -270,13 +270,15 @@ seq.Date <- function(from, to, by, length.out = NULL, along.with = NULL, ...) units(by) <- "days" by <- as.vector(by) } else if(is.character(by)) { - by2 <- strsplit(by, " ", fixed = TRUE)[[1L]] - if(length(by2) > 2L || length(by2) < 1L) + nby2 <- length(by2 <- strsplit(by, " ", fixed = TRUE)[[1L]]) + if(nby2 > 2L || nby2 < 1L) stop("invalid 'by' string") - valid <- pmatch(by2[length(by2)], - c("days", "weeks", "months", "quarters", "years")) + bys <- c("days", "weeks", "months", "quarters", "years") + valid <- pmatch(by2[nby2], bys) if(is.na(valid)) stop("invalid string for 'by'") + by <- bys[valid] # had *partial* match if(valid > 2L) { # seq.POSIXt handles the logic for non-arithmetic cases + if (nby2 == 2L) by <- paste(by2[1L], by) res <- switch(missing_arg, from = seq(to = as.POSIXlt(to), by = by, length.out = length.out), to = seq(from = as.POSIXlt(from), by = by, length.out = length.out), @@ -285,7 +287,7 @@ seq.Date <- function(from, to, by, length.out = NULL, along.with = NULL, ...) return(as.Date(res)) } by <- c(1L, 7L)[valid] - if (length(by2) == 2L) by <- by * as.integer(by2[1L]) + if (nby2 == 2L) by <- by * as.integer(by2[1L]) } else if(!is.numeric(by)) stop("invalid mode for 'by'") if(is.na(by)) stop("'by' is NA") diff --git a/src/library/base/R/datetime.R b/src/library/base/R/datetime.R index bcc9cad871..1c2fc2cc70 100644 --- a/src/library/base/R/datetime.R +++ b/src/library/base/R/datetime.R @@ -1004,6 +1004,7 @@ function(from, to, by, length.out = NULL, along.with = NULL, ...) ) return(.POSIXct(res, tz)) } + ## months or longer --> via POSIXlt lres <- as.POSIXlt(if (missing_arg != "from") from else to) if (missing_arg == "length.out") lto <- as.POSIXlt(to) if(valid == 7L) { # years diff --git a/src/library/base/man/seq.Date.Rd b/src/library/base/man/seq.Date.Rd index 4df37e6f55..6b86fa7b99 100644 --- a/src/library/base/man/seq.Date.Rd +++ b/src/library/base/man/seq.Date.Rd @@ -27,7 +27,8 @@ \item A number, taken to be in days. \item A object of class \code{\link{difftime}} \item A character string, containing one of \code{"day"}, - \code{"week"}, \code{"month"}, \code{"quarter"} or \code{"year"}. + \code{"week"}, \code{"month"}, \code{"quarter"} or \code{"year"}, or a + \code{\link{pmatch}()}able abbreviaton of these. This can optionally be preceded by a (positive or negative) integer and a space, or followed by \code{"s"}. @@ -55,11 +56,15 @@ seq(as.Date("2000/1/1"), as.Date("2003/1/1"), by = "quarter") ## 3-week period ending on a fixed date seq(to = as.Date("2024-06-18"), by = "day", length.out = 21) -## find all 7th of the month between two dates, the last being a 7th. +## find all 7th of the month _strictly_ inside two dates, the last being a 7th. st <- as.Date("1998-12-17") en <- as.Date("2000-1-7") ll <- seq(en, st, by = "-1 month") -rev(ll[ll > st & ll < en]) +rev(ll[st < ll & ll < en]) + +## can abbreviate 'month' to 'm': +identical(seq(st, en, by = "m"), + seq(st, en, by = "1 month")) } \keyword{manip} \keyword{chron} diff --git a/src/library/base/man/seq.POSIXt.Rd b/src/library/base/man/seq.POSIXt.Rd index 2199ce6d28..75aa96e88d 100644 --- a/src/library/base/man/seq.POSIXt.Rd +++ b/src/library/base/man/seq.POSIXt.Rd @@ -27,7 +27,8 @@ \item An object of class \code{\link{difftime}} \item A character string, containing one of \code{"sec"}, \code{"min"}, \code{"hour"}, \code{"day"}, \code{"DSTday"}, - \code{"week"}, \code{"month"}, \code{"quarter"} or \code{"year"}. + \code{"week"}, \code{"month"}, \code{"quarter"} or \code{"year"}, or a + \code{\link{pmatch}()}able abbreviaton of these. This can optionally be preceded by a (positive or negative) integer and a space, or followed by \code{"s"}. } diff --git a/tests/datetime3.R b/tests/datetime3.R index 1e77863507..f74d22560c 100644 --- a/tests/datetime3.R +++ b/tests/datetime3.R @@ -683,6 +683,8 @@ All.eq0 <- function(x,y, ...) all.equal(x, y, tolerance = 0, ...) stopifnot(exprs = { ## NB: use 'from' on LHS of reference to ensure the time zone of 'from' is used in the result identical(seq(from, to, by=by), from + wks2sec*(0:4)) + identical(seq(from, to, by=by), + seq(from, to, by="2 w")) # may abbreviate identical(seq(from, to, length.out=length.out), from + seq(0, difftime(to, from, units="secs"), length.out=length.out)) ## @@ -732,11 +734,16 @@ stopifnot(exprs = { ## variations on 'by' identical(seq(from, to, by='2 months'), from + c(0, c(31+29))) identical(seq(to, from, by='-2 months'), to - c(0, c(31+29))) + identical(seq(to, from, by='-2 m' ), to - c(0, c(31+29))) identical(seq(from, to, by=as.difftime(30, units='days')), from + 30*(0:2)) identical(seq(from, to, by=30), from + 30*(0:2)) + all.equal(seq(from, to, by = "1 week"), seq(from, by = "w", length.out = 9)) # TODO ident. ? + identical(seq(frI, toI, by = "1 week"), seq(from, by = "w", length.out = 9)) ## ## missing from= - All.eq0 ( seq(to=to, by='day', length.out=6), to - (5:0)) + identical(seq(to=to, by='day', length.out=6), + seq(to=to, length.out=6)) + All.eq0 ( seq(to=to, length.out=6), to - (5:0)) All.eq0 ( seq(to=to, by='-3 days', length.out=6), to + 3*(5:0)) identical(seq(to=to, by='2 months',length.out=3), to - c(31+29+31+30, 31+29, 0)) identical(seq(to=to, by='quarter', length.out=3), to - c(31+29+31+30+31+30, 31+29+31, 0))