tidyverse / lubridate

Make working with dates in R just that little bit easier
https://lubridate.tidyverse.org
GNU General Public License v3.0
724 stars 207 forks source link

[Feature Suggestion] ISO leap year (long years) #1129

Open ceisenhauer opened 1 year ago

ceisenhauer commented 1 year ago

I'm not sure if this use case is too specific (or if there is already a solution elsewhere!) but it's something I found myself needing rather often so I figured I would share.

In my job I use lubridate a lot to work with dates in general and ISO weeks/years in particular. I frequently need to know if a particular year is an ISO "leap year", i.e. a year with 53 ISO weeks instead of 52. While ISO leap years and standard leap years sometimes happen to match up, this is often not the case and leap_year() thus can't be used on its own to check if a week has 53 weeks.

So, I wrote a function, iso_leap_year() to compliments leap_year():

iso_leap_year <- function(date) {
  if (is.numeric(date)) {
    year <- date
  } else {
    year <- lubridate::year(date)
  }

  january_first <- lubridate::wday(paste0(year, "-01-01"))

  (january_first == 5) | (january_first == 4 & lubridate::leap_year(year))
}

As we can see, sometimes ISO and standard leap years line up:

# standard leap year AND ISO long year
year <- 2020
lubridate::leap_year(year)
#> [1] TRUE
iso_leap_year(year)
#> [1] TRUE

But often, they do not:

# standard leap year but NOT ISO long year
year <- 2012
lubridate::leap_year(year)
#> [1] TRUE
iso_leap_year(year)
#> [1] FALSE

# ISO long year but NOT standard leap year
year <- 2015
lubridate::leap_year(year)
#> [1] FALSE
iso_leap_year(year)
#> [1] TRUE

NB. ISO years with 53 weeks are technically called long years not "leap years". I found iso_leap_year() a more intuitive name, but it could equally be iso_long_year().

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

vspinu commented 11 months ago

I wold happily approve a PR for this. I think it's a nice addition to the package. But docs, tests and news entry should be added. Also please use ISOdate to construct the date instead of parsing the string.

Regarding the actual computation of the leap year, the peculiarity of the standard should be documented. The above rule would not predict correctly some of the years, like 2004. The rule suggested here seem to be doing the job:

A slight modification of the above rule, apparently first suggested by Sven Pran (Norway) and Lars Nordentoft (Denmark), successfully predicts the long ISO calendar years without any error:
    An ISO calendar year is long if and only if the corresponding Gregorian year begins on a Thursday when it is a common year or begins either on a Wednesday or a Thursday when it is a leap year.
A variant of the above rule was proposed in 1997 by Amos Shapir (Israel):
   An ISO calendar year is long if and only if the corresponding Gregorian year either begins or ends (or both) on a Thursday.
ceisenhauer commented 10 months ago

Great! I will add the requested changes and additional docs this week and then submit a PR.