Last active
February 13, 2022 23:00
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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