Chapter 15 Dates and Times in R

15.1 What makes dates and times special

Dates and times can be a headache to work with. They may look just like numbers, but they don’t follow regular number rules. Here’s just a handful of things that make dates and times different:

  • There’s no 61 after 60 when working with seconds and minutes;
  • There’s no 25 after 24 when working with hours;
  • There’s no 13 after 12 when working with months;
  • Most years are 365-days long, but some are 366-days long;
  • The same time means something different depending on the time zone;
  • The same time means something different between standard and daylight savings time. And daylight savings time starts on a different day each year.

Keeping track of these rules manually would be a nightmare, especially when working with large datasets. In this Chapter we’re going to learn how to reproducibly deal with dates and times in R by treating these data types as the special kind that they are.

15.2 Definitions

Let’s start with some definitions that I’ll use throughout this Chapter:

  • Date: this type of data contains information on day, month, and year;
  • Time: this type of data contains information on hour, minute, and second;
  • Timestamp: this type of data contains information on both date and time (day, month, year, hour, minute, second);
  • Date-time: a generic term I use to refer to anything that is either a date, a time, or a timestamp.

15.3 Under the hood

As you may remember from Chapter 4, Excel stores dates and times in a spreadsheet as numbers. While what you see may be something of the format “2021-04-15”, the actual data stored under the hood is a number: specifically, the number of days between a reference date (which is 12/31/1899 on Windows and 12/31/1903 on Mac) and the date in the spreadsheet. In R, something similar happens. Base R has two basic classes for date/times, called POSIXct and POSIXlt. POSIXct is the number of seconds since 1/1/1970 00:00:00 (this can be negative for dates before that day, so any dates are supported). POSIXlt is a named list of vectors representing second, minute, hour, day of the month, months since January, years since 1900, day of the week, day of the year, and a flag defining whether it’s daylight savings time or not. For instance:

d <- "2021-04-15"

class(d)
## [1] "character"

If I just write out a date in quotes, this is still just a character string.

d <- as.POSIXct(d)

class(d)
## [1] "POSIXct" "POSIXt"
d
## [1] "2021-04-15 PDT"

But if I convert that character string into POSIXct, now R understands it is a date. What is displayed is the date in my current time zone (which R knows):

Sys.timezone()
## [1] "America/Los_Angeles"

But what’s actually hiding under that date is the number of seconds from 1/1/1970. Let’s see what happens if I convert my date to POSIXlt instead:

d <- as.POSIXlt(d)

class(d)
## [1] "POSIXlt" "POSIXt"
d
## [1] "2021-04-15 PDT"

What’s displayed doesn’t change, but the way R is storing the information under the hood is different.

You may have noticed that each time I converted to POSIXct or POSIXlt R also added the class POSIXt to the object. POSIXt is a class that inherits some properties from both POSIXct and POSIXlt, and it makes operations between the two possible.

Here is how to do the same thing with a date-time string:

dt <- "2021-04-15 13:20:00"

as.POSIXct(dt)
## [1] "2021-04-15 13:20:00 PDT"
as.POSIXlt(dt)
## [1] "2021-04-15 13:20:00 PDT"

Besides POSIXct and POSIXlt, R also support a data type called Date. This is equivalent to Excel’s handling of dates, i.e., the number of days since 1/1/1970:

d <- "2021-04-15"

as.Date(d)
## [1] "2021-04-15"

This data type makes no assumptions about time zones. However, it can only support dates, not times:

dt <- "2021-04-15 13:20:00"

as.Date(dt)
## [1] "2021-04-15"

When we convert a date-time string into Date, the time information is lost.

What if our date was in a different format than “2021-04-15”?

as.Date("04/15/2021")
## Error in charToDate(x): character string is not in a standard unambiguous format
as.POSIXct("04/15/2021")
## Error in as.POSIXlt.character(x, tz, ...): character string is not in a standard unambiguous format
as.POSIXlt("04/15/2021")
## Error in as.POSIXlt.character("04/15/2021"): character string is not in a standard unambiguous format

None of the functions above work because they can’t understand the format of the date. To make this work, we would have to give R a template of how the object is formatted. In our case, the format is “month/day/4-digit year” which is encoded as %m/%d/%Y:

as.Date("04/15/2021", format = "%m/%d/%Y")
## [1] "2021-04-15"
as.POSIXct("04/15/2021", format = "%m/%d/%Y")
## [1] "2021-04-15 PDT"
as.POSIXlt("04/15/2021", format = "%m/%d/%Y")
## [1] "2021-04-15 PDT"

What happens if we want to do some calculations on dates? For example, we want to know how many days went by between two dates. If our dates are formatted using a different data type, we may get some unexpected results:

d1 <- as.POSIXct("2021-04-15")
d2 <- as.POSIXlt("2021-04-16")
d3 <- as.Date("2021-04-17")

d3 - d1
## Warning: Incompatible methods ("-.Date", "-.POSIXt") for "-"
## [1] "-4429200-12-09"
d2 - d3
## Warning: Incompatible methods ("-.POSIXt", "-.Date") for "-"
## Error in d2 - d3: non-numeric argument to binary operator
d3 - d1
## Warning: Incompatible methods ("-.Date", "-.POSIXt") for "-"
## [1] "-4429200-12-09"
d1 - d2
## Time difference of -1 days
d1 - d3
## Warning: Incompatible methods ("-.POSIXt", "-.Date") for "-"
## [1] "2021-04-14 18:47:46 PDT"

Only one of the calculations above (d1 - d2, where we are subtracting a POSIXlt from a POSIXct) worked and returned the right output. All the other ones returned weird outputs and a warning that the methods are incompatible.

The ones above are just a few examples to demonstrate how working with dates and times in R can still be complicated even using dedicated data types available within base R.

15.4 The lubridate package

Artwork by Allison Horst

Figure 15.1: Artwork by Allison Horst

Working with dates and times in R becomes a whole lot easier when using the lubridate package. lubridate is part of the tidyverse and it is meant to make working with dates and time as straightforward as possible. For one, functions in lubridate are designed to behave consistently regardless of the underlying object. No need to keep track of what functions we can or cannot use on different date-time objects: the same handful of function work predictably on all of them. Using lubridate does not mean foregoing base R’s handling of date-time objects: the package works nicely with built-in objects, it just makes them more user-friendly. Let’s look at some of the functionalities of lubridate.

library(lubridate)

15.4.1 Parsing dates and times

lubridate comes with a suite of different functions to parse (i.e., correctly interpret the components of) dates, times, and timestamp. These functions are:

ymd()
mdy()
dmy()

ymd_hms()
mdy_hms()
dmy_hms()

hms()
hm()
ms()

Depending on what format our input data is in, all we need to do is pick the correct function to convert our string into a date-time object:

# This string follows the format "year, month, day", so we use ymd()
ymd("2021-04-15")
## [1] "2021-04-15"
# Separator doesn't matter! And neither do leading zeros.
ymd("2021/4/15")
## [1] "2021-04-15"
# This string follows the format "month, day, year", so we use mdy()
mdy("4/15/2021")
## [1] "2021-04-15"

Note that all of these functions return the same identical output: the output format is always yyyy-mm-dd and the output data type is always Date.

class(ymd("2021-04-15"))
## [1] "Date"
class(ymd("2021/4/15"))
## [1] "Date"
class(mdy("4/15/2021"))
## [1] "Date"

Let’s try with some timestamps:

# This string follows the format "year, month, day, hour, minute, second", 
# so we use ymd_hms()
ymd_hms("2021-04-15 13:53:00")
## [1] "2021-04-15 13:53:00 UTC"
# Separator doesn't matter! 
ymd_hms("2021/4/15 13 53 00")
## [1] "2021-04-15 13:53:00 UTC"
# This string follows the format "month, day, year, hour, minute", so we use mdy_hm()
mdy_hm("4/15/2021 13:53")
## [1] "2021-04-15 13:53:00 UTC"

Again, the output format is always the same and the output class is too (this time it’s POSIXct because there’s a time component in addition to the date):

class(ymd_hms("2021-04-15 13:53:00"))
## [1] "POSIXct" "POSIXt"
class(ymd_hms("2021/4/15 13 53 00"))
## [1] "POSIXct" "POSIXt"
class(mdy_hm("4/15/2021 13:53"))
## [1] "POSIXct" "POSIXt"

What happens if we use a function that doesn’t match the format we are providing in input?

ymd("2021-04-15 13:56:00")
## Warning: All formats failed to parse. No formats found.
## [1] NA

The output is NA and we get a warning that the date failed to parse. That is because ymd() is expecting a year, a month, and a day only; instead, we are giving it a full timestamp (year, month, day, hour, minute, second).

ymd_hms("04/15/2021 13:56:00")
## Warning: All formats failed to parse. No formats found.
## [1] NA

This one failed because ymd_hms() is expecting the elements in this order: year, month, day, hour, minute, second, but we are giving it in a different order (month, day, year, hour, minute, second). So, whenever you get the warning, All formats failed to parse. No formats found. you’ll know that you either passed more components than the function expected, fewer components than the function expected, or you passed them in the wrong order.

One misconception that I frequently see is that people tend to think that the different functions we’ve seen (ymd(), mdy(), dmy(), etc.) are used to tell lubridate what format we want in output; but actually, the output returned is always the same. Those different functions exist so that can choose the appropriate function to match the format of our input, so that the string can be parsed correctly.

15.4.2 Extracting components of a date-time object

The following functions in lubridate let you extract components of a date-time object:

dt <- mdy_hms("04/15/2021 13:56:00")

day(dt)
## [1] 15
month(dt)
## [1] 4
year(dt)
## [1] 2021
hour(dt)
## [1] 13
minute(dt)
## [1] 56
second(dt)
## [1] 0

15.4.3 Time zones

By default, when we create a new timestamp with lubridate, it will be assumed to be in UTC (Coordinated Universal Time):

ymd_hms("2021-04-15 13:56:00")
## [1] "2021-04-15 13:56:00 UTC"

What if the timestamp is actually in a different time zone than that? We can use the argument tz to specify what time zone the data are in when we create the object. For a list of available time zones, we can look up:

OlsonNames()
##   [1] "Africa/Abidjan"                   "Africa/Accra"                    
##   [3] "Africa/Addis_Ababa"               "Africa/Algiers"                  
##   [5] "Africa/Asmara"                    "Africa/Asmera"                   
##   [7] "Africa/Bamako"                    "Africa/Bangui"                   
##   [9] "Africa/Banjul"                    "Africa/Bissau"                   
##  [11] "Africa/Blantyre"                  "Africa/Brazzaville"              
##  [13] "Africa/Bujumbura"                 "Africa/Cairo"                    
##  [15] "Africa/Casablanca"                "Africa/Ceuta"                    
##  [17] "Africa/Conakry"                   "Africa/Dakar"                    
##  [19] "Africa/Dar_es_Salaam"             "Africa/Djibouti"                 
##  [21] "Africa/Douala"                    "Africa/El_Aaiun"                 
##  [23] "Africa/Freetown"                  "Africa/Gaborone"                 
##  [25] "Africa/Harare"                    "Africa/Johannesburg"             
##  [27] "Africa/Juba"                      "Africa/Kampala"                  
##  [29] "Africa/Khartoum"                  "Africa/Kigali"                   
##  [31] "Africa/Kinshasa"                  "Africa/Lagos"                    
##  [33] "Africa/Libreville"                "Africa/Lome"                     
##  [35] "Africa/Luanda"                    "Africa/Lubumbashi"               
##  [37] "Africa/Lusaka"                    "Africa/Malabo"                   
##  [39] "Africa/Maputo"                    "Africa/Maseru"                   
##  [41] "Africa/Mbabane"                   "Africa/Mogadishu"                
##  [43] "Africa/Monrovia"                  "Africa/Nairobi"                  
##  [45] "Africa/Ndjamena"                  "Africa/Niamey"                   
##  [47] "Africa/Nouakchott"                "Africa/Ouagadougou"              
##  [49] "Africa/Porto-Novo"                "Africa/Sao_Tome"                 
##  [51] "Africa/Timbuktu"                  "Africa/Tripoli"                  
##  [53] "Africa/Tunis"                     "Africa/Windhoek"                 
##  [55] "America/Adak"                     "America/Anchorage"               
##  [57] "America/Anguilla"                 "America/Antigua"                 
##  [59] "America/Araguaina"                "America/Argentina/Buenos_Aires"  
##  [61] "America/Argentina/Catamarca"      "America/Argentina/ComodRivadavia"
##  [63] "America/Argentina/Cordoba"        "America/Argentina/Jujuy"         
##  [65] "America/Argentina/La_Rioja"       "America/Argentina/Mendoza"       
##  [67] "America/Argentina/Rio_Gallegos"   "America/Argentina/Salta"         
##  [69] "America/Argentina/San_Juan"       "America/Argentina/San_Luis"      
##  [71] "America/Argentina/Tucuman"        "America/Argentina/Ushuaia"       
##  [73] "America/Aruba"                    "America/Asuncion"                
##  [75] "America/Atikokan"                 "America/Atka"                    
##  [77] "America/Bahia"                    "America/Bahia_Banderas"          
##  [79] "America/Barbados"                 "America/Belem"                   
##  [81] "America/Belize"                   "America/Blanc-Sablon"            
##  [83] "America/Boa_Vista"                "America/Bogota"                  
##  [85] "America/Boise"                    "America/Buenos_Aires"            
##  [87] "America/Cambridge_Bay"            "America/Campo_Grande"            
##  [89] "America/Cancun"                   "America/Caracas"                 
##  [91] "America/Catamarca"                "America/Cayenne"                 
##  [93] "America/Cayman"                   "America/Chicago"                 
##  [95] "America/Chihuahua"                "America/Ciudad_Juarez"           
##  [97] "America/Coral_Harbour"            "America/Cordoba"                 
##  [99] "America/Costa_Rica"               "America/Creston"                 
## [101] "America/Cuiaba"                   "America/Curacao"                 
## [103] "America/Danmarkshavn"             "America/Dawson"                  
## [105] "America/Dawson_Creek"             "America/Denver"                  
## [107] "America/Detroit"                  "America/Dominica"                
## [109] "America/Edmonton"                 "America/Eirunepe"                
## [111] "America/El_Salvador"              "America/Ensenada"                
## [113] "America/Fort_Nelson"              "America/Fort_Wayne"              
## [115] "America/Fortaleza"                "America/Glace_Bay"               
## [117] "America/Godthab"                  "America/Goose_Bay"               
## [119] "America/Grand_Turk"               "America/Grenada"                 
## [121] "America/Guadeloupe"               "America/Guatemala"               
## [123] "America/Guayaquil"                "America/Guyana"                  
## [125] "America/Halifax"                  "America/Havana"                  
## [127] "America/Hermosillo"               "America/Indiana/Indianapolis"    
## [129] "America/Indiana/Knox"             "America/Indiana/Marengo"         
## [131] "America/Indiana/Petersburg"       "America/Indiana/Tell_City"       
## [133] "America/Indiana/Vevay"            "America/Indiana/Vincennes"       
## [135] "America/Indiana/Winamac"          "America/Indianapolis"            
## [137] "America/Inuvik"                   "America/Iqaluit"                 
## [139] "America/Jamaica"                  "America/Jujuy"                   
## [141] "America/Juneau"                   "America/Kentucky/Louisville"     
## [143] "America/Kentucky/Monticello"      "America/Knox_IN"                 
## [145] "America/Kralendijk"               "America/La_Paz"                  
## [147] "America/Lima"                     "America/Los_Angeles"             
## [149] "America/Louisville"               "America/Lower_Princes"           
## [151] "America/Maceio"                   "America/Managua"                 
## [153] "America/Manaus"                   "America/Marigot"                 
## [155] "America/Martinique"               "America/Matamoros"               
## [157] "America/Mazatlan"                 "America/Mendoza"                 
## [159] "America/Menominee"                "America/Merida"                  
## [161] "America/Metlakatla"               "America/Mexico_City"             
## [163] "America/Miquelon"                 "America/Moncton"                 
## [165] "America/Monterrey"                "America/Montevideo"              
## [167] "America/Montreal"                 "America/Montserrat"              
## [169] "America/Nassau"                   "America/New_York"                
## [171] "America/Nipigon"                  "America/Nome"                    
## [173] "America/Noronha"                  "America/North_Dakota/Beulah"     
## [175] "America/North_Dakota/Center"      "America/North_Dakota/New_Salem"  
## [177] "America/Nuuk"                     "America/Ojinaga"                 
## [179] "America/Panama"                   "America/Pangnirtung"             
## [181] "America/Paramaribo"               "America/Phoenix"                 
## [183] "America/Port-au-Prince"           "America/Port_of_Spain"           
## [185] "America/Porto_Acre"               "America/Porto_Velho"             
## [187] "America/Puerto_Rico"              "America/Punta_Arenas"            
## [189] "America/Rainy_River"              "America/Rankin_Inlet"            
## [191] "America/Recife"                   "America/Regina"                  
## [193] "America/Resolute"                 "America/Rio_Branco"              
## [195] "America/Rosario"                  "America/Santa_Isabel"            
## [197] "America/Santarem"                 "America/Santiago"                
## [199] "America/Santo_Domingo"            "America/Sao_Paulo"               
## [201] "America/Scoresbysund"             "America/Shiprock"                
## [203] "America/Sitka"                    "America/St_Barthelemy"           
## [205] "America/St_Johns"                 "America/St_Kitts"                
## [207] "America/St_Lucia"                 "America/St_Thomas"               
## [209] "America/St_Vincent"               "America/Swift_Current"           
## [211] "America/Tegucigalpa"              "America/Thule"                   
## [213] "America/Thunder_Bay"              "America/Tijuana"                 
## [215] "America/Toronto"                  "America/Tortola"                 
## [217] "America/Vancouver"                "America/Virgin"                  
## [219] "America/Whitehorse"               "America/Winnipeg"                
## [221] "America/Yakutat"                  "America/Yellowknife"             
## [223] "Antarctica/Casey"                 "Antarctica/Davis"                
## [225] "Antarctica/DumontDUrville"        "Antarctica/Macquarie"            
## [227] "Antarctica/Mawson"                "Antarctica/McMurdo"              
## [229] "Antarctica/Palmer"                "Antarctica/Rothera"              
## [231] "Antarctica/South_Pole"            "Antarctica/Syowa"                
## [233] "Antarctica/Troll"                 "Antarctica/Vostok"               
## [235] "Arctic/Longyearbyen"              "Asia/Aden"                       
## [237] "Asia/Almaty"                      "Asia/Amman"                      
## [239] "Asia/Anadyr"                      "Asia/Aqtau"                      
## [241] "Asia/Aqtobe"                      "Asia/Ashgabat"                   
## [243] "Asia/Ashkhabad"                   "Asia/Atyrau"                     
## [245] "Asia/Baghdad"                     "Asia/Bahrain"                    
## [247] "Asia/Baku"                        "Asia/Bangkok"                    
## [249] "Asia/Barnaul"                     "Asia/Beirut"                     
## [251] "Asia/Bishkek"                     "Asia/Brunei"                     
## [253] "Asia/Calcutta"                    "Asia/Chita"                      
## [255] "Asia/Choibalsan"                  "Asia/Chongqing"                  
## [257] "Asia/Chungking"                   "Asia/Colombo"                    
## [259] "Asia/Dacca"                       "Asia/Damascus"                   
## [261] "Asia/Dhaka"                       "Asia/Dili"                       
## [263] "Asia/Dubai"                       "Asia/Dushanbe"                   
## [265] "Asia/Famagusta"                   "Asia/Gaza"                       
## [267] "Asia/Harbin"                      "Asia/Hebron"                     
## [269] "Asia/Ho_Chi_Minh"                 "Asia/Hong_Kong"                  
## [271] "Asia/Hovd"                        "Asia/Irkutsk"                    
## [273] "Asia/Istanbul"                    "Asia/Jakarta"                    
## [275] "Asia/Jayapura"                    "Asia/Jerusalem"                  
## [277] "Asia/Kabul"                       "Asia/Kamchatka"                  
## [279] "Asia/Karachi"                     "Asia/Kashgar"                    
## [281] "Asia/Kathmandu"                   "Asia/Katmandu"                   
## [283] "Asia/Khandyga"                    "Asia/Kolkata"                    
## [285] "Asia/Krasnoyarsk"                 "Asia/Kuala_Lumpur"               
## [287] "Asia/Kuching"                     "Asia/Kuwait"                     
## [289] "Asia/Macao"                       "Asia/Macau"                      
## [291] "Asia/Magadan"                     "Asia/Makassar"                   
## [293] "Asia/Manila"                      "Asia/Muscat"                     
## [295] "Asia/Nicosia"                     "Asia/Novokuznetsk"               
## [297] "Asia/Novosibirsk"                 "Asia/Omsk"                       
## [299] "Asia/Oral"                        "Asia/Phnom_Penh"                 
## [301] "Asia/Pontianak"                   "Asia/Pyongyang"                  
## [303] "Asia/Qatar"                       "Asia/Qostanay"                   
## [305] "Asia/Qyzylorda"                   "Asia/Rangoon"                    
## [307] "Asia/Riyadh"                      "Asia/Saigon"                     
## [309] "Asia/Sakhalin"                    "Asia/Samarkand"                  
## [311] "Asia/Seoul"                       "Asia/Shanghai"                   
## [313] "Asia/Singapore"                   "Asia/Srednekolymsk"              
## [315] "Asia/Taipei"                      "Asia/Tashkent"                   
## [317] "Asia/Tbilisi"                     "Asia/Tehran"                     
## [319] "Asia/Tel_Aviv"                    "Asia/Thimbu"                     
## [321] "Asia/Thimphu"                     "Asia/Tokyo"                      
## [323] "Asia/Tomsk"                       "Asia/Ujung_Pandang"              
## [325] "Asia/Ulaanbaatar"                 "Asia/Ulan_Bator"                 
## [327] "Asia/Urumqi"                      "Asia/Ust-Nera"                   
## [329] "Asia/Vientiane"                   "Asia/Vladivostok"                
## [331] "Asia/Yakutsk"                     "Asia/Yangon"                     
## [333] "Asia/Yekaterinburg"               "Asia/Yerevan"                    
## [335] "Atlantic/Azores"                  "Atlantic/Bermuda"                
## [337] "Atlantic/Canary"                  "Atlantic/Cape_Verde"             
## [339] "Atlantic/Faeroe"                  "Atlantic/Faroe"                  
## [341] "Atlantic/Jan_Mayen"               "Atlantic/Madeira"                
## [343] "Atlantic/Reykjavik"               "Atlantic/South_Georgia"          
## [345] "Atlantic/St_Helena"               "Atlantic/Stanley"                
## [347] "Australia/ACT"                    "Australia/Adelaide"              
## [349] "Australia/Brisbane"               "Australia/Broken_Hill"           
## [351] "Australia/Canberra"               "Australia/Currie"                
## [353] "Australia/Darwin"                 "Australia/Eucla"                 
## [355] "Australia/Hobart"                 "Australia/LHI"                   
## [357] "Australia/Lindeman"               "Australia/Lord_Howe"             
## [359] "Australia/Melbourne"              "Australia/North"                 
## [361] "Australia/NSW"                    "Australia/Perth"                 
## [363] "Australia/Queensland"             "Australia/South"                 
## [365] "Australia/Sydney"                 "Australia/Tasmania"              
## [367] "Australia/Victoria"               "Australia/West"                  
## [369] "Australia/Yancowinna"             "Brazil/Acre"                     
## [371] "Brazil/DeNoronha"                 "Brazil/East"                     
## [373] "Brazil/West"                      "Canada/Atlantic"                 
## [375] "Canada/Central"                   "Canada/Eastern"                  
## [377] "Canada/Mountain"                  "Canada/Newfoundland"             
## [379] "Canada/Pacific"                   "Canada/Saskatchewan"             
## [381] "Canada/Yukon"                     "CET"                             
## [383] "Chile/Continental"                "Chile/EasterIsland"              
## [385] "CST6CDT"                          "Cuba"                            
## [387] "EET"                              "Egypt"                           
## [389] "Eire"                             "EST"                             
## [391] "EST5EDT"                          "Etc/GMT"                         
## [393] "Etc/GMT-0"                        "Etc/GMT-1"                       
## [395] "Etc/GMT-10"                       "Etc/GMT-11"                      
## [397] "Etc/GMT-12"                       "Etc/GMT-13"                      
## [399] "Etc/GMT-14"                       "Etc/GMT-2"                       
## [401] "Etc/GMT-3"                        "Etc/GMT-4"                       
## [403] "Etc/GMT-5"                        "Etc/GMT-6"                       
## [405] "Etc/GMT-7"                        "Etc/GMT-8"                       
## [407] "Etc/GMT-9"                        "Etc/GMT+0"                       
## [409] "Etc/GMT+1"                        "Etc/GMT+10"                      
## [411] "Etc/GMT+11"                       "Etc/GMT+12"                      
## [413] "Etc/GMT+2"                        "Etc/GMT+3"                       
## [415] "Etc/GMT+4"                        "Etc/GMT+5"                       
## [417] "Etc/GMT+6"                        "Etc/GMT+7"                       
## [419] "Etc/GMT+8"                        "Etc/GMT+9"                       
## [421] "Etc/GMT0"                         "Etc/Greenwich"                   
## [423] "Etc/UCT"                          "Etc/Universal"                   
## [425] "Etc/UTC"                          "Etc/Zulu"                        
## [427] "Europe/Amsterdam"                 "Europe/Andorra"                  
## [429] "Europe/Astrakhan"                 "Europe/Athens"                   
## [431] "Europe/Belfast"                   "Europe/Belgrade"                 
## [433] "Europe/Berlin"                    "Europe/Bratislava"               
## [435] "Europe/Brussels"                  "Europe/Bucharest"                
## [437] "Europe/Budapest"                  "Europe/Busingen"                 
## [439] "Europe/Chisinau"                  "Europe/Copenhagen"               
## [441] "Europe/Dublin"                    "Europe/Gibraltar"                
## [443] "Europe/Guernsey"                  "Europe/Helsinki"                 
## [445] "Europe/Isle_of_Man"               "Europe/Istanbul"                 
## [447] "Europe/Jersey"                    "Europe/Kaliningrad"              
## [449] "Europe/Kiev"                      "Europe/Kirov"                    
## [451] "Europe/Kyiv"                      "Europe/Lisbon"                   
## [453] "Europe/Ljubljana"                 "Europe/London"                   
## [455] "Europe/Luxembourg"                "Europe/Madrid"                   
## [457] "Europe/Malta"                     "Europe/Mariehamn"                
## [459] "Europe/Minsk"                     "Europe/Monaco"                   
## [461] "Europe/Moscow"                    "Europe/Nicosia"                  
## [463] "Europe/Oslo"                      "Europe/Paris"                    
## [465] "Europe/Podgorica"                 "Europe/Prague"                   
## [467] "Europe/Riga"                      "Europe/Rome"                     
## [469] "Europe/Samara"                    "Europe/San_Marino"               
## [471] "Europe/Sarajevo"                  "Europe/Saratov"                  
## [473] "Europe/Simferopol"                "Europe/Skopje"                   
## [475] "Europe/Sofia"                     "Europe/Stockholm"                
## [477] "Europe/Tallinn"                   "Europe/Tirane"                   
## [479] "Europe/Tiraspol"                  "Europe/Ulyanovsk"                
## [481] "Europe/Uzhgorod"                  "Europe/Vaduz"                    
## [483] "Europe/Vatican"                   "Europe/Vienna"                   
## [485] "Europe/Vilnius"                   "Europe/Volgograd"                
## [487] "Europe/Warsaw"                    "Europe/Zagreb"                   
## [489] "Europe/Zaporozhye"                "Europe/Zurich"                   
## [491] "GB"                               "GB-Eire"                         
## [493] "GMT"                              "GMT-0"                           
## [495] "GMT+0"                            "GMT0"                            
## [497] "Greenwich"                        "Hongkong"                        
## [499] "HST"                              "Iceland"                         
## [501] "Indian/Antananarivo"              "Indian/Chagos"                   
## [503] "Indian/Christmas"                 "Indian/Cocos"                    
## [505] "Indian/Comoro"                    "Indian/Kerguelen"                
## [507] "Indian/Mahe"                      "Indian/Maldives"                 
## [509] "Indian/Mauritius"                 "Indian/Mayotte"                  
## [511] "Indian/Reunion"                   "Iran"                            
## [513] "Israel"                           "Jamaica"                         
## [515] "Japan"                            "Kwajalein"                       
## [517] "Libya"                            "MET"                             
## [519] "Mexico/BajaNorte"                 "Mexico/BajaSur"                  
## [521] "Mexico/General"                   "MST"                             
## [523] "MST7MDT"                          "Navajo"                          
## [525] "NZ"                               "NZ-CHAT"                         
## [527] "Pacific/Apia"                     "Pacific/Auckland"                
## [529] "Pacific/Bougainville"             "Pacific/Chatham"                 
## [531] "Pacific/Chuuk"                    "Pacific/Easter"                  
## [533] "Pacific/Efate"                    "Pacific/Enderbury"               
## [535] "Pacific/Fakaofo"                  "Pacific/Fiji"                    
## [537] "Pacific/Funafuti"                 "Pacific/Galapagos"               
## [539] "Pacific/Gambier"                  "Pacific/Guadalcanal"             
## [541] "Pacific/Guam"                     "Pacific/Honolulu"                
## [543] "Pacific/Johnston"                 "Pacific/Kanton"                  
## [545] "Pacific/Kiritimati"               "Pacific/Kosrae"                  
## [547] "Pacific/Kwajalein"                "Pacific/Majuro"                  
## [549] "Pacific/Marquesas"                "Pacific/Midway"                  
## [551] "Pacific/Nauru"                    "Pacific/Niue"                    
## [553] "Pacific/Norfolk"                  "Pacific/Noumea"                  
## [555] "Pacific/Pago_Pago"                "Pacific/Palau"                   
## [557] "Pacific/Pitcairn"                 "Pacific/Pohnpei"                 
## [559] "Pacific/Ponape"                   "Pacific/Port_Moresby"            
## [561] "Pacific/Rarotonga"                "Pacific/Saipan"                  
## [563] "Pacific/Samoa"                    "Pacific/Tahiti"                  
## [565] "Pacific/Tarawa"                   "Pacific/Tongatapu"               
## [567] "Pacific/Truk"                     "Pacific/Wake"                    
## [569] "Pacific/Wallis"                   "Pacific/Yap"                     
## [571] "Poland"                           "Portugal"                        
## [573] "PRC"                              "PST8PDT"                         
## [575] "ROC"                              "ROK"                             
## [577] "Singapore"                        "Turkey"                          
## [579] "UCT"                              "Universal"                       
## [581] "US/Alaska"                        "US/Aleutian"                     
## [583] "US/Arizona"                       "US/Central"                      
## [585] "US/East-Indiana"                  "US/Eastern"                      
## [587] "US/Hawaii"                        "US/Indiana-Starke"               
## [589] "US/Michigan"                      "US/Mountain"                     
## [591] "US/Pacific"                       "US/Samoa"                        
## [593] "UTC"                              "W-SU"                            
## [595] "WET"                              "Zulu"                            
## attr(,"Version")
## [1] "2023c"

Our time zone is “America/Denver”. Let’s specify that when creating our object:

dt <- ymd_hms("2021-04-15 13:56:00", tz = "America/Denver")

dt
## [1] "2021-04-15 13:56:00 MDT"

Notice that lubridate knows that 2021-04-15 is during Daylight Savings Time, and it automatically assigns MDT instead of MST.

Now we can convert this object in whichever time zone we want. For instance, when it’s 13:56:00 in Logan, UT on April 15th 2021, what time is it in Rome, Italy?

with_tz(dt, tz = "Europe/Rome")
## [1] "2021-04-15 21:56:00 CEST"

And what time is that in New York City?

with_tz(dt, tz = "America/New_York")
## [1] "2021-04-15 15:56:00 EDT"

What if we didn’t mean to get the equivalent of “2021-04-15 13:56:00 MDT” in another time zone, but instead we wanted to keep the timestamp exactly as it is but set it in a different time zone? We can use force_tz():

dt
## [1] "2021-04-15 13:56:00 MDT"
force_tz(dt, tz = "America/New_York")
## [1] "2021-04-15 13:56:00 EDT"

15.4.4 Time spans

lubridate works with three types of time spans:

  • Periods are fixed amounts of time, ignoring any time irregularities;
  • Durations are amounts of time that account for time irregularities such as leap years or months of different length;
  • Intervals are stretches of time between a specific start date and end date.

Let’s look at a few examples to illustrate the difference between these. For example, we can get a period of N months by using the function months(), and a duration of N months by using the function dmonths():

class(months(2))
## [1] "Period"
## attr(,"package")
## [1] "lubridate"
class(dmonths(2))
## [1] "Duration"
## attr(,"package")
## [1] "lubridate"
dt + months(2)
## [1] "2021-06-15 13:56:00 MDT"
dt + dmonths(2)
## [1] "2021-06-15 10:56:00 MDT"

Our dt timestamp is in April. Adding a period of 2 months to dt means simply changing the month component to June, leaving everything else unchanged. Adding a duration of 2 months to dt means adding the average equivalent of 2 months in seconds (not all months have the same amount of seconds in them because the duration of a month changes between 28 and 31 days, so that’s why I specified “average”). That’s why the result when we add a 2-month duration to dt is 3 hours earlier than when we add a 2-month period.

Periods and durations are useful for doing math with date-time objects. We can answer questions such as, what time will it be in 1 hour and 30 minutes?

dt + hours(1) + minutes(30)
## [1] "2021-04-15 15:26:00 MDT"

Or, say that we want to know what day of the week it was today, 3 years ago:

dt_minus3y <- dt - years(3)

wday(dt_minus3y, label = TRUE) # wday() returns the day of the week
## [1] Sun
## Levels: Sun < Mon < Tue < Wed < Thu < Fri < Sat

Three years ago today was Sunday!

Unlike periods and durations, intervals are especially handy for some type of operations where it matters when exactly a time span takes place (not just how long it is). For example, say that we have a period that goes from January 19th, 2021 (our first class this semester) to April 27th, 2021 (our last class this semester):

# There are two equivalent ways to create an interval: 

# Option 1
s <- interval(ymd("2021-01-19", tz = "America/Denver"), ymd("2021-04-27", tz = "America/Denver"))

# Option 2
s <- ymd("2021-01-19", tz = "America/Denver") %--% ymd("2021-04-27", tz = "America/Denver")

class(s)
## [1] "Interval"
## attr(,"package")
## [1] "lubridate"

We can get the start or end date of an interval as follows:

int_start(s)
## [1] "2021-01-19 MST"
int_end(s)
## [1] "2021-04-27 MDT"

We can check how long the interval is:

int_length(s) # default output is in seconds
## [1] 8463600

We can check if any arbitrary date falls within that interval:

ymd("2021-03-14") %within% s
## [1] TRUE
ymd("1998-03-14") %within% s
## [1] FALSE

We can shift the interval up or down by a certain time span:

int_shift(s, by = months(1)) # push back by one month
## [1] 2021-02-19 MST--2021-05-27 MDT
int_shift(s, by = days(-7)) # bring forward by one week
## [1] 2021-01-12 MST--2021-04-20 MDT

We can create multiple intervals and check whether they overlap:

i <- interval(ymd("2017-07-24", tz = "America/Denver"), ymd("2021-02-12", tz = "America/Denver"))

int_overlaps(i, s)
## [1] TRUE

15.5 References