R example LifeDays

From "A B C"
Jump to navigation Jump to search

R example: Life Days


This is an R function to illustrate work with R time objects. The function takes a date as a string, and optionally a requested anniversary as an integer. It returns textual output: the interval between today and the requested date, and optionally the date on which the requested anniversary will occur. It is heavily annotated. This is teaching code, i.e. coded for clarity, not necessarily efficiency; also, robust handling of user input probably requires more sanity tests than we are doing here.


At thier core, time objects in R are seconds after a reference date - the "epoch" (1970-01-01 00:00 UTZ). Therefore one can simply add or subtract seconds from such an object to calculate differences, or add and subtract time objects themselves. Formatting takes care of all the leap-days, months of different length etc. See strptime() for a list of formatting options. To keep things simple, we are not considering time zones separately, but this can easily be done in R. You can even use a special invocation of seq() on them, e.g. seq(Sys.time(), len=7, by="1 day") for today and the next week[1].


myLifeDays <- function(inD = NULL, span = NULL) { # giving parameters a default value 
                                                  # allows us to test whether they have been set

   # calculates date differences in days and anniversary days
   # mandatory input: date (string)
   # optional input:  anniversary in days (integer)
   # output: text information.
   #

   if (is.null(inD)) {
   	 # no argument set: print instructions and return
      # use cat(), not print() for cleaner output
   	 cat("\nUsage:\n") # cat() requires explicit linebreaks "\n"
   	 cat("------\n")
   	 cat("myLifeDays(date, ...)\n")
   	 cat("\nArguments:\n")
   	 cat("----------\n")
      cat("date    a string in \"YYYY-MM-DD\" format.\n")
      cat("span    calculate date for *span* of days since *date*.\n")
      cat("\n")
      return(invisible())  # invisible, to supress returning NULL
   }
   
   # This is an implicit "else" ... we continue here if we didnt return()
   # via the previous condition.
   
   xD    <- strptime(inD, "%Y-%m-%d")        # convert inD string to time
   xD    <- xD + (12 * 60 * 60)              # change xD from midnight to noon
   toD   <- format(Sys.time(), "%Y-%m-%d")   # get "now" as time object
   diffD <- round(as.numeric(difftime(Sys.time(), xD, unit="days")))
   
   # Create a first output string.
   if        (diffD > 0) { m1 <- (paste(xD, " was ", diffD, " days ago.\n",         sep=""))
   } else if (diffD < 0) { m1 <- (paste(xD, " will be in ", abs(diffD), " days.\n", sep=""))
   } else                { m1 <- (paste(xD, " is today.\n",                         sep="")) }
   
   if ( ! is.null(span)) { # Calculate relative dates
   	
   	 span <- as.integer(span)    # handle erroneous input
   	 
   	 # subtract number of days that have already elapsed from span
   	 remainD <- span - round(as.numeric(difftime(toD, xD, unit="days")))
   	 
   	 # convert span into seconds
   	 spanS <- (span * 60 * 60 * 24)
   	 
   	 # add to xD and convert into a date string
      spanD <- format(xD + spanS,"%Y-%m-%d")
      
      # prepare a second string: how many days ago / in the future is the "span"th anniversary
      if        (remainD < 0) { m2 <- (paste(span, " days anniversary was on ", spanD, ", ",  
      	                                  abs(remainD), " days ago.\n", sep =""))
      } else if (remainD > 0) { m2 <- (paste(span, " days anniversary will be on ", spanD, ", in ", 
      	                                   abs(remainD),  " days.\n",    sep ="")) 
      } else                  { m2 <- (paste(span, " days anniversary is today.\n",
      	                                                          sep ="")) }
   } else { #  no argument was set for span. Prepare empty string
   	       # (or encourage user to set a parameter).
            m2 <- "";
   }
   
   # print both output strings.
   cat(m1)
   cat(m2)
   return(invisible())
   
} #end function


Sample usage
> myLifeDays()

Usage:
------
myLifeDays(date, ...)

Arguments:
----------
date    a string in "YYYY-MM-DD" format.
span    calculate date for *span* of days since *date*.

> myLifeDays("1685-03-31") # Johann Sebastian Bach's birthday
1685-03-31 12:00:00 was 119629 days ago.
> myLifeDays("1685-03-31", 120000)
1685-03-31 12:00:00 was 119629 days ago.
120000 days anniversary will be on 2013-10-18, in 372 days.


 

Notes and references

  1. Thanks to Xuyao Li for pointing this out