suppressPackageStartupMessages(library("tidyverse"))
package 㤼㸱tidyverse㤼㸲 was built under R version 3.6.3
suppressPackageStartupMessages(library("lubridate"))

1. Why is there months() but no dmonths()?

There is no direct unambiguous value of months in seconds since months have differing numbers of days.

  • 31 days: January, March, May, July, August, October
  • 30 days: April, June, September, November, December
  • 28 or 29 days: February

The month is not a duration of time defined independently of when it occurs, but a special interval between two dates.

2. Explain days(overnight * 1) to someone who has just started learning R. How does it work?

The variable overnight is equal to TRUE or FALSE. If it is an overnight flight, this becomes 1 day, and if not, then overnight = 0, and no days are added to the date.

3. Create a vector of dates giving the first day of every month in 2015. Create a vector of dates giving the first day of every month in the current year.

A vector of the first day of the month for every month in 2015:

ymd("2015-01-01") + months(0:11)
 [1] "2015-01-01" "2015-02-01" "2015-03-01" "2015-04-01" "2015-05-01" "2015-06-01" "2015-07-01"
 [8] "2015-08-01" "2015-09-01" "2015-10-01" "2015-11-01" "2015-12-01"

To get the vector of the first day of the month for this year, we first need to figure out what this year is, and get January 1st of it. I can do that by taking today() and truncating it to the year using floor_date():

floor_date(today(), unit = "year") + months(0:11)
 [1] "2020-01-01" "2020-02-01" "2020-03-01" "2020-04-01" "2020-05-01" "2020-06-01" "2020-07-01"
 [8] "2020-08-01" "2020-09-01" "2020-10-01" "2020-11-01" "2020-12-01"

4. Write a function that given your birthday (as a date), returns how old you are in years.

age <- function(bday) {
  (bday %--% today()) %/% years(1)
}
age(ymd("1990-10-12"))
Note: method with signature 㤼㸱Timespan#Timespan㤼㸲 chosen for function 㤼㸱%/%㤼㸲,
 target signature 㤼㸱Interval#Period㤼㸲.
 "Interval#ANY", "ANY#Period" would also be valid
[1] 29

5. Why can’t (today() %--% (today() + years(1)) / months(1) work?

The code in the question is missing a parentheses. So, I will assume that that the correct code is,

(today() %--% (today() + years(1))) / months(1)
[1] 12

While this code will not display a warning or message, it does not work exactly as expected. The problem is discussed in the Intervals section.

The numerator of the expression, (today() %--% (today() + years(1)), is an interval, which includes both a duration of time and a starting point. The interval has an exact number of seconds. The denominator of the expression, months(1), is a period, which is meaningful to humans but not defined in terms of an exact number of seconds. Months can be 28, 29, 30, or 31 days, so it is not clear what months(1) divide by? The code does not produce a warning message, but it will not always produce the correct result.

To find the number of months within an interval use %/% instead of /,

(today() %--% (today() + years(1))) %/% months(1)
[1] 12

Alternatively, we could define a “month” as 30 days, and run

(today() %--% (today() + years(1))) / days(30)
[1] 12.16667

This approach will not work with today() + years(1), which is not defined for February 29th on leap years:

as.Date("2016-02-29") + years(1)
[1] NA
LS0tDQp0aXRsZTogIlRpbWUgc3BhbnMiDQpvdXRwdXQ6IA0KICBodG1sX25vdGVib29rOg0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KLS0tDQoNCmBgYHtyfQ0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkoInRpZHl2ZXJzZSIpKQ0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkoImx1YnJpZGF0ZSIpKQ0KYGBgDQoNCiMjIyAxLiBXaHkgaXMgdGhlcmUgYG1vbnRocygpYCBidXQgbm8gYGRtb250aHMoKWA/DQoNClRoZXJlIGlzIG5vIGRpcmVjdCB1bmFtYmlndW91cyB2YWx1ZSBvZiBtb250aHMgaW4gc2Vjb25kcyBzaW5jZSBtb250aHMgaGF2ZSBkaWZmZXJpbmcgbnVtYmVycyBvZiBkYXlzLg0KDQogLSAzMSBkYXlzOiBKYW51YXJ5LCBNYXJjaCwgTWF5LCBKdWx5LCBBdWd1c3QsIE9jdG9iZXINCiAtIDMwIGRheXM6IEFwcmlsLCBKdW5lLCBTZXB0ZW1iZXIsIE5vdmVtYmVyLCBEZWNlbWJlcg0KIC0gMjggb3IgMjkgZGF5czogRmVicnVhcnkNCg0KVGhlIG1vbnRoIGlzIG5vdCBhIGR1cmF0aW9uIG9mIHRpbWUgZGVmaW5lZCBpbmRlcGVuZGVudGx5IG9mIHdoZW4gaXQgb2NjdXJzLCBidXQgYSBzcGVjaWFsIGludGVydmFsIGJldHdlZW4gdHdvIGRhdGVzLg0KDQojIyMgMi4gRXhwbGFpbiBgZGF5cyhvdmVybmlnaHQgKiAxKWAgdG8gc29tZW9uZSB3aG8gaGFzIGp1c3Qgc3RhcnRlZCBsZWFybmluZyBSLiBIb3cgZG9lcyBpdCB3b3JrPw0KDQpUaGUgdmFyaWFibGUgYG92ZXJuaWdodGAgaXMgZXF1YWwgdG8gYFRSVUVgIG9yIGBGQUxTRWAuIElmIGl0IGlzIGFuIG92ZXJuaWdodCBmbGlnaHQsIHRoaXMgYmVjb21lcyAxIGRheSwgYW5kIGlmIG5vdCwgdGhlbiBvdmVybmlnaHQgPSAwLCBhbmQgbm8gZGF5cyBhcmUgYWRkZWQgdG8gdGhlIGRhdGUuDQoNCiMjIyAzLiBDcmVhdGUgYSB2ZWN0b3Igb2YgZGF0ZXMgZ2l2aW5nIHRoZSBmaXJzdCBkYXkgb2YgZXZlcnkgbW9udGggaW4gMjAxNS4gQ3JlYXRlIGEgdmVjdG9yIG9mIGRhdGVzIGdpdmluZyB0aGUgZmlyc3QgZGF5IG9mIGV2ZXJ5IG1vbnRoIGluIHRoZSBjdXJyZW50IHllYXIuDQoNCkEgdmVjdG9yIG9mIHRoZSBmaXJzdCBkYXkgb2YgdGhlIG1vbnRoIGZvciBldmVyeSBtb250aCBpbiAyMDE1Og0KDQpgYGB7cn0NCnltZCgiMjAxNS0wMS0wMSIpICsgbW9udGhzKDA6MTEpDQpgYGANCg0KVG8gZ2V0IHRoZSB2ZWN0b3Igb2YgdGhlIGZpcnN0IGRheSBvZiB0aGUgbW9udGggZm9yIHRoaXMgeWVhciwgd2UgZmlyc3QgbmVlZCB0byBmaWd1cmUgb3V0IHdoYXQgdGhpcyB5ZWFyIGlzLCBhbmQgZ2V0IEphbnVhcnkgMXN0IG9mIGl0LiBJIGNhbiBkbyB0aGF0IGJ5IHRha2luZyBgdG9kYXkoKWAgYW5kIHRydW5jYXRpbmcgaXQgdG8gdGhlIHllYXIgdXNpbmcgYGZsb29yX2RhdGUoKWA6DQoNCmBgYHtyfQ0KZmxvb3JfZGF0ZSh0b2RheSgpLCB1bml0ID0gInllYXIiKSArIG1vbnRocygwOjExKQ0KYGBgDQoNCiMjIyA0LiBXcml0ZSBhIGZ1bmN0aW9uIHRoYXQgZ2l2ZW4geW91ciBiaXJ0aGRheSAoYXMgYSBkYXRlKSwgcmV0dXJucyBob3cgb2xkIHlvdSBhcmUgaW4geWVhcnMuDQoNCmBgYHtyfQ0KYWdlIDwtIGZ1bmN0aW9uKGJkYXkpIHsNCiAgKGJkYXkgJS0tJSB0b2RheSgpKSAlLyUgeWVhcnMoMSkNCn0NCmFnZSh5bWQoIjE5OTAtMTAtMTIiKSkNCmBgYA0KDQojIyMgNS4gV2h5IGNhbuKAmXQgYCh0b2RheSgpICUtLSUgKHRvZGF5KCkgKyB5ZWFycygxKSkgLyBtb250aHMoMSlgIHdvcms/DQoNClRoZSBjb2RlIGluIHRoZSBxdWVzdGlvbiBpcyBtaXNzaW5nIGEgcGFyZW50aGVzZXMuIFNvLCBJIHdpbGwgYXNzdW1lIHRoYXQgdGhhdCB0aGUgY29ycmVjdCBjb2RlIGlzLA0KDQpgYGB7cn0NCih0b2RheSgpICUtLSUgKHRvZGF5KCkgKyB5ZWFycygxKSkpIC8gbW9udGhzKDEpDQpgYGANCg0KV2hpbGUgdGhpcyBjb2RlIHdpbGwgbm90IGRpc3BsYXkgYSB3YXJuaW5nIG9yIG1lc3NhZ2UsIGl0IGRvZXMgbm90IHdvcmsgZXhhY3RseSBhcyBleHBlY3RlZC4gVGhlIHByb2JsZW0gaXMgZGlzY3Vzc2VkIGluIHRoZSBJbnRlcnZhbHMgc2VjdGlvbi4NCg0KVGhlIG51bWVyYXRvciBvZiB0aGUgZXhwcmVzc2lvbiwgYCh0b2RheSgpICUtLSUgKHRvZGF5KCkgKyB5ZWFycygxKSlgLCBpcyBhbiBpbnRlcnZhbCwgd2hpY2ggaW5jbHVkZXMgYm90aCBhIGR1cmF0aW9uIG9mIHRpbWUgYW5kIGEgc3RhcnRpbmcgcG9pbnQuIFRoZSBpbnRlcnZhbCBoYXMgYW4gZXhhY3QgbnVtYmVyIG9mIHNlY29uZHMuIFRoZSBkZW5vbWluYXRvciBvZiB0aGUgZXhwcmVzc2lvbiwgYG1vbnRocygxKWAsIGlzIGEgcGVyaW9kLCB3aGljaCBpcyBtZWFuaW5nZnVsIHRvIGh1bWFucyBidXQgbm90IGRlZmluZWQgaW4gdGVybXMgb2YgYW4gZXhhY3QgbnVtYmVyIG9mIHNlY29uZHMuIE1vbnRocyBjYW4gYmUgMjgsIDI5LCAzMCwgb3IgMzEgZGF5cywgc28gaXQgaXMgbm90IGNsZWFyIHdoYXQgbW9udGhzKDEpIGRpdmlkZSBieT8gVGhlIGNvZGUgZG9lcyBub3QgcHJvZHVjZSBhIHdhcm5pbmcgbWVzc2FnZSwgYnV0IGl0IHdpbGwgbm90IGFsd2F5cyBwcm9kdWNlIHRoZSBjb3JyZWN0IHJlc3VsdC4NCg0KVG8gZmluZCB0aGUgbnVtYmVyIG9mIG1vbnRocyB3aXRoaW4gYW4gaW50ZXJ2YWwgdXNlIGAlLyVgIGluc3RlYWQgb2YgYC9gLA0KDQpgYGB7cn0NCih0b2RheSgpICUtLSUgKHRvZGF5KCkgKyB5ZWFycygxKSkpICUvJSBtb250aHMoMSkNCmBgYA0KDQpBbHRlcm5hdGl2ZWx5LCB3ZSBjb3VsZCBkZWZpbmUgYSDigJxtb250aOKAnSBhcyAzMCBkYXlzLCBhbmQgcnVuDQoNCmBgYHtyfQ0KKHRvZGF5KCkgJS0tJSAodG9kYXkoKSArIHllYXJzKDEpKSkgLyBkYXlzKDMwKQ0KYGBgDQoNClRoaXMgYXBwcm9hY2ggd2lsbCBub3Qgd29yayB3aXRoIGB0b2RheSgpICsgeWVhcnMoMSlgLCB3aGljaCBpcyBub3QgZGVmaW5lZCBmb3IgRmVicnVhcnkgMjl0aCBvbiBsZWFwIHllYXJzOg0KDQpgYGB7cn0NCmFzLkRhdGUoIjIwMTYtMDItMjkiKSArIHllYXJzKDEpDQpgYGA=