In this notebook, I will:
  • Work with dates
  • Understand the different ways of representing dates
  • Format dates and times
  • Operate on dates and times

  • Plenty of real-world data are associates with instant in time and specific date such as companies reprt each quarter, stock markets report closing prices daily, or network analysts measuring traffic by the hour. Dealing with dates accurately can be complicated as on can encounter time-zone differences, leap years, and regional differences in holidays. Also, people report data differently in different places. For example, what an American would write as “May 12, 2010” or “05-12-10” would be written by someone in the United Kingdom as “12 May 2010” or “12-05-2010”. Similarly, time can be written as 9:25 p.m., 21:25, or 21h25, thus working with a time instant can be difficult (Meys and Vries 2015).


    # Write the opening day of the 2016 Rio Olympic Games
    xd <- as.Date("2016-08-05")
    xd
    ## [1] "2016-08-05"
    # Date structure
    str(xd)
    ##  Date[1:1], format: "2016-08-05"
    # Find out what day of the week it is
    weekdays(xd)
    ## [1] "Friday"
    # Add numbers fro dates
    xd + 7
    ## [1] "2016-08-12"
    # Create vector of seven days starting on July 27
    xd + 0:6
    ## [1] "2016-08-05" "2016-08-06" "2016-08-07" "2016-08-08" "2016-08-09"
    ## [6] "2016-08-10" "2016-08-11"
    # Return days of the week for 0:6
    weekdays(xd + 0:6)
    ## [1] "Friday"    "Saturday"  "Sunday"    "Monday"    "Tuesday"   "Wednesday"
    ## [7] "Thursday"
    # Use seq() to create sequences of dates 
    startDate <- as.Date("2016-01-01")
    xm <- seq(startDate, by = "2 months", length.out = 12)
    xm
    ##  [1] "2016-01-01" "2016-03-01" "2016-05-01" "2016-07-01" "2016-09-01"
    ##  [6] "2016-11-01" "2017-01-01" "2017-03-01" "2017-05-01" "2017-07-01"
    ## [11] "2017-09-01" "2017-11-01"
    # Report on months 
    months(xm)
    ##  [1] "January"   "March"     "May"       "July"      "September" "November" 
    ##  [7] "January"   "March"     "May"       "July"      "September" "November"
    # Report on quarters
    quarters(xm)
    ##  [1] "Q1" "Q1" "Q2" "Q3" "Q3" "Q4" "Q1" "Q1" "Q2" "Q3" "Q3" "Q4"
    # View locale setting of my machine
    # Locale setting describes elements of international customization on a specificinstallation of R
    Sys.localeconv()
    ##     decimal_point     thousands_sep          grouping   int_curr_symbol 
    ##               "."                ""                ""             "USD" 
    ##   currency_symbol mon_decimal_point mon_thousands_sep      mon_grouping 
    ##               "$"               "."               ","            "\003" 
    ##     positive_sign     negative_sign   int_frac_digits       frac_digits 
    ##                ""               "-"               "2"               "2" 
    ##     p_cs_precedes    p_sep_by_space     n_cs_precedes    n_sep_by_space 
    ##               "1"               "0"               "1"               "0" 
    ##       p_sign_posn       n_sign_posn 
    ##               "3"               "0"

    Presenting Dates in Different Format

    # Convert '5 Aug 2016' into a date
    as.Date('5 Aug 2016', format = "%d %b %Y")
    ## [1] "2016-08-05"
    # Convert with '5/08/2016' format
    as.Date('5/08/2016', format = "%d/%m/%Y")
    ## [1] "2016-08-05"
    # Table 6.2
    library(tibble)
    ## Warning: package 'tibble' was built under R version 4.5.2
    table1 <- tibble(
      Format = c("%Y", "%y", "%m", "%B","%b", "%d", "%A", "%a", "%w"),
      Description = c("Year with century.", "Year without century (00-99). Values 00 to 68 are prefixed vy 20, and values 69 to 99 are prefixed by 19.", "Months as decimal number (01-12).", "Full month name in the current locale.", "Abbreviated month name in the current locale.", "Day of the month as a decimal number (01-31)", 
                      "Full weekday name in the current locale.", "Abbreviated weekday name in the current locale.", "Weekday as decimal number (0-6, with Sunday being 0)")
    )
    table1
    ## # A tibble: 9 × 2
    ##   Format Description                                                            
    ##   <chr>  <chr>                                                                  
    ## 1 %Y     Year with century.                                                     
    ## 2 %y     Year without century (00-99). Values 00 to 68 are prefixed vy 20, and …
    ## 3 %m     Months as decimal number (01-12).                                      
    ## 4 %B     Full month name in the current locale.                                 
    ## 5 %b     Abbreviated month name in the current locale.                          
    ## 6 %d     Day of the month as a decimal number (01-31)                           
    ## 7 %A     Full weekday name in the current locale.                               
    ## 8 %a     Abbreviated weekday name in the current locale.                        
    ## 9 %w     Weekday as decimal number (0-6, with Sunday being 0)

    Adding Time Information to Dates

    # To specify time information, we use as.POSIXct() and as.POSIXlt()
    # POSIXct() refers to a time that is internally stored as the number of seconds since the start of 1970
    # POSIXlt() refers to a date stored as a named list of vectors for the year, month, day, hours, and minutes
    # Example, the time of the Apollo 11 moon landing was July 20 1969 at 20:17:39 UTC or Coordinated Universal 
    # Time, and that's how the world's clocks are regulated
    
    apollo <- "July 20, 1969, 20:17:39"
    
    apollo.fmt <- "%B %d, %Y, %H:%M:%S"
    
    xct <- as.POSIXct(apollo, format = apollo.fmt, tz = "UTC")
    xct
    ## [1] "1969-07-20 20:17:39 UTC"
    # Table 6.3
    library(tibble)
    table2 <- tibble(
      Format = c("%H", "%I", "%M", "%S","%p"),
      Description = c("Hours as a decimal number (00-23)", "Hours as a decimal number (01-12.)", "Minutes as a decimal number (00-59).", "Seconds as a decimal number (00-61).", "AM/PM indicator.")
    )
    table2
    ## # A tibble: 5 × 2
    ##   Format Description                         
    ##   <chr>  <chr>                               
    ## 1 %H     Hours as a decimal number (00-23)   
    ## 2 %I     Hours as a decimal number (01-12.)  
    ## 3 %M     Minutes as a decimal number (00-59).
    ## 4 %S     Seconds as a decimal number (00-61).
    ## 5 %p     AM/PM indicator.

    Fomatting Dates and Times

    # To format a date for pretty printing, we use format()
    format(xct, "%d/%m/%y")
    ## [1] "20/07/69"
    # Format the xct datetime as a sentence
    format(xct, "%M minutes past %I %p, on %d %B %Y")
    ## [1] "17 minutes past 08 PM, on 20 July 1969"

    Performing Operations on Dates and Times

    Addition and substraction
    # Add seven days to the Apollo landing scale (each day has 86400 seconds)
    xct + 7*86400
    ## [1] "1969-07-27 20:17:39 UTC"
    # Add 3 hours to the time of the Apollo moon landing
    xct + 3*60*60
    ## [1] "1969-07-20 23:17:39 UTC"

    There is a difference between Date objects and POSIXct or POSIXlt objects. When using a Date object, we add and subtract days; with POSIXct and POSIXlt, the operations add or subtract only seconds.

    # Example: Convert xct to a Date object and subtract by 7 (will affect days)
    as.Date(xct) - 7
    ## [1] "1969-07-13"

    Comparing of dates

    # Get current system time
    Sys.time()
    ## [1] "2026-03-15 20:08:05 EDT"
    # Compare current system time with the time of Apollo landing
    Sys.time() < xct
    ## [1] FALSE
    The moon landing happened more than 40 years ago, so it’s no surprise this statement is false.

    Now, we will compare the start of several decades to the moon landing date. Then, we will create a POSIXct object containing the first day of 1950, and use the seq() function to create a sequence with intervals of ten years.

    dec.start <- as.POSIXct("1950-01-01")
    dec <- seq(dec.start, by = "10 years", length.out = 4)
    dec
    ## [1] "1950-01-01 EST" "1960-01-01 EST" "1970-01-01 EST" "1980-01-01 EST"
    # Compare dec to with moon landing date
    dec > xct
    ## [1] FALSE FALSE  TRUE  TRUE

    Extraction

    Scientists may want to compare the weather in a specific month for many different years. To do this, they have to determine the month by extracting the months from the datetime object.

    N.B. - To achieve this, we use the POSIXlt class, as this type of data is stored internally as a named list, which enables us to extract components by name.

    # Convert the Date class
    xlt <- as.POSIXlt(xct)
    xlt
    ## [1] "1969-07-20 20:17:39 UTC"
    # Use $ operator to extract the different components
    xlt$year
    ## [1] 69
    # To get month
    xlt$mon
    ## [1] 6
    R considers July to be the sixth month of the year. POSIXlt is based on an old Unix format, and that format starts to count from 0 instead of 1. So, January is 0 and July is 6.
    # Internal structure of POSIXlt objects
    unclass(xlt)
    ## $sec
    ## [1] 39
    ## 
    ## $min
    ## [1] 17
    ## 
    ## $hour
    ## [1] 20
    ## 
    ## $mday
    ## [1] 20
    ## 
    ## $mon
    ## [1] 6
    ## 
    ## $year
    ## [1] 69
    ## 
    ## $wday
    ## [1] 0
    ## 
    ## $yday
    ## [1] 200
    ## 
    ## $isdst
    ## [1] 0
    ## 
    ## $zone
    ## [1] "UTC"
    ## 
    ## $gmtoff
    ## [1] 0
    ## 
    ## attr(,"tzone")
    ## [1] "UTC"
    ## attr(,"balanced")
    ## [1] TRUE

    References

    Meys, Joris, and Andrie de Vries. 2015. R for Dummies. 2nd ed. Hoboken, NJ: Wiley.