Skip to content

# Fix interval %/% and %% errors, NA in setdiff(), century leap years - #1227

Open
sims1253 wants to merge 3 commits into
tidyverse:mainfrom
sims1253:fix/ry-audit-round2
Open

sims1253 wants to merge 3 commits into
tidyverse:mainfrom
sims1253:fix/ry-audit-round2

Conversation

@sims1253

@sims1253 sims1253 commented Sep 9, 2026

Copy link
Copy Markdown

Report the operator the user wrote for %/% and %% on an Interval

%/% for (Duration, Interval) and (Period, Interval) in R/ops-integer-division.r dispatches to trunc_divide(), which computes trunc(e1 / e2). / for those signatures is a stop-only method in R/ops-division.r, so the error blames an operator the user never wrote:

int <- interval(ymd("2020-01-01"), ymd("2020-02-15"))
ddays(10) %/% int
#> Error: Incompatible classes: <Duration> / <Interval>

%% for (Period, Interval) in R/ops-modulo.r goes through modulo_period_by_period(), which calls %/% and lands on the same message:

months(3) %% int
#> Error: Incompatible classes: <Period> / <Interval>

Treat NA entries as not discontinuous in setdiff()

setdiff.Interval() in R/intervals.r computes makes2 <- !aligned & inside and then runs if (sum(makes2)). Any NA in x or y turns inside into NA, and the if() fails:

x <- interval(ymd(c(NA, "2020-02-01")), ymd(c(NA, "2020-04-10")))
y <- interval(ymd("2021-01-01"), ymd("2021-02-01"))
setdiff(x, y)
#> Error: argument is not interpretable as logical

Use the calendar year for February lengths in .int_to_period()

.int_to_period() in R/coercion.r converts end to POSIXlt and passes end$year to .days_in_month() at two sites. POSIXlt stores years since 1900, and .days_in_month() (R/accessors-month.r) calls leap_year() on an absolute year, so February gets the wrong length in years where the two disagree. For 2000 it receives 100: leap_year(100) is FALSE, leap_year(2000) is TRUE. For 1900 it receives 0, which the rule counts as a leap year while 1900 is not.

as.period(interval(ymd("2000-01-20"), ymd("2000-03-01")))
#> [1] "1m 9d 0H 0M 0S"    # February 2000 has 29 days; should be 10d
as.period(interval(ymd("1900-01-20"), ymd("1900-03-01")))
#> [1] "1m 10d 0H 0M 0S"   # February 1900 has 28 days; should be 9d

Found during my most recent ry audit.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant