How to account for leap years?

前端 未结 4 360
陌清茗
陌清茗 2020-12-06 09:58

I have some doubts about the leap years, how can I be sure that by using a formula like this

add.years= function(x,y){    
if(!isTRUE(all.equal(y,round(y))))         


        
相关标签:
4条回答
  • 2020-12-06 10:25

    A year is a leap year if:

    • Is divisible by 4.
    • Not if it is divisible by 100.
    • But is if it is divisible by 400.

    That is why 2000 was a leap year (although it's divisible by 100, it's also divisible by 400).

    But generally, if you have a library that can take of date/time calculations then use it. It's very complicated to do these calculations and easy to do wrong, especially with ancient dates (calendar reforms) and timezones involved.

    0 讨论(0)
  • 2020-12-06 10:30

    You can check if a year is a leap year with leap_year from lubridate.

    years <- 1895:2005
    years[leap_year(years)]
    

    This package will also handle not generating impossible 29ths of February.

    ymd("2000-2-29") + years(1)    # NA
    ymd("2000-2-29") %m+% years(1) # "2001-02-28"
    

    The %m+% "add months" operator, as mentioned by @VitoshKa, rolls the date back to the end of the previous month if the actual day doesn't exist.

    0 讨论(0)
  • 2020-12-06 10:38

    Following the suggestion of DarkDust and Dirk Eddelbuettel, you can easily roll your own leap_year function:

    leap_year <- function(year) {
      return(ifelse((year %%4 == 0 & year %%100 != 0) | year %%400 == 0, TRUE, FALSE))
    }
    

    and apply it to vector data:

    years = 2000:2050
    years[leap_year(years)]
    
    [1] 2000 2004 2008 2012 2016 2020 2024 2028 2032 2036 2040 2044 2048
    
    0 讨论(0)
  • 2020-12-06 10:46

    Your suspicions are indeed correct:

    x <- as.POSIXlt("2000-02-29")
    y <- x
    y$year <- y$year+100
    y
    #[1] "2100-03-01"
    

    The strange thing is that other parts of y remain unchanged so you can't use these for comparison:

    y$mday
    #[1] 29
    y$mon
    #[1] 1
    

    But you can use strftime:

    strftime(x,"%d")
    #[1] "29"
    strftime(y,"%d")
    #[1] "01"
    

    So how about:

    add.years <- function(x,y){
       if(!isTRUE(all.equal(y,round(y)))) stop("Argument \"y\" must be an integer.\n")
       x.out <- as.POSIXlt(x)
       x.out$year <- x.out$year+y
       ifelse(strftime(x,"%d")==strftime(x.out,"%d"),as.Date(x.out),NA)
       } 
    

    You can then subset your data using [ and is.na to get rid of the otherwise duplicate 1st March dates. Though as these dates seem to be consecutive, you might want to consider a solution that uses seq.Date and avoid dropping data.

    0 讨论(0)
提交回复
热议问题