Dealing with dates and time with lubridate

Most data we collect has a time stamp. The time stamp indicate the date and time the data was collected. Dealing with dates is a complicated task becuase of different formats and the time zones. R has built in functions that make your life working with dates and times a bit easier. In addition, @lubridate developed a lubridate package that allows to work smoothly with dates and times. Therefore, this chapter deals specifically with dates and times. We deal with both gregorian calender that sound familiar to us as we see this structure in our wall calender. We will also deals with julian format, commonly used by oceanographers and meteorologist that counts days from specific reference.

Getting the Date and Time with localtime

To get current date and time information that is pulled from your computer internal clock, simply use the Sys.Date()

Sys.Date()
[1] "2021-09-04"

For for dates and times simply use Sys.time

Sys.time()
[1] "2021-09-04 17:54:15 EAT"

use Sys.timezone() function to locate the timezone of your machine

Sys.timezone()
[1] "Africa/Nairobi"

Note that the default date format in R is YYY-MM-DD.

Converting strings to Dates

When date and times variables are imported into R’s worksapce, the functions that reads the file tend to convert the date into character. Hence, we ought to convert these strings back to date format.

convert strings to dates

The function as.Date() is used to convert character into YYY-MM-DD date format.

sampling.date = c("2019-02-19")

as.Date(sampling.date)
[1] "2019-02-19"

There are times the date comes in a format that is weird and R can not figure out how to put in the right order. To obtain the list of all available dates and times conversion format, just write ?strftime() in console. A help window with the specific information will pop-up.

sampling.date = c("05/06/2019")
as.Date(sampling.date, format = "%d/%m/%Y")
[1] "2019-06-05"

Converting date with lubridate package

The lubridate package has dozens of functions that convert dates and times from different characters format. The advantage of using the lubridate package is the fact that it automatically recognizes the common separators used when recording dates. Whether the date were separated with "-","/",".","and", or without separators, lubridate will recognize it. The only trick thing you have to bother is to specify the right order of date elements to determine the appropriate function applied. The table 1 indicates the date variables and the corresponding function

data.frame(order = c("year,month,day", "year,day,month", "month,day,year","day,month,year", "hour,minute", "hour,minute,second", "year,month,day,hour,minute,second"),
           fun = c("ymd()", "ydm()", "mdy","dmy", "hm", "hms", "ymd_hms")) %>%
  kableExtra::kable(format = "html", caption = "Lubridate's function for dealing with dates and times", col.names = c("Date and Time variables", "lubridate's function")) %>%
  kableExtra::column_spec(column = 1, width = "8cm")
Table 1: Lubridate’s function for dealing with dates and times
Date and Time variables lubridate’s function
year,month,day ymd()
year,day,month ydm()
month,day,year mdy
day,month,year dmy
hour,minute hm
hour,minute,second hms
year,month,day,hour,minute,second ymd_hms
require(lubridate)
require(tidyverse)
require(magrittr)
require(oce)

Create Dates by Merging Data

Sometimes your date data are collected in separate elements. To convert these separate data into one date object incorporate the ISOdate() function:

yr <- c ("2012", "2013", "2014", "2015") 
mo <- c ("1", "5", "7", "2") 
day <- c ("02", "22", "15", "28") 

ISOdate converts to a POSIXct object

ISOdate (year = yr, month = mo, day = day) 
[1] "2012-01-02 12:00:00 GMT" "2013-05-22 12:00:00 GMT"
[3] "2014-07-15 12:00:00 GMT" "2015-02-28 12:00:00 GMT"
as.Date ( ISOdate (year = yr, month = mo, day = day)) 
[1] "2012-01-02" "2013-05-22" "2014-07-15" "2015-02-28"

Note that ISODate() also has arguments to accept data for hours, minutes, seconds, and time-zone if you need to merge all these separate components.

Extract and Manipulate Parts of Dates

To extract and manipulate individual elements of a date I typically use the lubridate package due to its simplistic function syntax . The functions provided by lubridate to perform extraction and manipulation of dates are shown in table 2

data.frame(date = c("Year", "Month", "Week", "Day of year","Day of month", "Day of week", "Hour", "Minute", "Second", "Time zone"), accessor = c("year()","month()","week()","yday()","mday()","wday()","hour()","minute()","second()","tz()")) %>%
  kableExtra::kable(format = "html", caption = "Accessor functions for lubridate", col.names = c("Date component", "Accessor")) %>%
  kableExtra::column_spec(column = 1, width = "5cm")
Table 2: Accessor functions for lubridate
Date component Accessor
Year year()
Month month()
Week week()
Day of year yday()
Day of month mday()
Day of week wday()
Hour hour()
Minute minute()
Second second()
Time zone tz()

To extract an individual element of the date variable you simply use the accessor function desired. Note that the accessor variables have additional arguments that can be used to show the name of the date element in full or abbreviated form

x <- c ("2015-07-01", "2015-08-01", "2015-09-01") 
lubridate::year (x) 
[1] 2015 2015 2015
lubridate::month(x)
[1] 7 8 9
# show abbreviated name 
lubridate::month (x, label = TRUE)
[1] Jul Aug Sep
12 Levels: Jan < Feb < Mar < Apr < May < Jun < Jul < Aug < Sep < ... < Dec
# show unabbreviated name 
lubridate::month (x, label = TRUE, abbr = FALSE) 
[1] July      August    September
12 Levels: January < February < March < April < May < June < ... < December
lubridate::wday (x, label = TRUE, abbr = FALSE) 
[1] Wednesday Saturday  Tuesday  
7 Levels: Sunday < Monday < Tuesday < Wednesday < Thursday < ... < Saturday

To manipulate or change the values of date elements we simply use the accessor function to extract the element of choice and then use the assignment function to assign a new value

# convert to date format 
x = lubridate::ymd(x) 
x
[1] "2015-07-01" "2015-08-01" "2015-09-01"
# change the days for the dates 
lubridate::mday (x) 
[1] 1 1 1

Creating Date Sequences

o create a sequence of dates we can leverage the seq () function. As with numeric vectors , you have to specify at least three of the four arguments ( from , to , by , and length.out ).

## by years
seq(lubridate::ymd("2010-1-1"), lubridate::ymd("2018-1-1"), by = "years") 
[1] "2010-01-01" "2011-01-01" "2012-01-01" "2013-01-01" "2014-01-01"
[6] "2015-01-01" "2016-01-01" "2017-01-01" "2018-01-01"
## by quuarters
seq(lubridate::ymd("2016-1-1"), lubridate::ymd("2018-12-31"), by = "quarters") 
 [1] "2016-01-01" "2016-04-01" "2016-07-01" "2016-10-01" "2017-01-01"
 [6] "2017-04-01" "2017-07-01" "2017-10-01" "2018-01-01" "2018-04-01"
[11] "2018-07-01" "2018-10-01"
## by month
seq(lubridate::ymd("2017-10-1"), lubridate::ymd("2018-09-1"), by = "month") 
 [1] "2017-10-01" "2017-11-01" "2017-12-01" "2018-01-01" "2018-02-01"
 [6] "2018-03-01" "2018-04-01" "2018-05-01" "2018-06-01" "2018-07-01"
[11] "2018-08-01" "2018-09-01"
## by week
seq(lubridate::ymd("2018-10-1"), lubridate::ymd("2018-12-1"), by = "week") 
[1] "2018-10-01" "2018-10-08" "2018-10-15" "2018-10-22" "2018-10-29"
[6] "2018-11-05" "2018-11-12" "2018-11-19" "2018-11-26"
## by days
seq(lubridate::ymd("2018-1-1"), lubridate::ymd("2018-1-31"), by = "3.5 days") 
 [1] "2018-01-01" "2018-01-04" "2018-01-07" "2018-01-10" "2018-01-13"
 [6] "2018-01-16" "2018-01-19" "2018-01-22" "2018-01-25" "2018-01-28"
[11] "2018-01-31"
## by days
seq(lubridate::ymd("2018-1-1"), lubridate::ymd("2018-1-10"), by = "day") 
 [1] "2018-01-01" "2018-01-02" "2018-01-03" "2018-01-04" "2018-01-05"
 [6] "2018-01-06" "2018-01-07" "2018-01-08" "2018-01-09" "2018-01-10"

Calculations with Dates

Since R stores date and time objects as numbers, this allows you to perform various calculations such as logical comparisons, addition, subtraction, and working with durations .

x <- Sys.Date() 
x 
[1] "2021-09-04"
y = lubridate::ymd("2015-09-11") 
x > y 
[1] TRUE
x - y
Time difference of 2185 days

Time difference of 15 days

The nice thing about the date/time classes is that they keep track of leap years, leap seconds, daylight savings , and time zones. Use OlsonNames() for a full list of acceptable time zone specifications.

## create sequence and identify leap years
seq(lubridate::dmy("290212"), lubridate::ymd("170228"), by = "year")
[1] "2012-02-29" "2013-03-01" "2014-03-01" "2015-03-01" "2016-02-29"
# last leap year 
x <- lubridate::ymd("2016-03-1") 
y <- lubridate::ymd("2016-02-28") 
x - y 
Time difference of 2 days

example with time zones

x <- lubridate::now(tzone ="US/Eastern") 
y <- lubridate::now(tzone ="Africa/Nairobi")

x;y;y == x;y-x
[1] "2021-09-04 10:54:17 EDT"
[1] "2021-09-04 17:54:17 EAT"
[1] FALSE
Time difference of 0.0019629 secs

We can also deal with time spans by using the duration functions in lubridate. Durations simply measure the time span between start and end dates. Using base R date functions for duration calculations is tedious and often results in wrong measurements. lubridate provides simplistic syntax to calculate durations with the desired measurement (seconds, minutes, hours, etc.).

 # create new duration (represented in seconds) 
lubridate::duration (60) 
[1] "60s (~1 minutes)"
# create durations for minutes, hours, years 
lubridate::dminutes (1)
[1] "60s (~1 minutes)"
lubridate::dhours(1)
[1] "3600s (~1 hours)"
lubridate::dyears(1)
[1] "31557600s (~1 years)"
# add/subtract durations from date/time object 
x <- lubridate::ymd_hms ("2015-09-22 12:00:00") 
x + lubridate::dhours (10) 
[1] "2015-09-22 22:00:00 UTC"
x + lubridate::dhours (10) + lubridate::dminutes (33) + lubridate::dseconds (54) 
[1] "2015-09-22 22:33:54 UTC"