Time is the next dimension.

This topic covers the basics of how we put together data based upone date and time objects. For this, we will use the following data frame with a single column of data representing dates as they are written in the US.

These are several challenges associated with working with date and time objects. To those of us who are reading this with a background of how US time and date formats are read, we can easily interpret data objects as Month/Day/Year formats (e.g., ā€œ2/14/2018ā€), and is commonly represented in the kind of input data we work in R with as with a string of characters. Dates and times are sticky things in data analysis because they do not work the way we think they should. Here are some wrinkles:

  1. There are many types of calendars, we use the Julian calendar. However, there are many other calendars that are in use that we may run into. Each of these calendars has a different starting year (e.g., in the Assyrian calendar it is year 6770, it is 4718 in the Chinese calendar, 2020 in the Gregorian, and 1442 in the Islamic calendar).
  2. Western calendar has leap years (+1 day in February) as well as leap seconds because it is based on the rotation around the sun, others are based upon the lunar cycle and have other corrections.
  3. On this planet, we have 24 different time zones. Some states (looking at you Arizona) donā€™t feel it necessary to follow the other states around so they may be the same as PST some of the year and the same as MST the rest of the year. The provence of Newfoundland decided to be half-way between time zones so they are GMT-2:30. Some states have more than one time zone even if they are not large in size (hello Indiana).
  4. Dates and time are made up of odd units, 60-seconds a minute, 60-minutes an hour, 24-hours a day, 7-days a week, 2-weeks a fortnight, 28,29,30,or 31-days in a month, 365 or 366 days in a year, 100 years in a century, etc.

Fortunately, some smart programmers have figured this out for us already. What they did is made the second as the base unit of time and designated 00:00:00 on 1 January 1970 as the unix epoch. Time on most modern computers is measured from that starting point. It is much easier to measure the difference between two points in time using the seconds since unix epich and then translate it into one or more of these calendars than to deal with all the different calendars each time. So under the hood, much of the date and time issues are kept in terms of epoch seconds.

unclass( Sys.time() )
[1] 1605102067

Basic Date Objects

R has some basic date functionality built into it. One of the easiest says to get a date object created is to specify the a date as a character string and then coerce it into a data object. By default, this requires us to represent the date objects as ā€œYEAR-MONTH-DAYā€ with padding 0 values for any integer of month or date below 9 (e.g., must be two-digits).

So for example, we can specify a date object as:

class_start <- as.Date("2021-01-15")
class_start
[1] "2021-01-15"

And it is of type:

class( class_start )
[1] "Date"
Date

If you want to make a the date from a different format, you need to specify what elements within the string representation using format codes. These codes (and many more) can be found by looking at ?strptime.

class_end <- as.Date( "5/10/21", format = "%m/%d/%y")
class_end
[1] "2021-05-10"

Date objects can be put into vectors and sequences just like other objects.

semester <- seq( class_start, class_end, by = "1 day")
semester
  [1] "2021-01-15" "2021-01-16" "2021-01-17" "2021-01-18" "2021-01-19"
  [6] "2021-01-20" "2021-01-21" "2021-01-22" "2021-01-23" "2021-01-24"
 [11] "2021-01-25" "2021-01-26" "2021-01-27" "2021-01-28" "2021-01-29"
 [16] "2021-01-30" "2021-01-31" "2021-02-01" "2021-02-02" "2021-02-03"
 [21] "2021-02-04" "2021-02-05" "2021-02-06" "2021-02-07" "2021-02-08"
 [26] "2021-02-09" "2021-02-10" "2021-02-11" "2021-02-12" "2021-02-13"
 [31] "2021-02-14" "2021-02-15" "2021-02-16" "2021-02-17" "2021-02-18"
 [36] "2021-02-19" "2021-02-20" "2021-02-21" "2021-02-22" "2021-02-23"
 [41] "2021-02-24" "2021-02-25" "2021-02-26" "2021-02-27" "2021-02-28"
 [46] "2021-03-01" "2021-03-02" "2021-03-03" "2021-03-04" "2021-03-05"
 [51] "2021-03-06" "2021-03-07" "2021-03-08" "2021-03-09" "2021-03-10"
 [56] "2021-03-11" "2021-03-12" "2021-03-13" "2021-03-14" "2021-03-15"
 [61] "2021-03-16" "2021-03-17" "2021-03-18" "2021-03-19" "2021-03-20"
 [66] "2021-03-21" "2021-03-22" "2021-03-23" "2021-03-24" "2021-03-25"
 [71] "2021-03-26" "2021-03-27" "2021-03-28" "2021-03-29" "2021-03-30"
 [76] "2021-03-31" "2021-04-01" "2021-04-02" "2021-04-03" "2021-04-04"
 [81] "2021-04-05" "2021-04-06" "2021-04-07" "2021-04-08" "2021-04-09"
 [86] "2021-04-10" "2021-04-11" "2021-04-12" "2021-04-13" "2021-04-14"
 [91] "2021-04-15" "2021-04-16" "2021-04-17" "2021-04-18" "2021-04-19"
 [96] "2021-04-20" "2021-04-21" "2021-04-22" "2021-04-23" "2021-04-24"
[101] "2021-04-25" "2021-04-26" "2021-04-27" "2021-04-28" "2021-04-29"
[106] "2021-04-30" "2021-05-01" "2021-05-02" "2021-05-03" "2021-05-04"
[111] "2021-05-05" "2021-05-06" "2021-05-07" "2021-05-08" "2021-05-09"
[116] "2021-05-10"

Some helpful functions include the Julian Ordinal Day (e.g., number of days since the start of the year).

ordinal_day <- yday( semester[102] )
ordinal_day
[1] 116

The weekday as an integer (0-6 starting on Sunday), which I use to index the named values.

days_of_week <- c("Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday")
x <- wday( semester[32] )
days_of_week[ x ]
[1] "Monday"
Monday

Since we did not specify a time, things like hour() and minute() do not provide any usable information.

Dates & Times

To add time to the date objects, we need to specify both date and time specifically. Here are some example data:

df <- data.frame( Date = c("8/21/2004 7:33:51 AM",
                           "7/12/2008 9:23:08 PM",
                           "2/14/2010 8:18:30 AM",
                           "12/23/2018 11:11:45 PM",
                           "2/1/2019 4:42:00 PM",
                           "5/17/2012 1:23:23 AM",
                           "12/11/2020 9:48:02 PM") )
summary( df )
     Date          
 Length:7          
 Class :character  
 Mode  :character  

Just like above, if we want to turn these into date and time objects we must be able to tell the parsing algorithm what elements are represented in each entry. There are many ways to make dates and time, 10/14 or 14 Oct or October 14 or Julian day 287, etc. These are designated by a format string were we indicate what element represents a day or month or year or hour or minute or second, etc. These are found by looking at the documentation for?strptime.

In our case, we have:
- Month as 1 or 2 digits
- Day as 1 or 2 digits
- Year as 4 digits
- a space to separate date from time
- hour (not 24-hour though)
- minutes in 2 digits
- seconds in 2 digits
- a space to separate time from timezone
- timezone
- / separating date objects
- : separating time objects

To make the format string, we need to look up how to encode these items. The items in df for a date & time object such as 2/1/2019 4:42:00 PM have the format string:

format <- "%m/%d/%Y %I:%M:%S %p"

Now, we can convert the character string in the data frame to a date and time object. Instead of using the built-in as.Date() functionality, I like the lubridate library1 as it has a lot of additional functionality that weā€™ll play with a bit later.

library( lubridate )
df$Date <- parse_date_time( df$Date, 
                            orders=format, 
                            tz = "EST" )
summary( df )
      Date                    
 Min.   :2004-08-21 07:33:51  
 1st Qu.:2009-04-29 14:50:49  
 Median :2012-05-17 01:23:23  
 Mean   :2013-07-11 07:28:39  
 3rd Qu.:2019-01-12 19:56:52  
 Max.   :2020-12-11 21:48:02  
class( df$Date )
[1] "POSIXct" "POSIXt" 
POSIXct

POSIXt

Now, we can ask Date-like questions about the data such as what day of the week was the first sample taken?

weekdays( df$Date[1] )
[1] "Saturday"
Saturday

What is the range of dates?

range( df$Date )
[1] "2004-08-21 07:33:51 EST" "2020-12-11 21:48:02 EST"

What is the median of samples

median( df$Date )
[1] "2012-05-17 01:23:23 EST"

and what julian ordinal day (e.g., how many days since start of the year) is the last record.

yday( df$Date[4] )
[1] 357

Just for fun, Iā€™ll add a column to the data that has weekday.

df$Weekday <- weekdays( df$Date )
df

However, we should probably turn it into a factor (e.g., a data type with pre-defined levelsā€”and for us hereā€”an intrinsic order of the levels).

df$Weekday <- factor( df$Weekday, 
                        ordered = TRUE, 
                        levels = days_of_week
                        )
summary( df$Weekday )
   Sunday    Monday   Tuesday Wednesday  Thursday    Friday  Saturday 
        2         0         0         0         1         2         2 

Filtering on Date Objects

We can easily filter the content within a data.frame using some helper functions such as hour(), minute(), weekday(), etc. Here are some examples including pulling out the weekends.

weekends <- df[ df$Weekday %in% c("Saturday","Sunday"), ]
weekends

finding items that are in the past (paste being defined as the last time this document was knit).

past <- df$Date[ df$Date < Sys.time() ]
past
[1] "2004-08-21 07:33:51 EST" "2008-07-12 21:23:08 EST"
[3] "2010-02-14 08:18:30 EST" "2018-12-23 23:11:45 EST"
[5] "2019-02-01 16:42:00 EST" "2012-05-17 01:23:23 EST"

Items that are during working hours

work <- df$Date[ hour(df$Date) >= 9 & hour(df$Date) <= 17 ]
work
[1] "2019-02-01 16:42:00 EST"

And total range of values in days using normal arithmatic operations such as the minus operator.

max(df$Date) - min(df$Date)
Time difference of 5956.593 days

  1. If you get an error saying something like, ā€œthere is no package named lubridateā€ then use install.packages("lubridate") and install it. You only need to do this once.ā†©ļøŽ

LS0tCnRpdGxlOiAiRGF0ZSBEYXRhIFR5cGVzIPCfk4UiCmF1dGhvcjogIlRoZSBgbHVicmlkYXRlYCBsaWJyYXJ5IgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICBjc3M6IFsiaHR0cHM6Ly9keWVybGFiLmdpdGh1Yi5pby9FTlZTLUxlY3R1cmVzL2Nzcy9uYXJyYXRpdmVfc3R5bGUuY3NzIl0KLS0tCgpgYGB7ciBzdGFydHVwLCBpbmNsdWRlPUZBTFNFfQpvcHRpb25zKCBkcGx5ci5zdW1tYXJpc2UuaW5mb3JtID0gRkFMU0UgKSAgCmxpYnJhcnkoIHRpZHl2ZXJzZSApCmxpYnJhcnkoIGx1YnJpZGF0ZSApCmxpYnJhcnkoIGtuaXRyICkKb3B0c19jaHVuayRzZXQoIHdhcm5pbmcgPSBGQUxTRSwKICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBGQUxTRSwKICAgICAgICAgICAgICAgIGZpZy5hbGlnbiA9ICdjZW50ZXInICkKZ2dwbG90Mjo6dGhlbWVfc2V0KCB0aGVtZV9idyggYmFzZV9zaXplPTE2KSApCmBgYAoKCgoKPiBUaW1lIGlzIHRoZSBuZXh0IGRpbWVuc2lvbi4KClRoaXMgdG9waWMgY292ZXJzIHRoZSBiYXNpY3Mgb2YgaG93IHdlIHB1dCB0b2dldGhlciBkYXRhIGJhc2VkIHVwb25lIGRhdGUgYW5kIHRpbWUgb2JqZWN0cy4gIEZvciB0aGlzLCB3ZSB3aWxsIHVzZSB0aGUgZm9sbG93aW5nIGRhdGEgZnJhbWUgd2l0aCBhIHNpbmdsZSBjb2x1bW4gb2YgZGF0YSByZXByZXNlbnRpbmcgZGF0ZXMgYXMgdGhleSBhcmUgd3JpdHRlbiBpbiB0aGUgVVMuCgpUaGVzZSBhcmUgc2V2ZXJhbCBjaGFsbGVuZ2VzIGFzc29jaWF0ZWQgd2l0aCB3b3JraW5nIHdpdGggZGF0ZSBhbmQgdGltZSBvYmplY3RzLiAgVG8gdGhvc2Ugb2YgdXMgd2hvIGFyZSByZWFkaW5nIHRoaXMgd2l0aCBhIGJhY2tncm91bmQgb2YgaG93IFVTIHRpbWUgYW5kIGRhdGUgZm9ybWF0cyBhcmUgcmVhZCwgd2UgY2FuIGVhc2lseSBpbnRlcnByZXQgZGF0YSBvYmplY3RzIGFzIE1vbnRoL0RheS9ZZWFyIGZvcm1hdHMgKGUuZy4sICIyLzE0LzIwMTgiKSwgYW5kIGlzIGNvbW1vbmx5IHJlcHJlc2VudGVkIGluIHRoZSBraW5kIG9mIGlucHV0IGRhdGEgd2Ugd29yayBpbiBgUmAgd2l0aCBhcyB3aXRoIGEgc3RyaW5nIG9mIGNoYXJhY3RlcnMuICBEYXRlcyBhbmQgdGltZXMgYXJlIHN0aWNreSB0aGluZ3MgaW4gZGF0YSBhbmFseXNpcyBiZWNhdXNlIHRoZXkgZG8gbm90IHdvcmsgdGhlIHdheSB3ZSB0aGluayB0aGV5IHNob3VsZC4gIEhlcmUgYXJlIHNvbWUgd3JpbmtsZXM6CgoxLiBUaGVyZSBhcmUgbWFueSB0eXBlcyBvZiBjYWxlbmRhcnMsIHdlIHVzZSB0aGUgSnVsaWFuIGNhbGVuZGFyLiAgSG93ZXZlciwgdGhlcmUgYXJlIG1hbnkgb3RoZXIgY2FsZW5kYXJzIHRoYXQgYXJlIGluIHVzZSB0aGF0IHdlIG1heSBydW4gaW50by4gIEVhY2ggb2YgdGhlc2UgY2FsZW5kYXJzIGhhcyBhIGRpZmZlcmVudCBzdGFydGluZyB5ZWFyIChlLmcuLCBpbiB0aGUgQXNzeXJpYW4gY2FsZW5kYXIgaXQgaXMgeWVhciA2NzcwLCBpdCBpcyA0NzE4IGluIHRoZSBDaGluZXNlIGNhbGVuZGFyLCAyMDIwIGluIHRoZSBHcmVnb3JpYW4sIGFuZCAxNDQyIGluIHRoZSBJc2xhbWljIGNhbGVuZGFyKS4KMi4gV2VzdGVybiBjYWxlbmRhciBoYXMgbGVhcCB5ZWFycyAoKzEgZGF5IGluIEZlYnJ1YXJ5KSBhcyB3ZWxsIGFzIGxlYXAgc2Vjb25kcyBiZWNhdXNlIGl0IGlzIGJhc2VkIG9uIHRoZSByb3RhdGlvbiBhcm91bmQgdGhlIHN1biwgb3RoZXJzIGFyZSBiYXNlZCB1cG9uIHRoZSBsdW5hciBjeWNsZSBhbmQgaGF2ZSBvdGhlciBjb3JyZWN0aW9ucy4gCjMuIE9uIHRoaXMgcGxhbmV0LCB3ZSBoYXZlIDI0IGRpZmZlcmVudCB0aW1lIHpvbmVzLiBTb21lIHN0YXRlcyAobG9va2luZyBhdCB5b3UgQXJpem9uYSkgZG9uJ3QgZmVlbCBpdCBuZWNlc3NhcnkgdG8gZm9sbG93IHRoZSBvdGhlciBzdGF0ZXMgYXJvdW5kIHNvIHRoZXkgbWF5IGJlIHRoZSBzYW1lIGFzIFBTVCBzb21lIG9mIHRoZSB5ZWFyIGFuZCB0aGUgc2FtZSBhcyBNU1QgdGhlIHJlc3Qgb2YgdGhlIHllYXIuICBUaGUgcHJvdmVuY2Ugb2YgTmV3Zm91bmRsYW5kIGRlY2lkZWQgdG8gYmUgaGFsZi13YXkgYmV0d2VlbiB0aW1lIHpvbmVzIHNvIHRoZXkgYXJlIEdNVC0yOjMwLiBTb21lIHN0YXRlcyBoYXZlIG1vcmUgdGhhbiBvbmUgdGltZSB6b25lIGV2ZW4gaWYgdGhleSBhcmUgbm90IGxhcmdlIGluIHNpemUgKGhlbGxvIEluZGlhbmEpLgo0LiBEYXRlcyBhbmQgdGltZSBhcmUgbWFkZSB1cCBvZiBvZGQgdW5pdHMsIDYwLXNlY29uZHMgYSBtaW51dGUsIDYwLW1pbnV0ZXMgYW4gaG91ciwgMjQtaG91cnMgYSBkYXksIDctZGF5cyBhIHdlZWssIDItd2Vla3MgYSBmb3J0bmlnaHQsIDI4LDI5LDMwLG9yIDMxLWRheXMgaW4gYSBtb250aCwgMzY1IG9yIDM2NiBkYXlzIGluIGEgeWVhciwgMTAwIHllYXJzIGluIGEgY2VudHVyeSwgZXRjLgoKRm9ydHVuYXRlbHksIHNvbWUgc21hcnQgcHJvZ3JhbW1lcnMgaGF2ZSBmaWd1cmVkIHRoaXMgb3V0IGZvciB1cyBhbHJlYWR5LiAgV2hhdCB0aGV5IGRpZCBpcyBtYWRlIHRoZSBzZWNvbmQgYXMgdGhlIGJhc2UgdW5pdCBvZiB0aW1lIGFuZCBkZXNpZ25hdGVkIDAwOjAwOjAwIG9uIDEgSmFudWFyeSAxOTcwIGFzIHRoZSAqdW5peCBlcG9jaCouICBUaW1lIG9uIG1vc3QgbW9kZXJuIGNvbXB1dGVycyBpcyBtZWFzdXJlZCBmcm9tIHRoYXQgc3RhcnRpbmcgcG9pbnQuICBJdCBpcyBtdWNoIGVhc2llciB0byBtZWFzdXJlIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdHdvIHBvaW50cyBpbiB0aW1lIHVzaW5nIHRoZSBzZWNvbmRzIHNpbmNlIHVuaXggZXBpY2ggKmFuZCB0aGVuKiB0cmFuc2xhdGUgaXQgaW50byBvbmUgb3IgbW9yZSBvZiB0aGVzZSBjYWxlbmRhcnMgdGhhbiB0byBkZWFsIHdpdGggYWxsIHRoZSBkaWZmZXJlbnQgY2FsZW5kYXJzIGVhY2ggdGltZS4gU28gdW5kZXIgdGhlIGhvb2QsIG11Y2ggb2YgdGhlIGRhdGUgYW5kIHRpbWUgaXNzdWVzIGFyZSBrZXB0IGluIHRlcm1zIG9mIGVwb2NoIHNlY29uZHMuICAKCmBgYHtyfQp1bmNsYXNzKCBTeXMudGltZSgpICkKYGBgCgojIyBCYXNpYyBEYXRlIE9iamVjdHMKCmBSYCBoYXMgc29tZSBiYXNpYyBkYXRlIGZ1bmN0aW9uYWxpdHkgYnVpbHQgaW50byBpdC4gIE9uZSBvZiB0aGUgZWFzaWVzdCBzYXlzIHRvIGdldCBhIGRhdGUgb2JqZWN0IGNyZWF0ZWQgaXMgdG8gc3BlY2lmeSB0aGUgYSBkYXRlIGFzIGEgY2hhcmFjdGVyIHN0cmluZyBhbmQgdGhlbiBjb2VyY2UgaXQgaW50byBhIGRhdGEgb2JqZWN0LiAgQnkgZGVmYXVsdCwgdGhpcyByZXF1aXJlcyB1cyB0byByZXByZXNlbnQgdGhlIGRhdGUgb2JqZWN0cyBhcyAiWUVBUi1NT05USC1EQVkiIHdpdGggcGFkZGluZyBgMGAgdmFsdWVzIGZvciBhbnkgaW50ZWdlciBvZiBtb250aCBvciBkYXRlIGJlbG93IDkgKGUuZy4sIG11c3QgYmUgdHdvLWRpZ2l0cykuCgpTbyBmb3IgZXhhbXBsZSwgd2UgY2FuIHNwZWNpZnkgYSBkYXRlIG9iamVjdCBhczoKCmBgYHtyfQpjbGFzc19zdGFydCA8LSBhcy5EYXRlKCIyMDIxLTAxLTE1IikKY2xhc3Nfc3RhcnQKYGBgCgpBbmQgaXQgaXMgb2YgdHlwZTogCgpgYGB7cn0KY2xhc3MoIGNsYXNzX3N0YXJ0ICkKYGBgCgpJZiB5b3Ugd2FudCB0byBtYWtlIGEgdGhlIGRhdGUgZnJvbSBhIGRpZmZlcmVudCBmb3JtYXQsIHlvdSBuZWVkIHRvIHNwZWNpZnkgd2hhdCBlbGVtZW50cyB3aXRoaW4gdGhlIHN0cmluZyByZXByZXNlbnRhdGlvbiB1c2luZyBmb3JtYXQgY29kZXMuICBUaGVzZSBjb2RlcyAoYW5kIG1hbnkgbW9yZSkgY2FuIGJlIGZvdW5kIGJ5IGxvb2tpbmcgYXQgYD9zdHJwdGltZWAuCgpgYGB7cn0KY2xhc3NfZW5kIDwtIGFzLkRhdGUoICI1LzEwLzIxIiwgZm9ybWF0ID0gIiVtLyVkLyV5IikKY2xhc3NfZW5kCmBgYAoKRGF0ZSBvYmplY3RzIGNhbiBiZSBwdXQgaW50byB2ZWN0b3JzIGFuZCBzZXF1ZW5jZXMganVzdCBsaWtlIG90aGVyIG9iamVjdHMuCgpgYGB7cn0Kc2VtZXN0ZXIgPC0gc2VxKCBjbGFzc19zdGFydCwgY2xhc3NfZW5kLCBieSA9ICIxIGRheSIpCnNlbWVzdGVyCmBgYAoKU29tZSBoZWxwZnVsIGZ1bmN0aW9ucyBpbmNsdWRlIHRoZSBKdWxpYW4gT3JkaW5hbCBEYXkgKGUuZy4sIG51bWJlciBvZiBkYXlzIHNpbmNlIHRoZSBzdGFydCBvZiB0aGUgeWVhcikuCgpgYGB7cn0Kb3JkaW5hbF9kYXkgPC0geWRheSggc2VtZXN0ZXJbMTAyXSApCm9yZGluYWxfZGF5CmBgYAoKVGhlIHdlZWtkYXkgYXMgYW4gaW50ZWdlciAoMC02IHN0YXJ0aW5nIG9uIFN1bmRheSksIHdoaWNoIEkgdXNlIHRvIGluZGV4IHRoZSBuYW1lZCB2YWx1ZXMuCgpgYGB7cn0KZGF5c19vZl93ZWVrIDwtIGMoIlN1bmRheSIsIk1vbmRheSIsIlR1ZXNkYXkiLCJXZWRuZXNkYXkiLCJUaHVyc2RheSIsIkZyaWRheSIsIlNhdHVyZGF5IikKeCA8LSB3ZGF5KCBzZW1lc3RlclszMl0gKQpkYXlzX29mX3dlZWtbIHggXQpgYGAKClNpbmNlIHdlIGRpZCBub3Qgc3BlY2lmeSBhIHRpbWUsIHRoaW5ncyBsaWtlIGBob3VyKClgIGFuZCBgbWludXRlKClgIGRvIG5vdCBwcm92aWRlIGFueSB1c2FibGUgaW5mb3JtYXRpb24uCgoKCiMjIERhdGVzICYgVGltZXMKClRvIGFkZCB0aW1lIHRvIHRoZSBkYXRlIG9iamVjdHMsIHdlIG5lZWQgdG8gc3BlY2lmeSBib3RoIGRhdGUgYW5kIHRpbWUgc3BlY2lmaWNhbGx5LiAgSGVyZSBhcmUgc29tZSBleGFtcGxlIGRhdGE6IAoKYGBge3J9CmRmIDwtIGRhdGEuZnJhbWUoIERhdGUgPSBjKCI4LzIxLzIwMDQgNzozMzo1MSBBTSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICI3LzEyLzIwMDggOToyMzowOCBQTSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICIyLzE0LzIwMTAgODoxODozMCBBTSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICIxMi8yMy8yMDE4IDExOjExOjQ1IFBNIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIjIvMS8yMDE5IDQ6NDI6MDAgUE0iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiNS8xNy8yMDEyIDE6MjM6MjMgQU0iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiMTIvMTEvMjAyMCA5OjQ4OjAyIFBNIikgKQpzdW1tYXJ5KCBkZiApCmBgYAoKSnVzdCBsaWtlIGFib3ZlLCBpZiB3ZSB3YW50IHRvIHR1cm4gdGhlc2UgaW50byBkYXRlIGFuZCB0aW1lIG9iamVjdHMgd2UgbXVzdCBiZSBhYmxlIHRvIHRlbGwgdGhlIHBhcnNpbmcgYWxnb3JpdGhtIHdoYXQgZWxlbWVudHMgYXJlIHJlcHJlc2VudGVkIGluIGVhY2ggZW50cnkuIFRoZXJlIGFyZSBtYW55IHdheXMgdG8gbWFrZSBkYXRlcyBhbmQgdGltZSwgMTAvMTQgb3IgMTQgT2N0IG9yIE9jdG9iZXIgMTQgb3IgSnVsaWFuIGRheSAyODcsIGV0Yy4gIFRoZXNlIGFyZSBkZXNpZ25hdGVkIGJ5IGEgZm9ybWF0IHN0cmluZyB3ZXJlIHdlIGluZGljYXRlIHdoYXQgZWxlbWVudCByZXByZXNlbnRzIGEgKmRheSogb3IgKm1vbnRoKiBvciAqeWVhciogb3IgKmhvdXIqIG9yICptaW51dGUqIG9yICpzZWNvbmQqLCBldGMuICBUaGVzZSBhcmUgZm91bmQgYnkgbG9va2luZyBhdCB0aGUgZG9jdW1lbnRhdGlvbiBmb3JgP3N0cnB0aW1lYC4KCkluIG91ciBjYXNlLCB3ZSBoYXZlOiAgCi0gTW9udGggYXMgMSBvciAyIGRpZ2l0cyAgCi0gRGF5IGFzIDEgb3IgMiBkaWdpdHMgIAotIFllYXIgYXMgNCBkaWdpdHMgIAotIGEgc3BhY2UgdG8gc2VwYXJhdGUgZGF0ZSBmcm9tIHRpbWUgICAKLSBob3VyIChub3QgMjQtaG91ciB0aG91Z2gpICAKLSBtaW51dGVzIGluIDIgZGlnaXRzICAKLSBzZWNvbmRzIGluIDIgZGlnaXRzICAKLSBhIHNwYWNlIHRvIHNlcGFyYXRlIHRpbWUgZnJvbSB0aW1lem9uZSAgIAotIHRpbWV6b25lICAKLSBgL2Agc2VwYXJhdGluZyBkYXRlIG9iamVjdHMgIAotIGA6YCBzZXBhcmF0aW5nIHRpbWUgb2JqZWN0cyAgCgpUbyBtYWtlIHRoZSBmb3JtYXQgc3RyaW5nLCB3ZSBuZWVkIHRvIGxvb2sgdXAgaG93IHRvIGVuY29kZSB0aGVzZSBpdGVtcy4gIFRoZSBpdGVtcyBpbiBgZGZgIGZvciBhIGRhdGUgJiB0aW1lIG9iamVjdCBzdWNoIGFzIGByIGRmJERhdGVbNV1gIGhhdmUgdGhlIGZvcm1hdCBzdHJpbmc6CgpgYGB7cn0KZm9ybWF0IDwtICIlbS8lZC8lWSAlSTolTTolUyAlcCIKYGBgCgpOb3csIHdlIGNhbiBjb252ZXJ0IHRoZSBjaGFyYWN0ZXIgc3RyaW5nIGluIHRoZSBkYXRhIGZyYW1lIHRvIGEgZGF0ZSBhbmQgdGltZSBvYmplY3QuICBJbnN0ZWFkIG9mIHVzaW5nIHRoZSBidWlsdC1pbiBgYXMuRGF0ZSgpYCBmdW5jdGlvbmFsaXR5LCBJIGxpa2UgdGhlIGBsdWJyaWRhdGVgIGxpYnJhcnlbXjFdIGFzIGl0IGhhcyBhIGxvdCBvZiBhZGRpdGlvbmFsIGZ1bmN0aW9uYWxpdHkgdGhhdCB3ZSdsbCBwbGF5IHdpdGggYSBiaXQgbGF0ZXIuCgpgYGB7cn0KbGlicmFyeSggbHVicmlkYXRlICkKZGYkRGF0ZSA8LSBwYXJzZV9kYXRlX3RpbWUoIGRmJERhdGUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXJzPWZvcm1hdCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eiA9ICJFU1QiICkKc3VtbWFyeSggZGYgKQpjbGFzcyggZGYkRGF0ZSApCmBgYAoKTm93LCB3ZSBjYW4gYXNrICpEYXRlLWxpa2UqIHF1ZXN0aW9ucyBhYm91dCB0aGUgZGF0YSBzdWNoIGFzIHdoYXQgZGF5IG9mIHRoZSB3ZWVrIHdhcyB0aGUgZmlyc3Qgc2FtcGxlIHRha2VuPwoKYGBge3Igd2FybmluZz1GQUxTRSB9CndlZWtkYXlzKCBkZiREYXRlWzFdICkKYGBgCgpXaGF0IGlzIHRoZSByYW5nZSBvZiBkYXRlcz8KCmBgYHtyfQpyYW5nZSggZGYkRGF0ZSApCmBgYAoKV2hhdCBpcyB0aGUgbWVkaWFuIG9mIHNhbXBsZXMKCmBgYHtyfQptZWRpYW4oIGRmJERhdGUgKQpgYGAKCmFuZCB3aGF0IGp1bGlhbiBvcmRpbmFsIGRheSAoZS5nLiwgaG93IG1hbnkgZGF5cyBzaW5jZSBzdGFydCBvZiB0aGUgeWVhcikgaXMgdGhlIGxhc3QgcmVjb3JkLgoKYGBge3J9CnlkYXkoIGRmJERhdGVbNF0gKQpgYGAKCkp1c3QgZm9yIGZ1biwgSSdsbCBhZGQgYSBjb2x1bW4gdG8gdGhlIGRhdGEgdGhhdCBoYXMgd2Vla2RheS4KCmBgYHtyfQpkZiRXZWVrZGF5IDwtIHdlZWtkYXlzKCBkZiREYXRlICkKZGYKYGBgCgpIb3dldmVyLCB3ZSBzaG91bGQgcHJvYmFibHkgdHVybiBpdCBpbnRvIGEgZmFjdG9yIChlLmcuLCBhIGRhdGEgdHlwZSB3aXRoIHByZS1kZWZpbmVkIGxldmVsc+KAlGFuZCBmb3IgdXMgaGVyZeKAlGFuIGludHJpbnNpYyBvcmRlciBvZiB0aGUgbGV2ZWxzKS4KCmBgYHtyfQpkZiRXZWVrZGF5IDwtIGZhY3RvciggZGYkV2Vla2RheSwgCiAgICAgICAgICAgICAgICAgICAgICAgIG9yZGVyZWQgPSBUUlVFLCAKICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gZGF5c19vZl93ZWVrCiAgICAgICAgICAgICAgICAgICAgICAgICkKc3VtbWFyeSggZGYkV2Vla2RheSApCmBgYAoKCgojIyBGaWx0ZXJpbmcgb24gRGF0ZSBPYmplY3RzCgpXZSBjYW4gZWFzaWx5IGZpbHRlciB0aGUgY29udGVudCB3aXRoaW4gYSBgZGF0YS5mcmFtZWAgdXNpbmcgc29tZSBoZWxwZXIgZnVuY3Rpb25zIHN1Y2ggYXMgYGhvdXIoKWAsIGBtaW51dGUoKWAsIGB3ZWVrZGF5KClgLCBldGMuICBIZXJlIGFyZSBzb21lIGV4YW1wbGVzIGluY2x1ZGluZyBwdWxsaW5nIG91dCB0aGUgd2Vla2VuZHMuCgpgYGB7cn0Kd2Vla2VuZHMgPC0gZGZbIGRmJFdlZWtkYXkgJWluJSBjKCJTYXR1cmRheSIsIlN1bmRheSIpLCBdCndlZWtlbmRzCmBgYApmaW5kaW5nIGl0ZW1zIHRoYXQgYXJlIGluIHRoZSBwYXN0IChwYXN0ZSBiZWluZyBkZWZpbmVkIGFzIHRoZSBsYXN0IHRpbWUgdGhpcyBkb2N1bWVudCB3YXMga25pdCkuCgpgYGB7cn0KcGFzdCA8LSBkZiREYXRlWyBkZiREYXRlIDwgU3lzLnRpbWUoKSBdCnBhc3QKYGBgCgpJdGVtcyB0aGF0IGFyZSBkdXJpbmcgd29ya2luZyBob3VycwoKYGBge3J9CndvcmsgPC0gZGYkRGF0ZVsgaG91cihkZiREYXRlKSA+PSA5ICYgaG91cihkZiREYXRlKSA8PSAxNyBdCndvcmsKYGBgCgpBbmQgdG90YWwgcmFuZ2Ugb2YgdmFsdWVzIGluIGRheXMgdXNpbmcgbm9ybWFsIGFyaXRobWF0aWMgb3BlcmF0aW9ucyBzdWNoIGFzIHRoZSBtaW51cyBvcGVyYXRvci4KCmBgYHtyfQptYXgoZGYkRGF0ZSkgLSBtaW4oZGYkRGF0ZSkKYGBgCgoKW14xXTogSWYgeW91IGdldCBhbiBlcnJvciBzYXlpbmcgc29tZXRoaW5nIGxpa2UsICJ0aGVyZSBpcyBubyBwYWNrYWdlIG5hbWVkIGx1YnJpZGF0ZSIgdGhlbiB1c2UgYGluc3RhbGwucGFja2FnZXMoImx1YnJpZGF0ZSIpYCBhbmQgaW5zdGFsbCBpdC4gIFlvdSBvbmx5IG5lZWQgdG8gZG8gdGhpcyBvbmNlLgo=