--- title: "Dates and Times" --- ## Packages for this section ```{r dates-and-times-1} library(tidyverse) # library(lubridate) ``` `lubridate` is the package that handles dates and times, but is now part of the `tidyverse`, so no need to load separately. ## Dates - Dates represented on computers as “days since an origin”, typically Jan 1, 1970, with a negative date being before the origin: ```{r dates-and-times-2, paged.print=T} mydates <- c("1931-08-05", "1970-01-01", "2007-09-04") tibble(text = mydates) %>% mutate( d = as.Date(text), numbers = as.numeric(d) ) -> somedates somedates ``` ## Doing arithmetic with dates - Dates are "actually" numbers, so can add and subtract: ```{r} edit_date <- as.Date("2026-06-15") edit_date edit_date + 50 # add 50 days ``` ```{r} birth_date <- as.Date("2007-09-04") edit_date - birth_date ``` ## Reading in dates from a file - `read_csv` and the others can guess that you have dates, if you format them as year-month-day, like column 1 of this `.csv`: ``` date,status,dunno 2011-08-03,hello,August 3 2011 2011-11-15,still here,November 15 2011 2012-02-01,goodbye,February 1 2012 ``` ## ... continued - Then read them in: ```{r dates-and-times-4} #| message: true my_url <- "http://datafiles.ritsokiguess.site/mydates.csv" dates1 <- read_csv(my_url) ``` - read_csv guessed that the 1st column is dates, but not 3rd. ## The data as read in ```{r dates-and-times-5, paged.print=T} dates1 ``` ## Dates in other formats - `dates1` shows that dates are best stored as text in format yyyy-mm-dd ([ISO 8601 standard](https://www.iso8601.com/)). - To deal with dates in other formats, use `lubridate` functions to convert. For example, dates in US format with month first: ```{r dates-and-times-6, paged.print=T} tibble(us_dates = c("05/27/2012", "01/03/2016", "12/31/2015")) %>% mutate(proper_dates = mdy(us_dates)) ``` ## If you have UK dates ```{r dates-and-times-7, paged.print=T} tibble(ukdates = c("27/5/2012", "1/3/2016", "12/31/2015")) %>% mutate(uk = dmy(ukdates)) ``` - The second one is ambiguous: is it March 1 (UK), Jan 3 (US)? - Note that day and month are gotten correct in `uk`, except for the last one, which makes no sense as a date written this way. ## Our data frame's last column: - Back to this: ```{r dates-and-times-8, paged.print=F} dates1 ``` - Column `dunno` has month (name), day, year in that order. ## so interpret as such ```{r dates-and-times-9, paged.print=T} (dates1 %>% mutate(date_from_text = mdy(dunno)) -> dates2) ``` ## Are they really the same? - Compare dates originally read in (in `date`) with those converted from `dunno`: ```{r dates-and-times-10, paged.print=F} dates2 %>% mutate(equal = identical(date, date_from_text)) ``` - The two columns of dates are all the same. ## The opposite problem - We know how to convert text to dates, but what if we want to convert dates to text, for example to display them in a certain format? - Can use `strftime` (challenging) or `stamp` (easier). ## Using `stamp` - To use `stamp`: - first, create a "stamp" with the kind of format you want (but not necessarily the right date) - then, apply the stamp to your date(s): ```{r} my_stamp <- stamp("21 Jan, year 1999") dates1 %>% mutate(date_stamp = my_stamp(date)) ``` ## When you run `stamp`... you get a message: ```{r} #| message: true my_stamp <- stamp("21 Jan, year 1999") ``` These codes are what `strftime` uses: - `%d` is the day of the month (with leading zero) - `%b` is the abbreviated month name - `%Y` is the four-digit year See [here](https://rdrr.io/r/base/strptime.html) for all the codes. \small ```{r} strftime(dates1$date, format = "%d %b, year %Y") ``` \normalsize ## Making dates from pieces Starting from this file: ```{r dates-and-times-11} my_url <- "http://datafiles.ritsokiguess.site/pieces.txt" dates3 <- read_delim(my_url, " ") dates3 ``` ## Literally `make`-ing a `date` ```{r} dates3 %>% mutate(the_date = make_date(year, month, day)) -> newdates newdates ``` ## Extracting information from dates ```{r dates-and-times-14, paged.print=T} newdates %>% select(the_date) %>% mutate( yr = year(the_date), mon = month(the_date, label = TRUE), day = day(the_date), weekday = wday(the_date, label = TRUE) ) ``` ## Dates and times - Standard format for times is to put the time after the date, hours, minutes, seconds: ```{r dates-and-times-15, paged.print=F} (dd <- tibble(text = c( "1970-01-01 07:50:01", "2007-09-04 15:30:00", "1940-04-15 06:45:10", "2016-02-10 12:26:40" ))) ``` ## Converting text to date-times: - Then get from this text using `ymd_hms`: ```{r dates-and-times-16, paged.print=F} dd %>% mutate(dt = ymd_hms(text)) %>% pull(dt) ``` ## Timezones - Default timezone is UTC, "Universal Coordinated Time". Change it via `tz=` and the name of a timezone: ```{r dates-and-times-17, paged.print=T} dd %>% mutate(dt = ymd_hms(text, tz = "America/Toronto")) -> dd dd %>% mutate(zone = tz(dt)) ``` ## Finding a timezone name - Use `OlsonNames()`. Some of them: \small ```{r} sample(OlsonNames(), 10) ``` \normalsize - Timezones are a mess. If you are arranging a meeting with people in different time zones, you should give times in UTC, and then everybody can convert for their time zone and daylight time. ## Extracting time parts - As you would expect: ```{r dates-and-times-18, paged.print=F} dd %>% select(-text) %>% mutate( h = hour(dt), sec = second(dt), min = minute(dt), zone = tz(dt) ) ``` ## Same times, but different time zone: ```{r dates-and-times-19, paged.print=T} dd %>% select(dt) %>% mutate(oz = with_tz(dt, "Australia/Sydney")) ``` - that is, if you are in Toronto and call someone in Sydney, Australia at the time shown in `dt`, the time it will be for them is as shown in `oz`. ## In more detail ```{r dates-and-times-20} dd %>% mutate(oz = with_tz(dt, "Australia/Sydney")) %>% pull(oz) ``` "Australian Eastern Time", Standard or Daylight. Note when the Australian summer is. ## How long between date-times? - We may need to calculate the time between two events. For example, these are the dates and times that some patients were admitted to and discharged from a hospital: ``` admit,discharge 1981-12-10 22:00:00,1982-01-03 14:00:00 2014-03-07 14:00:00,2014-03-08 09:30:00 2016-08-31 21:00:00,2016-09-02 17:00:00 ``` ## Do they get read in as date-times? - These ought to get read in and converted to date-times: ```{r dates-and-times-21} my_url <- "http://ritsokiguess.site/datafiles/hospital.csv" stays <- read_csv(my_url) stays ``` - and so it proves. ## Subtracting the date-times - In the obvious way, this gets us an answer: ```{r dates-and-times-22} stays %>% mutate(stay = discharge - admit) ``` - Number of hours; hard to interpret. - The `drtn` at the top of the column stands for "duration". ## Days - Fractional number of days would be better: ```{r dates-and-times-23, paged.print=T} stays %>% mutate( stay_days = as.period(admit %--% discharge) / days(1)) ``` - The distinction between "duration" and "period" is explained later. ## Completed days - Pull out with `day()` etc, as for a date-time: ```{r dates-and-times-24, error=TRUE} stays %>% mutate( stay = as.period(admit %--% discharge), stay_days = day(stay), stay_hours = hour(stay) ) %>% select(starts_with("stay")) ``` ## Comments - Date-times are stored internally as seconds-since-something, so that subtracting two of them will give, internally, a number of seconds. - Just subtracting the date-times is displayed as a time (in units that R chooses for us). - Convert to fractional times via a "period", then divide by `days(1)`, `months(1)` etc. - These ideas useful for calculating time from a start point until an event happens (in this case, a patient being discharged from hospital).