Skip to content

Instantly share code, notes, and snippets.

@jimjam-slam
Last active February 13, 2022 23:00
Show Gist options
  • Save jimjam-slam/07b1fa15f8091ed6e897faae65204b8b to your computer and use it in GitHub Desktop.
Save jimjam-slam/07b1fa15f8091ed6e897faae65204b8b to your computer and use it in GitHub Desktop.
Get the number of leap days between a vector of dates and a constant epoch. Useful if you need to go between Gregorian and Julian/365 day calendars #rstatstips
# count_leap_days: returns an integer for the number of leap days between a
# vector of dates and a constant epoch
count_leap_days <- function(dates, epoch = ymd('1850-01-01'), proleptic = FALSE)
{
require(lubridate)
# check input
if (!is(epoch, 'Date') | !is(dates, 'Date'))
{
stop('count_leap_days: both arguments must be Date objects.')
}
if (any(dates <= epoch))
{
stop('count_leap_days: dates should all be later than epoch.')
}
if (proleptic = FALSE & epoch < ymd('1582-10-15'))
{
message('count_leap_days: ',
'no leap days before 1582-10-15 unless proleptic = TRUE.')
epoch = ymd('1582-10-15')
}
# get the year range
# exclude start (end) years if they begin after (start before) feb 29
y.epoch = year(epoch) +
ifelse(epoch >= ymd(paste0(year(epoch), '-03-01')), 1, 0)
y.dates = year(dates) -
ifelse(dates <= ymd(paste0(year(dates), '-02-28')), 1, 0)
span = y.dates - y.epoch + 1
# a year is a leap year if it's:
# - divisble by 4. but
# - NOT if it's also divisible by 100, unless
# - it's also divisible by 400.
# all years div. by 4 are also div. by 100, and
# all years div. by 100 are also div. by 400.
# hence, total days = (div. by 4) - (div. by 100) + (div. by 400)
div4 = span %/% 4 +
ifelse(
(y.epoch %% 4) %in% c(0, (4 - (span %% 4) - 1):(4 - 1)) &
(y.dates %% 4) %in% 0:(span %% 4 - 1),
1, 0)
div100 = span %/% 100 +
ifelse(
(y.epoch %% 100) %in% c(0, (100 - (span %% 100) - 1):(100 - 1)) &
(y.dates %% 100) %in% 0:(span %% 100 - 1),
1, 0)
div400 = span %/% 400 +
ifelse(
(y.epoch %% 400) %in% c(0, (400 - (span %% 400) - 1):(400 - 1)) &
(y.dates %% 400) %in% 0:(span %% 400 - 1),
1, 0)
return(div4 - div100 + div400)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment