Background

Data are often associated with a point in time, a particular

Some issues with points in time:

R has data types to represent

R stores dates as days since January 1, 1970, and date-times as the the number of seconds since midnight on that day in Coordinated Universal Time (UTC).

Date objects are less complicated than data-time objects, so if you only need dates you should stick with date objects.

Base R provides many facilities for dealing with dates and date-times.

The lubridate package provides a useful interface.

library(lubridate)

The Dates and Times chapter of R for Data Science provides more details.

Creating Dates and Times

Today and Now

The lubridate function today() returns today’s date as a Date object:

today()
## [1] "2023-05-05"
class(today())
## [1] "Date"

The lubridate function now() returns the current date-time as a POSIXct object:

now()
## [1] "2023-05-05 19:31:08 CDT"
class(now())
## [1] "POSIXct" "POSIXt"

The printed representation follows the international standard for the representation of dates and times (ISO8601).

Date and date-time objects can be used with addition and subtraction:

now() + 3600  ## one hour from now
## [1] "2023-05-05 20:31:08 CDT"
today() - 7   ## one week ago
## [1] "2023-04-28"

Parsing Dates and Times From Strings

Some common date formats:

d1 <- "2023-04-16"
d2 <- "April 16, 2023"
d3 <- "16 April 2023"
d4 <- "16 April 23"

These can be decoded by the functions ymd(), mdy(), and dmy():

ymd(d1)
## [1] "2023-04-16"
mdy(d2)
## [1] "2023-04-16"
dmy(d3)
## [1] "2023-04-16"
dmy(d4)
## [1] "2023-04-16"

By default, these functions use the current locale settings for interpreting month names or abbreviations.

Sys.getlocale("LC_TIME")
## [1] "en_US.UTF-8"

If you need to parse a French date you might use

dmy("16 Avril, 2023", locale = "fr_FR.UTF-8")
## [1] "2023-04-16"

Date-times can be decoded with functions like mdy_hm:

mdy_hm("April 16, 2023, 6:15 PM")
## [1] "2023-04-16 18:15:00 UTC"

or

mdy_hms("April 16, 2023, 6:15:08 PM")
## [1] "2023-04-16 18:15:08 UTC"

By default these assume the time is specified in the UTC time zone.

Creating Dates and Times from Components

Dates can be created from year, month, and day by make_date():

make_date(2023, 4, 16)
## [1] "2023-04-16"

Creating a date variable from the year, month, and day variables in the New York City flights table:

library(nycflights13)
fl <- mutate(flights,
             date = make_date(year, month, day))

ggplot and other graphics systems know how to make useful axis labels for dates:

ggplot(count(fl, date)) +
    geom_line(aes(x = date, y = n))

Weekday/weekend differenes are clearly visible.

Date-times can be created from year, month, day, hour, minute, and second using make_datetime():

make_datetime(2023, 4, 16, 18, 15)
## [1] "2023-04-16 18:15:00 UTC"

An attempt to recreate the time_hour variable in the flights table:

fl <- mutate(fl,
             th = make_datetime(year, month, day,
                                hour))

This does not quite re-create the time_hour variable:

identical(fl$th, fl$time_hour)
## [1] FALSE
fl$th[1]
## [1] "2013-01-01 05:00:00 UTC"
fl$time_hour[1]
## [1] "2013-01-01 05:00:00 EST"

By default, make_datetime() assumes the time points it is given are in UTC.

The time_hour variable is using local (eastern US) time.

We will look at time zones more below.

Date and Time Components

Components of dates and date-times can be extracted with:

By default, wday() returns an integer:

wday(today())
## [1] 6

But it can also return a label:

wday(today(), label = TRUE)
## [1] Fri
## Levels: Sun < Mon < Tue < Wed < Thu < Fri < Sat
wday(today(), label = TRUE, abbr = FALSE)
## [1] Friday
## 7 Levels: Sunday < Monday < Tuesday < Wednesday < Thursday < ... < Saturday

Weekday names and abbreviations are obviously locale-specific, and you can specify an alternative to the default current locale:

wday(today(), label = TRUE, abbr = FALSE, locale = "de_DE.UTF-8")
## [1] Freitag
## 7 Levels: Sonntag < Montag < Dienstag < Mittwoch < Donnerstag < ... < Samstag
wday(today(), label = TRUE, locale = "de_DE.UTF-8")
## [1] Fr
## Levels: So < Mo < Di < Mi < Do < Fr < Sa

Even the integer value can be tricky:

wday() can be asked to use a different first day, and a global default can be set.

Using wday() and the date variable we can look at the distribution of the number of flights by day of the week:

ggplot(fl, aes(x = wday(date, label = TRUE))) +
    geom_bar(fill = "deepskyblue3")

There were substantially fewer flights on Saturdays but only slightly fewer flights on Sundays.

Rounding

floor_date(), round_date(), and ceiling_date() can be used to round to a particular unit; the most useful are week and quarter.

Flights by week:

The first and last weeks were incomplete:

as.character(wday(ymd("2013-01-01"),
                  label = TRUE, abbr = FALSE))
## [1] "Tuesday"
as.character(wday(ymd("2013-12-31"),
                  label = TRUE, abbr = FALSE))
## [1] "Tuesday"

Time Spans

Subtracting dates or date-times produces difftime objects:

now() - as_datetime(today())
## Time difference of 1.021668 days
today() - ymd("2023-01-01")
## Time difference of 124 days

Working with different units can be awkward; lubridate provides durations, which always work in seconds:

as.duration(now() - as_datetime(today()))
## [1] "88272.1419148445s (~1.02 days)"
as.duration(today() - ymd("2023-01-01"))
## [1] "10713600s (~17.71 weeks)"

Durations can be created with dyears(), ddays(), dweeks(), etc.:

dyears(1)
## [1] "31557600s (~1 years)"
ddays(1)
## [1] "86400s (~1 days)"

Durations can be added to a date or date-time object and can be multiplied by a number:

today()
## [1] "2023-05-05"
today() + ddays(2)
## [1] "2023-05-07"
today() + 2 * ddays(1)
## [1] "2023-05-07"
(n1 <- now())
## [1] "2023-05-05 19:31:12 CDT"
n1 + dminutes(3)
## [1] "2023-05-05 19:34:12 CDT"

Duations represent an exact number of seconds, which can lead to surprises when DST is involved.

In 2023 the switch to DST happened in the US on March 12:

ymd_hm("2023-03-11 23:02", tz = "America/Chicago") + ddays(1)
## [1] "2023-03-13 00:02:00 CDT"

Periods are an alternative that may work more intuitively.

Periods are constructed with years(), months(), days(), etc:

ymd_hm("2023-03-11 23:02", tz = "America/Chicago") + days(1)
## [1] "2023-03-12 23:02:00 CDT"

Time Zones

Date-time objects specify a point in time relative to second zero, minute zero, hour zero, on January 1, 1970 in Coordinated Universal Time (UTC).

Date-time objects can have a time zone associated with them that affects how they are printed.

now() returns a date-time object with the time zone set as the local time zone of the computer.

now()
## [1] "2023-05-05 19:31:12 CDT"

Time zones are complex, they can change on a regular basis (DST) or as a result of politics.

When a date-time object is created from components, by default it is given the UTC time zone.

To create a point in time based on local time information, such as 10 AM on April 16, 2023, in Iowa City, a time zone for interpreting the local time needs to be specified.

The short notations like CDT are not adequate for this: Both the US and Australia have EST, which are quite different.

R uses the Internet Assigned Numbers Authority (IANA) naming convention and data base.

The local time zone is:

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

The time point 10:00:00 AM on April 16, 2023 in Iowa City can be specified as

(tm <- make_datetime(2023, 4, 16, 10, tz = "America/Chicago"))
## [1] "2023-04-16 10:00:00 CDT"

Time zones of date-time objects can be changed in two ways:

The available time zone specifications are contained in OlsonNames:

head(OlsonNames())
## [1] "Africa/Abidjan"     "Africa/Accra"       "Africa/Addis_Ababa"
## [4] "Africa/Algiers"     "Africa/Asmara"      "Africa/Asmera"

The instant tm in some other time zones:

with_tz(tm, tz = "UTC")
## [1] "2023-04-16 15:00:00 UTC"
with_tz(tm, tz = "America/New_York")
## [1] "2023-04-16 11:00:00 EDT"
with_tz(tm, tz = "Asia/Shanghai")
## [1] "2023-04-16 23:00:00 CST"
with_tz(tm, tz = "Pacific/Auckland")
## [1] "2023-04-17 03:00:00 NZST"
with_tz(tm, tz = "Asia/Kolkata")
## [1] "2023-04-16 20:30:00 IST"
with_tz(tm, tz = "Canada/Newfoundland")
## [1] "2023-04-16 12:30:00 NDT"
with_tz(tm, tz = "Asia/Katmandu")
## [1] "2023-04-16 20:45:00 +0545"

Some more examples:

## All offsets that are not a full hour:
get_offset <- function(z)
    abs(minute(with_tz(tm, tz = z)) - minute(tm))
offsets <- data.frame(zone = OlsonNames()) %>%
    mutate(offset = sapply(zone, get_offset)) %>%
    arrange(offset)
filter(offsets, offset != 0)

## Offsets for Australia:
filter(offsets, grepl("Australia", zone))

If we create the th variable for the flights data as

fl <- mutate(flights, th = make_datetime(year, month, day, hour,
                                         tz = "America/New_York"))

then the result matches the date_time variable:

identical(fl$th, fl$time_hour)
## [1] TRUE

The time_hour variable in the weather table reflects actual points in time and, together with origin, can serve as a primary key:

filter(count(weather, origin, time_hour), n > 1)
## # A tibble: 0 × 3
## # ℹ 3 variables: origin <chr>, time_hour <dttm>, n <int>

The month, day, hour variables are confused by the time change.

In November there is a repeat:

count(weather, origin, month, day, hour) %>%
    filter(n > 1)
## # A tibble: 3 × 5
##   origin month   day  hour     n
##   <chr>  <int> <int> <int> <int>
## 1 EWR       11     3     1     2
## 2 JFK       11     3     1     2
## 3 LGA       11     3     1     2

and there is a missing hour in March:

select(weather, origin, month, day, hour) %>%
    filter(origin == "EWR", month == 3,
           day == 10, hour <= 3)
## # A tibble: 3 × 4
##   origin month   day  hour
##   <chr>  <int> <int> <int>
## 1 EWR        3    10     0
## 2 EWR        3    10     1
## 3 EWR        3    10     3

Things to Look Out For

For dates:

For date-times

Reading

Chapter Dates and Times in R for Data Science.

Exercises

  1. Using the NYC flights data, how many flights were there on Saturdays from Newark (EWR) to Cicago O’Hare (ORD) in 2013?

    1. 413
    2. 522
    3. 601
    4. 733
  2. What day of the week will July 4, 2030, fall on?

    1. Monday
    2. Wednesday
    3. Thursday
    4. Saturday
LS0tCnRpdGxlOiAiRGF0ZXMgYW5kIFRpbWVzIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tCgo8bGluayByZWw9InN0eWxlc2hlZXQiIGhyZWY9InN0YXQ0NTgwLmNzcyIgdHlwZT0idGV4dC9jc3MiIC8+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+IC5yZW1hcmstY29kZSB7IGZvbnQtc2l6ZTogODUlOyB9IDwvc3R5bGU+CmBgYHtyIHNldHVwLCBpbmNsdWRlID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0Kc291cmNlKGhlcmU6OmhlcmUoInNldHVwLlIiKSkKa25pdHI6Om9wdHNfY2h1bmskc2V0KGNvbGxhcHNlID0gVFJVRSwgbWVzc2FnZSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgZmlnLmhlaWdodCA9IDUsIGZpZy53aWR0aCA9IDYsIGZpZy5hbGlnbiA9ICJjZW50ZXIiKQoKc2V0LnNlZWQoMTIzNDUpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShsYXR0aWNlKQpsaWJyYXJ5KGdyaWRFeHRyYSkKbGlicmFyeShwYXRjaHdvcmspCnNvdXJjZShoZXJlOjpoZXJlKCJkYXRhc2V0cy5SIikpCnRoZW1lX3NldCh0aGVtZV9taW5pbWFsKCkgKwogICAgICAgICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYpLAogICAgICAgICAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG9yID0gImdyZXkzMCIsIGZpbGwgPSBOQSkpKQpgYGAKCgojIyBCYWNrZ3JvdW5kCgpEYXRhIGFyZSBvZnRlbiBhc3NvY2lhdGVkIHdpdGggYSBwb2ludCBpbiB0aW1lLCBhIHBhcnRpY3VsYXIKCiogeWVhcjsKCiogbW9udGg7CgoqIGRheTsKCiogaG91ciwgbWludXRlLCBzZWNvbmQsIC4uLgoKU29tZSBpc3N1ZXMgd2l0aCBwb2ludHMgaW4gdGltZToKCiogTGVhcCB5ZWFycyBhbmQgbGVhcCBzZWNvbmRzLgoKKiBEYXlsaWdodCBzYXZpbmcgdGltZS4KCiogTG9jYWwgdGltZSBvcgogIFtfQ29vcmRpbmF0ZWQgVW5pdmVyc2FsIFRpbWUKICAgICAgKFVUQylfXShodHRwczovL3d3dy50aW1lYW5kZGF0ZS5jb20vdGltZS9hYm91dHV0Yy5odG1sKS4KCiogRm9yIGhpc3RvcmljYWwgZGF0YTogY2hhbmdlcyBpbiBjYWxlbmRhcnMuCgpSIGhhcyBkYXRhIHR5cGVzIHRvIHJlcHJlc2VudAoKKiBhIHBhcnRpY3VsYXIgZGF0ZSAoYERhdGVgKTsKCiogYSBwYXJ0aWN1bGFyIHNlY29uZCAoYFBPU0lYY3RgLCBkYXRlLXRpbWUpLgoKUiBzdG9yZXMgZGF0ZXMgYXMgZGF5cyBzaW5jZSBKYW51YXJ5IDEsIDE5NzAsIGFuZCBkYXRlLXRpbWVzIGFzIHRoZQp0aGUgbnVtYmVyIG9mIHNlY29uZHMgc2luY2UgbWlkbmlnaHQgb24gdGhhdCBkYXkgaW4KW19Db29yZGluYXRlZCBVbml2ZXJzYWwgVGltZV8KICAgIChVVEMpXShodHRwczovL3d3dy50aW1lYW5kZGF0ZS5jb20vdGltZS9hYm91dHV0Yy5odG1sKS4KCkRhdGUgb2JqZWN0cyBhcmUgbGVzcyBjb21wbGljYXRlZCB0aGFuIGRhdGEtdGltZSBvYmplY3RzLCBzbyBpZiB5b3UKb25seSBuZWVkIGRhdGVzIHlvdSBzaG91bGQgc3RpY2sgd2l0aCBkYXRlIG9iamVjdHMuCgpCYXNlIFIgcHJvdmlkZXMgbWFueSBmYWNpbGl0aWVzIGZvciBkZWFsaW5nIHdpdGggZGF0ZXMgYW5kIGRhdGUtdGltZXMuCgpUaGUgYGx1YnJpZGF0ZWAgcGFja2FnZSBwcm92aWRlcyBhIHVzZWZ1bCBpbnRlcmZhY2UuCgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFfQpsaWJyYXJ5KGx1YnJpZGF0ZSkKYGBgCgpUaGUKW19EYXRlcyBhbmQgVGltZXNfIGNoYXB0ZXJdKGh0dHBzOi8vcjRkcy5oYWQuY28ubnovZGF0ZXMtYW5kLXRpbWVzLmh0bWwpCm9mIFtfUiBmb3IgRGF0YSBTY2llbmNlX10oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei8pIHByb3ZpZGVzIG1vcmUKZGV0YWlscy4KCgojIyBDcmVhdGluZyBEYXRlcyBhbmQgVGltZXMKCgojIyMgVG9kYXkgYW5kIE5vdwoKVGhlIGBsdWJyaWRhdGVgIGZ1bmN0aW9uIGB0b2RheSgpYCByZXR1cm5zIHRvZGF5J3MgZGF0ZSBhcyBhIGBEYXRlYCBvYmplY3Q6CgpgYGB7cn0KdG9kYXkoKQpgYGAKCmBgYHtyfQpjbGFzcyh0b2RheSgpKQpgYGAKClRoZSBgbHVicmlkYXRlYCBmdW5jdGlvbiBgbm93KClgIHJldHVybnMgdGhlIGN1cnJlbnQgZGF0ZS10aW1lIGFzIGEKYFBPU0lYY3RgIG9iamVjdDoKCmBgYHtyfQpub3coKQpgYGAKCmBgYHtyfQpjbGFzcyhub3coKSkKYGBgCgpUaGUgcHJpbnRlZCByZXByZXNlbnRhdGlvbiBmb2xsb3dzIHRoZSBpbnRlcm5hdGlvbmFsIHN0YW5kYXJkIGZvciB0aGUKcmVwcmVzZW50YXRpb24gb2YgZGF0ZXMgYW5kIHRpbWVzCihbSVNPODYwMV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvSVNPXzg2MDEpKS4KCkRhdGUgYW5kIGRhdGUtdGltZSBvYmplY3RzIGNhbiBiZSB1c2VkIHdpdGggYWRkaXRpb24gYW5kIHN1YnRyYWN0aW9uOgoKYGBge3J9Cm5vdygpICsgMzYwMCAgIyMgb25lIGhvdXIgZnJvbSBub3cKYGBgCgpgYGB7cn0KdG9kYXkoKSAtIDcgICAjIyBvbmUgd2VlayBhZ28KYGBgCgoKIyMjIFBhcnNpbmcgRGF0ZXMgYW5kIFRpbWVzIEZyb20gU3RyaW5ncwoKU29tZSBjb21tb24gZGF0ZSBmb3JtYXRzOgoKYGBge3J9CmQxIDwtICIyMDIzLTA0LTE2IgpkMiA8LSAiQXByaWwgMTYsIDIwMjMiCmQzIDwtICIxNiBBcHJpbCAyMDIzIgpkNCA8LSAiMTYgQXByaWwgMjMiCmBgYAoKVGhlc2UgY2FuIGJlIGRlY29kZWQgYnkgdGhlIGZ1bmN0aW9ucyBgeW1kKClgLCBgbWR5KClgLCBhbmQgYGRteSgpYDoKCmBgYHtyfQp5bWQoZDEpCmBgYAoKYGBge3J9Cm1keShkMikKYGBgCgpgYGB7cn0KZG15KGQzKQpgYGAKCmBgYHtyfQpkbXkoZDQpCmBgYAoKQnkgZGVmYXVsdCwgdGhlc2UgZnVuY3Rpb25zIHVzZSB0aGUgY3VycmVudCBfbG9jYWxlXyBzZXR0aW5ncyBmb3IKaW50ZXJwcmV0aW5nIG1vbnRoIG5hbWVzIG9yIGFiYnJldmlhdGlvbnMuCgpgYGB7cn0KU3lzLmdldGxvY2FsZSgiTENfVElNRSIpCmBgYAoKSWYgeW91IG5lZWQgdG8gcGFyc2UgYSBGcmVuY2ggZGF0ZSB5b3UgbWlnaHQgdXNlCgpgYGB7cn0KZG15KCIxNiBBdnJpbCwgMjAyMyIsIGxvY2FsZSA9ICJmcl9GUi5VVEYtOCIpCmBgYAoKRGF0ZS10aW1lcyBjYW4gYmUgZGVjb2RlZCB3aXRoIGZ1bmN0aW9ucyBsaWtlIGBtZHlfaG1gOgoKYGBge3J9Cm1keV9obSgiQXByaWwgMTYsIDIwMjMsIDY6MTUgUE0iKQpgYGAKCm9yCgpgYGB7cn0KbWR5X2htcygiQXByaWwgMTYsIDIwMjMsIDY6MTU6MDggUE0iKQpgYGAKCkJ5IGRlZmF1bHQgdGhlc2UgYXNzdW1lIHRoZSB0aW1lIGlzIHNwZWNpZmllZCBpbiB0aGUgVVRDIHRpbWUgem9uZS4KCgojIyMgQ3JlYXRpbmcgRGF0ZXMgYW5kIFRpbWVzIGZyb20gQ29tcG9uZW50cwoKRGF0ZXMgY2FuIGJlIGNyZWF0ZWQgZnJvbSB5ZWFyLCBtb250aCwgYW5kIGRheSBieSBgbWFrZV9kYXRlKClgOgoKYGBge3J9Cm1ha2VfZGF0ZSgyMDIzLCA0LCAxNikKYGBgCgpDcmVhdGluZyBhIGBkYXRlYCB2YXJpYWJsZSBmcm9tIHRoZSBgeWVhcmAsIGBtb250aGAsIGFuZCBgZGF5YAp2YXJpYWJsZXMgaW4gdGhlIE5ldyBZb3JrIENpdHkgYGZsaWdodHNgIHRhYmxlOgoKYGBge3J9CmxpYnJhcnkobnljZmxpZ2h0czEzKQpmbCA8LSBtdXRhdGUoZmxpZ2h0cywKICAgICAgICAgICAgIGRhdGUgPSBtYWtlX2RhdGUoeWVhciwgbW9udGgsIGRheSkpCmBgYAoKYGdncGxvdGAgYW5kIG90aGVyIGdyYXBoaWNzIHN5c3RlbXMga25vdyBob3cgdG8gbWFrZSB1c2VmdWwgYXhpcwpsYWJlbHMgZm9yIGRhdGVzOgoKYGBge3IsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQpnZ3Bsb3QoY291bnQoZmwsIGRhdGUpKSArCiAgICBnZW9tX2xpbmUoYWVzKHggPSBkYXRlLCB5ID0gbikpCmBgYAoKV2Vla2RheS93ZWVrZW5kIGRpZmZlcmVuZXMgYXJlIGNsZWFybHkgdmlzaWJsZS4KCkRhdGUtdGltZXMgY2FuIGJlIGNyZWF0ZWQgZnJvbSB5ZWFyLCBtb250aCwgZGF5LCBob3VyLCBtaW51dGUsIGFuZCBzZWNvbmQKdXNpbmcgYG1ha2VfZGF0ZXRpbWUoKWA6CgpgYGB7cn0KbWFrZV9kYXRldGltZSgyMDIzLCA0LCAxNiwgMTgsIDE1KQpgYGAKCkFuIGF0dGVtcHQgdG8gcmVjcmVhdGUgdGhlIGB0aW1lX2hvdXJgIHZhcmlhYmxlIGluIHRoZSBmbGlnaHRzIHRhYmxlOgoKYGBge3J9CmZsIDwtIG11dGF0ZShmbCwKICAgICAgICAgICAgIHRoID0gbWFrZV9kYXRldGltZSh5ZWFyLCBtb250aCwgZGF5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhvdXIpKQpgYGAKClRoaXMgZG9lcyBub3QgcXVpdGUgcmUtY3JlYXRlIHRoZSBgdGltZV9ob3VyYCB2YXJpYWJsZToKYGBge3J9CmlkZW50aWNhbChmbCR0aCwgZmwkdGltZV9ob3VyKQpgYGAKCmBgYHtyfQpmbCR0aFsxXQpmbCR0aW1lX2hvdXJbMV0KYGBgCgpCeSBkZWZhdWx0LCBgbWFrZV9kYXRldGltZSgpYCBhc3N1bWVzIHRoZSB0aW1lIHBvaW50cyBpdCBpcyBnaXZlbiBhcmUgaW4gVVRDLgoKVGhlIGB0aW1lX2hvdXJgIHZhcmlhYmxlIGlzIHVzaW5nIGxvY2FsIChlYXN0ZXJuIFVTKSB0aW1lLgoKV2Ugd2lsbCBsb29rIGF0IHRpbWUgem9uZXMgbW9yZSBbYmVsb3ddKCN0aW1lLXpvbmVzKS4KCgojIyBEYXRlIGFuZCBUaW1lIENvbXBvbmVudHMKCkNvbXBvbmVudHMgb2YgZGF0ZXMgYW5kIGRhdGUtdGltZXMgY2FuIGJlIGV4dHJhY3RlZCB3aXRoOgoKKiBgeWVhcigpYCwgYG1vbnRoKClgLCBgZGF5KClgLCBgaG91cigpYCwgYG1pbnV0ZSgpYCwgYHNlY29uZCgpYAoqIGB5ZGF5KClgIC0tIGRheSBvZiB0aGUgeWVhcgoqIGBtZGF5KClgIC0tIGRheSBvZiB0aGUgbW9udGgsIHNhbWUgYXMgYGRheWAKKiBgd2RheSgpYCAtLSBkYXkgb2YgdGhlIHdlZWsKCkJ5IGRlZmF1bHQsIGB3ZGF5KClgIHJldHVybnMgYW4gaW50ZWdlcjoKCmBgYHtyfQp3ZGF5KHRvZGF5KCkpCmBgYAoKQnV0IGl0IGNhbiBhbHNvIHJldHVybiBhIGxhYmVsOgoKYGBge3J9CndkYXkodG9kYXkoKSwgbGFiZWwgPSBUUlVFKQpgYGAKCmBgYHtyfQp3ZGF5KHRvZGF5KCksIGxhYmVsID0gVFJVRSwgYWJiciA9IEZBTFNFKQpgYGAKCldlZWtkYXkgbmFtZXMgYW5kIGFiYnJldmlhdGlvbnMgYXJlIG9idmlvdXNseSBsb2NhbGUtc3BlY2lmaWMsIGFuZCB5b3UKY2FuIHNwZWNpZnkgYW4gYWx0ZXJuYXRpdmUgdG8gdGhlIGRlZmF1bHQgY3VycmVudCBsb2NhbGU6CgpgYGB7cn0Kd2RheSh0b2RheSgpLCBsYWJlbCA9IFRSVUUsIGFiYnIgPSBGQUxTRSwgbG9jYWxlID0gImRlX0RFLlVURi04IikKYGBgCgpgYGB7cn0Kd2RheSh0b2RheSgpLCBsYWJlbCA9IFRSVUUsIGxvY2FsZSA9ICJkZV9ERS5VVEYtOCIpCmBgYAoKRXZlbiB0aGUgaW50ZWdlciB2YWx1ZSBjYW4gYmUgdHJpY2t5OgoKKiBJbiB0aGUgVVMsIENhbmFkYSwgSmFwYW4gdGhlIGZpcnN0IGRheSBvZiB0aGUgd2VlayBpcyBTdW5kYXkuCgoqIEluIEdlcm1hbnksIEZyYW5jZSwgYW5kIHRoZSBJU084NjAxIHN0YW5kYXJkIHRoZSBmaXJzdCBkYXkgb2YgdGhlCiAgd2VlayBpcyBNb25kYXkuCgpgd2RheSgpYCBjYW4gYmUgYXNrZWQgdG8gdXNlIGEgZGlmZmVyZW50IGZpcnN0IGRheSwgYW5kIGEgZ2xvYmFsIGRlZmF1bHQKY2FuIGJlIHNldC4KClVzaW5nIGB3ZGF5KClgIGFuZCB0aGUgYGRhdGVgIHZhcmlhYmxlIHdlIGNhbiBsb29rIGF0IHRoZSBkaXN0cmlidXRpb24Kb2YgdGhlIG51bWJlciBvZiBmbGlnaHRzIGJ5IGRheSBvZiB0aGUgd2VlazoKCmBgYHtyIGZsaWdodHMtd2RheSwgZXZhbCA9IEZBTFNFfQpnZ3Bsb3QoZmwsIGFlcyh4ID0gd2RheShkYXRlLCBsYWJlbCA9IFRSVUUpKSkgKwogICAgZ2VvbV9iYXIoZmlsbCA9ICJkZWVwc2t5Ymx1ZTMiKQpgYGAKCmBgYHtyIGZsaWdodHMtd2RheSwgZWNobyA9IEZBTFNFfQpgYGAKClRoZXJlIHdlcmUgc3Vic3RhbnRpYWxseSBmZXdlciBmbGlnaHRzIG9uIFNhdHVyZGF5cyBidXQgb25seSBzbGlnaHRseQpmZXdlciBmbGlnaHRzIG9uIFN1bmRheXMuCgoKIyMgUm91bmRpbmcKCmBmbG9vcl9kYXRlKClgLCBgcm91bmRfZGF0ZSgpYCwgYW5kIGBjZWlsaW5nX2RhdGUoKWAgY2FuIGJlIHVzZWQgdG8Kcm91bmQgdG8gYSBwYXJ0aWN1bGFyIHVuaXQ7IHRoZSBtb3N0IHVzZWZ1bCBhcmUgYHdlZWtgIGFuZCBgcXVhcnRlcmAuCgpGbGlnaHRzIGJ5IHdlZWs6CgpgYGB7ciwgZWNobyA9IEZBTFNFLCBkcGkgPSAzMDAsIGZpZy5oZWlnaHQgPSA0fQpnZ3Bsb3QoZmwsIGFlcyh4ID0gcm91bmRfZGF0ZShkYXRlLCAid2VlayIpKSkgKwogICAgZ2VvbV9iYXIoZmlsbCA9ICJkZWVwc2t5Ymx1ZTMiKQpgYGAKClRoZSBmaXJzdCBhbmQgbGFzdCB3ZWVrcyB3ZXJlIGluY29tcGxldGU6CgpgYGB7cn0KYXMuY2hhcmFjdGVyKHdkYXkoeW1kKCIyMDEzLTAxLTAxIiksCiAgICAgICAgICAgICAgICAgIGxhYmVsID0gVFJVRSwgYWJiciA9IEZBTFNFKSkKYGBgCmBgYHtyfQphcy5jaGFyYWN0ZXIod2RheSh5bWQoIjIwMTMtMTItMzEiKSwKICAgICAgICAgICAgICAgICAgbGFiZWwgPSBUUlVFLCBhYmJyID0gRkFMU0UpKQpgYGAKCgojIyBUaW1lIFNwYW5zCgpTdWJ0cmFjdGluZyBkYXRlcyBvciBkYXRlLXRpbWVzIHByb2R1Y2VzIGBkaWZmdGltZWAgb2JqZWN0czoKCmBgYHtyfQpub3coKSAtIGFzX2RhdGV0aW1lKHRvZGF5KCkpCmBgYAoKYGBge3J9CnRvZGF5KCkgLSB5bWQoIjIwMjMtMDEtMDEiKQpgYGAKCldvcmtpbmcgd2l0aCBkaWZmZXJlbnQgdW5pdHMgY2FuIGJlIGF3a3dhcmQ7IGBsdWJyaWRhdGVgIHByb3ZpZGVzCl9kdXJhdGlvbnNfLCB3aGljaCBhbHdheXMgd29yayBpbiBzZWNvbmRzOgoKYGBge3J9CmFzLmR1cmF0aW9uKG5vdygpIC0gYXNfZGF0ZXRpbWUodG9kYXkoKSkpCmBgYAoKYGBge3J9CmFzLmR1cmF0aW9uKHRvZGF5KCkgLSB5bWQoIjIwMjMtMDEtMDEiKSkKYGBgCgpEdXJhdGlvbnMgY2FuIGJlIGNyZWF0ZWQgd2l0aCBgZHllYXJzKClgLCBgZGRheXMoKWAsIGBkd2Vla3MoKWAsIGV0Yy46CgpgYGB7cn0KZHllYXJzKDEpCmBgYAoKYGBge3J9CmRkYXlzKDEpCmBgYAoKRHVyYXRpb25zIGNhbiBiZSBhZGRlZCB0byBhIGRhdGUgb3IgZGF0ZS10aW1lIG9iamVjdCBhbmQgY2FuIGJlCm11bHRpcGxpZWQgYnkgYSBudW1iZXI6CgpgYGB7cn0KdG9kYXkoKQpgYGAKCmBgYHtyfQp0b2RheSgpICsgZGRheXMoMikKYGBgCgpgYGB7cn0KdG9kYXkoKSArIDIgKiBkZGF5cygxKQpgYGAKCmBgYHtyfQoobjEgPC0gbm93KCkpCmBgYAoKYGBge3J9Cm4xICsgZG1pbnV0ZXMoMykKYGBgCgpEdWF0aW9ucyByZXByZXNlbnQgYW4gZXhhY3QgbnVtYmVyIG9mIHNlY29uZHMsIHdoaWNoIGNhbiBsZWFkIHRvCnN1cnByaXNlcyB3aGVuIERTVCBpcyBpbnZvbHZlZC4KCkluIDIwMjMgdGhlIHN3aXRjaCB0byBEU1QgaGFwcGVuZWQgaW4gdGhlIFVTIG9uIE1hcmNoIDEyOgoKYGBge3J9CnltZF9obSgiMjAyMy0wMy0xMSAyMzowMiIsIHR6ID0gIkFtZXJpY2EvQ2hpY2FnbyIpICsgZGRheXMoMSkKYGBgCgpfUGVyaW9kc18gYXJlIGFuIGFsdGVybmF0aXZlIHRoYXQgbWF5IHdvcmsgbW9yZSBpbnR1aXRpdmVseS4KClBlcmlvZHMgYXJlIGNvbnN0cnVjdGVkIHdpdGggYHllYXJzKClgLCBgbW9udGhzKClgLCBgZGF5cygpYCwgZXRjOgoKYGBge3J9CnltZF9obSgiMjAyMy0wMy0xMSAyMzowMiIsIHR6ID0gIkFtZXJpY2EvQ2hpY2FnbyIpICsgZGF5cygxKQpgYGAKCgojIyBUaW1lIFpvbmVzCgpEYXRlLXRpbWUgb2JqZWN0cyBzcGVjaWZ5IGEgcG9pbnQgaW4gdGltZSByZWxhdGl2ZSB0byBzZWNvbmQgemVybywKbWludXRlIHplcm8sIGhvdXIgemVybywgb24gSmFudWFyeSAxLCAxOTcwIGluCltDb29yZGluYXRlZCBVbml2ZXJzYWwgVGltZSAoVVRDKV0oaHR0cHM6Ly93d3cudGltZWFuZGRhdGUuY29tL3RpbWUvYWJvdXR1dGMuaHRtbCkuCgpEYXRlLXRpbWUgb2JqZWN0cyBjYW4gaGF2ZSBhIHRpbWUgem9uZSBhc3NvY2lhdGVkIHdpdGggdGhlbSB0aGF0CmFmZmVjdHMgaG93IHRoZXkgYXJlIHByaW50ZWQuCgpgbm93KClgIHJldHVybnMgYSBkYXRlLXRpbWUgb2JqZWN0IHdpdGggdGhlIHRpbWUgem9uZSBzZXQgYXMgdGhlCmxvY2FsIHRpbWUgem9uZSBvZiB0aGUgY29tcHV0ZXIuCgpgYGB7cn0Kbm93KCkKYGBgCgpUaW1lIHpvbmVzIGFyZSBjb21wbGV4LCB0aGV5IGNhbiBjaGFuZ2Ugb24gYSByZWd1bGFyIGJhc2lzIChEU1QpIG9yIGFzCmEgcmVzdWx0IG9mIHBvbGl0aWNzLgoKV2hlbiBhIGRhdGUtdGltZSBvYmplY3QgaXMgY3JlYXRlZCBmcm9tIGNvbXBvbmVudHMsIGJ5IGRlZmF1bHQgaXQgaXMKZ2l2ZW4gdGhlIFVUQyB0aW1lIHpvbmUuCgpUbyBjcmVhdGUgYSBwb2ludCBpbiB0aW1lIGJhc2VkIG9uIGxvY2FsIHRpbWUgaW5mb3JtYXRpb24sIHN1Y2ggYXMgMTAKQU0gb24gQXByaWwgMTYsIDIwMjMsIGluIElvd2EgQ2l0eSwgYSB0aW1lIHpvbmUgZm9yIGludGVycHJldGluZyB0aGUKbG9jYWwgdGltZSBuZWVkcyB0byBiZSBzcGVjaWZpZWQuCgpUaGUgc2hvcnQgbm90YXRpb25zIGxpa2UgQ0RUIGFyZSBub3QgYWRlcXVhdGUgZm9yIHRoaXM6IEJvdGggdGhlIFVTCiBhbmQgQXVzdHJhbGlhIGhhdmUgRVNULCB3aGljaCBhcmUgcXVpdGUgZGlmZmVyZW50LgoKUiB1c2VzIHRoZQpbX0ludGVybmV0IEFzc2lnbmVkIE51bWJlcnMgQXV0aG9yaXR5XyAoSUFOQSldKGh0dHBzOi8vd3d3LmlhbmEub3JnL3RpbWUtem9uZXMpCm5hbWluZyBjb252ZW50aW9uIGFuZCBkYXRhIGJhc2UuCgpUaGUgbG9jYWwgdGltZSB6b25lIGlzOgoKYGBge3J9ClN5cy50aW1lem9uZSgpCmBgYAoKVGhlIHRpbWUgcG9pbnQgMTA6MDA6MDAgQU0gb24gQXByaWwgMTYsIDIwMjMgaW4gSW93YSBDaXR5IGNhbiBiZQpzcGVjaWZpZWQgYXMKCmBgYHtyfQoodG0gPC0gbWFrZV9kYXRldGltZSgyMDIzLCA0LCAxNiwgMTAsIHR6ID0gIkFtZXJpY2EvQ2hpY2FnbyIpKQpgYGAKClRpbWUgem9uZXMgb2YgZGF0ZS10aW1lIG9iamVjdHMgY2FuIGJlIGNoYW5nZWQgaW4gdHdvIHdheXM6CgoqIGB3aXRoX3R6YCBrZWVwcyB0aGUgaW5zdGFudCBpbiB0aW1lIGFuZCBjaGFuZ2VzIHRoZSB0aW1lIHpvbmUgdXNlZAogIGZvciBkaXNwbGF5LgoKKiBgZm9yY2VfdHpgIGNoYW5nZXMgdGhlIGluc3RhbnQgaW4gdGltZTsgdXNlIHRoaXMgaWYgdGhlIHRpbWUgem9uZSBpcwogIGluY29ycmVjdGx5IHNwZWNpZmllZCBidXQgdGhlIGNsb2NrIHRpbWUgaXMgY29ycmVjdC4KClRoZSBhdmFpbGFibGUgdGltZSB6b25lIHNwZWNpZmljYXRpb25zIGFyZSBjb250YWluZWQgaW4gYE9sc29uTmFtZXNgOgoKYGBge3J9CmhlYWQoT2xzb25OYW1lcygpKQpgYGAKClRoZSBpbnN0YW50IGB0bWAgaW4gc29tZSBvdGhlciB0aW1lIHpvbmVzOgoKYGBge3J9CndpdGhfdHoodG0sIHR6ID0gIlVUQyIpCmBgYAoKYGBge3J9CndpdGhfdHoodG0sIHR6ID0gIkFtZXJpY2EvTmV3X1lvcmsiKQpgYGAKCmBgYHtyfQp3aXRoX3R6KHRtLCB0eiA9ICJBc2lhL1NoYW5naGFpIikKYGBgCgpgYGB7cn0Kd2l0aF90eih0bSwgdHogPSAiUGFjaWZpYy9BdWNrbGFuZCIpCmBgYAoKYGBge3J9CndpdGhfdHoodG0sIHR6ID0gIkFzaWEvS29sa2F0YSIpCmBgYAoKYGBge3J9CndpdGhfdHoodG0sIHR6ID0gIkNhbmFkYS9OZXdmb3VuZGxhbmQiKQpgYGAKCmBgYHtyfQp3aXRoX3R6KHRtLCB0eiA9ICJBc2lhL0thdG1hbmR1IikKYGBgCgpTb21lIG1vcmUgZXhhbXBsZXM6CgpgYGB7ciwgZXZhbCA9IEZBTFNFfQojIyBBbGwgb2Zmc2V0cyB0aGF0IGFyZSBub3QgYSBmdWxsIGhvdXI6CmdldF9vZmZzZXQgPC0gZnVuY3Rpb24oeikKICAgIGFicyhtaW51dGUod2l0aF90eih0bSwgdHogPSB6KSkgLSBtaW51dGUodG0pKQpvZmZzZXRzIDwtIGRhdGEuZnJhbWUoem9uZSA9IE9sc29uTmFtZXMoKSkgJT4lCiAgICBtdXRhdGUob2Zmc2V0ID0gc2FwcGx5KHpvbmUsIGdldF9vZmZzZXQpKSAlPiUKICAgIGFycmFuZ2Uob2Zmc2V0KQpmaWx0ZXIob2Zmc2V0cywgb2Zmc2V0ICE9IDApCgojIyBPZmZzZXRzIGZvciBBdXN0cmFsaWE6CmZpbHRlcihvZmZzZXRzLCBncmVwbCgiQXVzdHJhbGlhIiwgem9uZSkpCmBgYAoKSWYgd2UgY3JlYXRlIHRoZSBgdGhgIHZhcmlhYmxlIGZvciB0aGUgZmxpZ2h0cyBkYXRhIGFzCgpgYGB7cn0KZmwgPC0gbXV0YXRlKGZsaWdodHMsIHRoID0gbWFrZV9kYXRldGltZSh5ZWFyLCBtb250aCwgZGF5LCBob3VyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR6ID0gIkFtZXJpY2EvTmV3X1lvcmsiKSkKYGBgCgp0aGVuIHRoZSByZXN1bHQgbWF0Y2hlcyB0aGUgYGRhdGVfdGltZWAgdmFyaWFibGU6CgpgYGB7cn0KaWRlbnRpY2FsKGZsJHRoLCBmbCR0aW1lX2hvdXIpCmBgYAoKVGhlIGB0aW1lX2hvdXJgIHZhcmlhYmxlIGluIHRoZSBgd2VhdGhlcmAgdGFibGUgcmVmbGVjdHMgYWN0dWFsIHBvaW50cwppbiB0aW1lIGFuZCwgdG9nZXRoZXIgd2l0aCBgb3JpZ2luYCwgY2FuIHNlcnZlIGFzIGEgcHJpbWFyeSBrZXk6CgpgYGB7cn0KZmlsdGVyKGNvdW50KHdlYXRoZXIsIG9yaWdpbiwgdGltZV9ob3VyKSwgbiA+IDEpCmBgYAoKVGhlIGBtb250aGAsIGBkYXlgLCBgaG91cmAgdmFyaWFibGVzIGFyZSBjb25mdXNlZCBieSB0aGUgdGltZQpjaGFuZ2UuCgpJbiBOb3ZlbWJlciB0aGVyZSBpcyBhIHJlcGVhdDoKCmBgYHtyfQpjb3VudCh3ZWF0aGVyLCBvcmlnaW4sIG1vbnRoLCBkYXksIGhvdXIpICU+JQogICAgZmlsdGVyKG4gPiAxKQpgYGAKCmFuZCB0aGVyZSBpcyBhIG1pc3NpbmcgaG91ciBpbiBNYXJjaDoKCmBgYHtyfQpzZWxlY3Qod2VhdGhlciwgb3JpZ2luLCBtb250aCwgZGF5LCBob3VyKSAlPiUKICAgIGZpbHRlcihvcmlnaW4gPT0gIkVXUiIsIG1vbnRoID09IDMsCiAgICAgICAgICAgZGF5ID09IDEwLCBob3VyIDw9IDMpCmBgYAoKCiMjIFRoaW5ncyB0byBMb29rIE91dCBGb3IKCkZvciBkYXRlczoKCiogTGFuZ3VhZ2UgdXNlZCBmb3IgbW9udGhzIGFuZCB3ZWVrZGF5cywgYW5kIHRoZWlyIGFiYnJldmlhdGlvbnMuCgoqIEFtYmlndW91cyBudW1lcmljYWwgY29udmVudGlvbnMgbGlrZSA0LzExLzIwMjM6IGlzIHRoaXMgQXByaWwgMTEgb3IKICBOb3ZlbWJlciA0PwoKKiBEYXkgMiBvZiB0aGUgd2VlazogaXMgdGhpcyBNb25kYXkgb3IgVHVlc2RheT8KCiogRm9yIGhpc3RvcmljYWwgZGF0YSwgd2hhdCBjYWxlbmRhciBpcyBiZWluZyB1c2VkPyAoVGhlIF9PY3RvYmVyCiAgUmV2b2x1dGlvbl8gaGFwcGVuZWQgb24gTm92ZW1iZXIgNi83LCAxOTE3IGJ5IHRoZSBjdXJyZW50IEdyZWdvcmlhbgogIGNhbGVuZGFyOyBPY3RvYmVyIDI0LzI1IGJ5IHRoZSBKdWxpYW4gY2FsZW5kYXIgUnVzc2lhIHdhcyBzdGlsbAogIHVzaW5nLikKCkZvciBkYXRlLXRpbWVzCgoqIEFsbCBvZiB0aGUgYWJvdmUuCgoqIERheWxpZ2h0IHNhdmluZyB0aW1lLgoKKiBUaW1lIHpvbmVzLgoKPCEtLQpMb2NhbGUgc3R1ZmYgbWF5IGZhaWwgb24gc29tZSBzeXN0ZW1zIChvbiBVYnVudHUgbWF5IG5lZWQgdG8gdXNlCmxvY2FsZS1nZW4KLS0+CjwhLS0KbGlicmFyeShsdWJyaWRhdGUpCmZsaWdodHMgPC0gbXV0YXRlKGZsaWdodHMsIHRoID0gbWFrZV9kYXRldGltZSh5ZWFyLCBtb250aCwgZGF5LCBob3VyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHogPSAiQW1lcmljYS9OZXdfWW9yayIpKQp3ZWF0aGVyIDwtIG11dGF0ZSh3ZWF0aGVyLCB0aCA9IG1ha2VfZGF0ZXRpbWUoeWVhciwgbW9udGgsIGRheSwgaG91ciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR6ID0gIlVUQyIpKQoKZmwwIDwtIHNlbGVjdChmaWx0ZXIoZmxpZ2h0cywgb3JpZ2luID09ICJMR0EiKSwKICAgICAgICAgICAgICBkZXBfdGltZSwgdGltZV9ob3VyLCB0aCkKdzAgPC0gc2VsZWN0KGZpbHRlcih3ZWF0aGVyLCBvcmlnaW4gPT0gIkxHQSIpWy0oMSA6IDkpLF0sCiAgICAgICAgICAgICB0aW1lX2hvdXIsIHRoLCB0ZW1wKQoKZmwxIDwtIGxlZnRfam9pbihmbDAsIHNlbGVjdCh3MCwgLXRoKSwgInRpbWVfaG91ciIpCmZsMiA8LSBsZWZ0X2pvaW4oZmwwLCBzZWxlY3QodzAsIC10aW1lX2hvdXIpLCAidGgiKQotLT4KCgojIyBSZWFkaW5nCgpDaGFwdGVyIFtfRGF0ZXMgYW5kIFRpbWVzX10oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei9kYXRlcy1hbmQtdGltZXMuaHRtbCkKaW4gW19SIGZvciBEYXRhIFNjaWVuY2VfXShodHRwczovL3I0ZHMuaGFkLmNvLm56LykuCgoKIyMgRXhlcmNpc2VzCgoxLiBVc2luZyB0aGUgTllDIGZsaWdodHMgZGF0YSwgaG93IG1hbnkgZmxpZ2h0cyB3ZXJlIHRoZXJlIG9uCiAgIFNhdHVyZGF5cyBmcm9tIE5ld2FyayAoRVdSKSB0byBDaWNhZ28gTydIYXJlIChPUkQpIGluIDIwMTM/CiAgIAogICAgYS4gNDEzCiAgICBiLiA1MjIKICAgIGMuIDYwMQogICAgZC4gNzMzCiAgIAoKMi4gV2hhdCBkYXkgb2YgdGhlIHdlZWsgd2lsbCBKdWx5IDQsIDIwMzAsIGZhbGwgb24/CgogICAgYS4gTW9uZGF5CiAgICBiLiBXZWRuZXNkYXkKICAgIGQuIFRodXJzZGF5CiAgICBjLiBTYXR1cmRheQo=