Overview

Date-time data can be frustrating to work with in R. R commands for date-times are generally unintuitive and change depending on the type of date-time object being used. Moreover, the methods we use with date-times must be robust to time zones, leap days, daylight savings times, and other time related quirks, and R lacks these capabilities in some situations. Lubridate makes it easier to do the things R does with date-times and possible to do the things R does not.

Resources

Lubridate tutorial

Let’s explore some of the functions within the lubridate package.

But first, installation!

# The easiest way to get lubridate is to install the whole tidyverse:
install.packages("tidyverse")

# Alternatively, install just lubridate:
install.packages("lubridate")

# Or the the development version from GitHub:
# install.packages("devtools")
devtools::install_github("tidyverse/lubridate")

Some of these functions can be done usng strptime and strftime in base R but we will be focusing on lubridate.

Load package

library(lubridate)

Quick note:

POSI….what? There are two internal implementations of date/time: POSIXct, which stores seconds since UNIX epoch (+some other data), and POSIXlt, which stores a list of day, month, year, hour, minute, second, etc.

Lubridate features

Reference points

today = today()
today
[1] "2018-09-12"
now = now()
now
[1] "2018-09-12 18:49:39 SAST"

Parsing dates and times

To create POSIXlt objects from strings, lubridate has a number of aptly named functions: - ymd(), ymd_hms, dmy(), dmy_hms, mdy(), …

# When is Women's day?
ymd("20180809")
[1] "2018-08-09"
mdy("08-09-2018")
[1] "2018-08-09"
dmy("09/08/2018")
[1] "2018-08-09"
# You try

# Parse as date
___("17 Sep 2018")

____("March 6, 1957")

# Parse as date and time (with no seconds!)
_____("July 15, 2012 12:56")

# Parse as date-time (with seconds)
______('2011-04-06 08:00:10')

# Parse all as dates
x <- c("2009-01-01", "2009-01-02", "2009-01-03")
___(x)

# Format to display date as "2018-01-01"
___(180101, 180102)

# Format to display "2010-02-01"
___(010210)

# Format to display "2010-01-02"
___(010210)

You see that these functions contain the formatting of the string in their name. In addition, they are robust in which separators are used (e.g. : or -). Note that you can set the tz input argument to set the timezone.

Extracting information from date-times

womensday<-ymd_hms("2018-08-09-12-30-30", tz="GMT")
womensday
[1] "2018-08-09 12:30:30 GMT"
# Extract only the date
date(womensday)
[1] "2018-08-09"
# Is it a leap year?
leap_year(womensday)
[1] FALSE

GET components of date-times

Getting useful information using functions such as year(), month(), mday(), hour(), minute() and second():

year(womensday)
[1] 2018
month(womensday)
[1] 8
week(womensday)
[1] 32
wday(womensday)
[1] 5
wday(womensday, label=TRUE)
[1] Thu
Levels: Sun < Mon < Tue < Wed < Thu < Fri < Sat
yday(womensday)
[1] 221
hour(womensday)
[1] 12
# You try
# Save your birthday as a date-time object (make up a time if you don't know!)
bday <- _______()

# What number week in the year were you born?
_____(bday)

# What number day in the year were you born
____(bday)

# Were you born in a leap year?
____(bday)

# What day of the week were you born? Print out the full, unabbreviated weekday
____(bday)

SET components of date-time

Lubridate can be used to not only extract but also change prts of date-time objects

month(womensday) <- 12
womensday
[1] "2018-12-09 12:30:30 GMT"
second(womensday) <-99
womensday
[1] "2018-12-09 12:31:39 GMT"
# Use update to change multiple values
youthday <- update(womensday, year = 1976, day = 16, month = 6, hour = 8)
# You try
# Change your birth date to your birthday this year

bday_2018 <- ____
bday_2018

Arithmetic with dates

Some important time spans

intervals

  • The time information between two points in time.
  • Intervals are specific time spans (because they are tied to specific dates).
  • Can be created by subtracting two instants, or using the new_interval function.
# Create an interval in time
my_int <- interval(womensday, now)
my_int
[1] 2018-12-09 12:31:39 GMT--2018-09-12 16:49:39 GMT
# Access the start of an interval
start <- int_start(my_int)
start
[1] "2018-12-09 12:31:39 GMT"
# Access the end point of an interval
end <- int_end(my_int)
end
[1] "2018-09-12 16:49:39 GMT"
# Flip an interval
tni_ym <- int_flip(my_int)
tni_ym
[1] 2018-09-12 16:49:39 GMT--2018-12-09 12:31:39 GMT

Lubridate also provides two helper functions for general time spans:

durations

  • Which measure the exact amount of time between two points.
  • Did you know seconds are the only time unit with a consistent length?! Durations are always measured in seconds.
  • Helper functions for creating durations are named after the units of time (plural) but begin with a “d” (for duration).
  • For example dhours, dseconds
# Create durations
d <- ddays(14)
d
[1] "1209600s (~2 weeks)"
w <- dweeks(104)
w
[1] "62899200s (~1.99 years)"
# Create duration from numeric
as.duration(100)
[1] "100s (~1.67 minutes)"
# Create duration from a time interval
my_int_d <- as.duration(my_int)
my_int_d
[1] "7587719.47534204s (~12.55 weeks)"

periods

  • Which accurately track clock times despite leap years, leap seconds, and day light savings time
  • Helper functions for creating periods are named after the units of time (plural)
  • For example, you use functions like years, months. Note that these are not the same as the function year and month.
# Create periods
p <- months(3) + days(12)
p
[1] "3m 12d 0H 0M 0S"
# Create a period from a time interval
my_int_p <- as.period(my_int)
my_int_p
[1] "-2m -26d -19H -41M -59.4753420352936S"

Why two classes?

minutes(2) # period
[1] "2M 0S"
## 2 minutes
dminutes(2) # duration
[1] "120s (~2 minutes)"
## 120s (~2 minutes)
my_int_d == my_int_p
[1] FALSE

Comparing the “timeline” and the “number line”

leap_year(2011)
[1] FALSE
## FALSE
ymd(20110101) + dyears(1)
[1] "2012-01-01"
## "2012-01-01 UTC"
ymd(20110101) + years(1)
[1] "2012-01-01"
## "2012-01-01 UTC"
 
leap_year(2012)
[1] TRUE
## TRUE
ymd(20120101) + dyears(1)
[1] "2012-12-31"
## "2012-12-31 UTC"
ymd(20120101) + years(1)
[1] "2013-01-01"
## "2013-01-01 UTC"
# Is my date within an interval?
a = ymd(20170101)
a
[1] "2017-01-01"
a %within% my_int
[1] FALSE
b = a + years(1) + months(7) + days(10)
b
[1] "2018-08-11"
b %within% my_int
[1] FALSE
# You try

# How many seconds in 6 months?


# Create a period of 2 seconds and 34 milliseconds


# Add four hours onto now and save as bedtime


# What interval in time has passed since your birthday and now?


# How old are you in seconds at this point in time?


# Create an interval of time from beginning of this year to now


# Has your birthday this year occured within this interval?


# How long until you turn 100 years old?!
LS0tCnRpdGxlOiAiUi1MYWRpZXMgQ2FwZSBUb3duOiBXaGF0J3MgaW4gYSBkYXRlPyIKYXV0aG9yOiAiTWVnYW4gQmVja2V0dCIKZGF0ZTogIjE2IEF1Z3VzdCAyMDE4IgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGZpZ19oZWlnaHQ6IDQuNQogICAgZmlnX3dpZHRoOiA3CiAgICB0b2M6IHllcwotLS0KIyBPdmVydmlldwpEYXRlLXRpbWUgZGF0YSBjYW4gYmUgZnJ1c3RyYXRpbmcgdG8gd29yayB3aXRoIGluIFIuIFIgY29tbWFuZHMgZm9yIGRhdGUtdGltZXMgYXJlIGdlbmVyYWxseSB1bmludHVpdGl2ZSBhbmQgY2hhbmdlIGRlcGVuZGluZyBvbiB0aGUgdHlwZSBvZiBkYXRlLXRpbWUgb2JqZWN0IGJlaW5nIHVzZWQuIE1vcmVvdmVyLCB0aGUgbWV0aG9kcyB3ZSB1c2Ugd2l0aCBkYXRlLXRpbWVzIG11c3QgYmUgcm9idXN0IHRvIHRpbWUgem9uZXMsIGxlYXAgZGF5cywgZGF5bGlnaHQgc2F2aW5ncyB0aW1lcywgYW5kIG90aGVyIHRpbWUgcmVsYXRlZCBxdWlya3MsIGFuZCBSIGxhY2tzIHRoZXNlIGNhcGFiaWxpdGllcyBpbiBzb21lIHNpdHVhdGlvbnMuIEx1YnJpZGF0ZSBtYWtlcyBpdCBlYXNpZXIgdG8gZG8gdGhlIHRoaW5ncyBSIGRvZXMgd2l0aCBkYXRlLXRpbWVzIGFuZCBwb3NzaWJsZSB0byBkbyB0aGUgdGhpbmdzIFIgZG9lcyBub3QuCgojIFJlc291cmNlcwotIE1ha2Ugc3VyZSB5b3UgY2hlY2sgb3V0IHRoZSB2ZXJ5IGhhbmR5IFItU3R1ZGlvIGNoZWF0c2hlZXQ6IGh0dHBzOi8vcmF3Z2l0LmNvbS9yc3R1ZGlvL2NoZWF0c2hlZXRzL21hc3Rlci9sdWJyaWRhdGUucGRmCi0gVGhlIGNoYXB0ZXIgaW4gUiBmb3IgRGF0YSBTY2llbmNlIG9uIGRhdGVzIGFuZCB0aW1lczogaHR0cDovL3I0ZHMuaGFkLmNvLm56L2RhdGVzLWFuZC10aW1lcy5odG1sCgojIEx1YnJpZGF0ZSB0dXRvcmlhbApMZXQncyBleHBsb3JlIHNvbWUgb2YgdGhlIGZ1bmN0aW9ucyB3aXRoaW4gdGhlIGx1YnJpZGF0ZSBwYWNrYWdlLgoKIyMgQnV0IGZpcnN0LCBpbnN0YWxsYXRpb24hCmBgYHtyIG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0KIyBUaGUgZWFzaWVzdCB3YXkgdG8gZ2V0IGx1YnJpZGF0ZSBpcyB0byBpbnN0YWxsIHRoZSB3aG9sZSB0aWR5dmVyc2U6Cmluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpCgojIEFsdGVybmF0aXZlbHksIGluc3RhbGwganVzdCBsdWJyaWRhdGU6Cmluc3RhbGwucGFja2FnZXMoImx1YnJpZGF0ZSIpCgojIE9yIHRoZSB0aGUgZGV2ZWxvcG1lbnQgdmVyc2lvbiBmcm9tIEdpdEh1YjoKIyBpbnN0YWxsLnBhY2thZ2VzKCJkZXZ0b29scyIpCmRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigidGlkeXZlcnNlL2x1YnJpZGF0ZSIpCmBgYAoKU29tZSBvZiB0aGVzZSBmdW5jdGlvbnMgY2FuIGJlIGRvbmUgdXNuZyBzdHJwdGltZSBhbmQgc3RyZnRpbWUgaW4gYmFzZSBSIGJ1dCB3ZSB3aWxsIGJlIGZvY3VzaW5nIG9uIGx1YnJpZGF0ZS4KCiMjIExvYWQgcGFja2FnZQpgYGB7ciBtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkobHVicmlkYXRlKQoKYGBgCiMjIyBRdWljayBub3RlOgpQT1NJLi4uLndoYXQ/ClRoZXJlIGFyZSB0d28gaW50ZXJuYWwgaW1wbGVtZW50YXRpb25zIG9mIGRhdGUvdGltZTogUE9TSVhjdCwgd2hpY2ggc3RvcmVzIHNlY29uZHMgc2luY2UgVU5JWCBlcG9jaCAoK3NvbWUgb3RoZXIgZGF0YSksIGFuZCBQT1NJWGx0LCB3aGljaCBzdG9yZXMgYSBsaXN0IG9mIGRheSwgbW9udGgsIHllYXIsIGhvdXIsIG1pbnV0ZSwgc2Vjb25kLCBldGMuCgojIyBMdWJyaWRhdGUgZmVhdHVyZXMKIyMjIFJlZmVyZW5jZSBwb2ludHMKYGBge3J9CnRvZGF5ID0gdG9kYXkoKQp0b2RheQpub3cgPSBub3coKQpub3cKYGBgCgojIyMgUGFyc2luZyBkYXRlcyBhbmQgdGltZXMKVG8gY3JlYXRlIFBPU0lYbHQgb2JqZWN0cyBmcm9tIHN0cmluZ3MsIGx1YnJpZGF0ZSBoYXMgYSBudW1iZXIgb2YgYXB0bHkgbmFtZWQgZnVuY3Rpb25zOgotIHltZCgpLCB5bWRfaG1zLCBkbXkoKSwgZG15X2htcywgbWR5KCksIOKApgoKYGBge3J9CiMgV2hlbiBpcyBXb21lbidzIGRheT8KCnltZCgiMjAxODA4MDkiKQptZHkoIjA4LTA5LTIwMTgiKQpkbXkoIjA5LzA4LzIwMTgiKQpgYGAKCmBgYHtyIG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0KIyBZb3UgdHJ5CgojIFBhcnNlIGFzIGRhdGUKX19fKCIxNyBTZXAgMjAxOCIpCgpfX19fKCJNYXJjaCA2LCAxOTU3IikKCiMgUGFyc2UgYXMgZGF0ZSBhbmQgdGltZSAod2l0aCBubyBzZWNvbmRzISkKX19fX18oIkp1bHkgMTUsIDIwMTIgMTI6NTYiKQoKIyBQYXJzZSBhcyBkYXRlLXRpbWUgKHdpdGggc2Vjb25kcykKX19fX19fKCcyMDExLTA0LTA2IDA4OjAwOjEwJykKCiMgUGFyc2UgYWxsIGFzIGRhdGVzCnggPC0gYygiMjAwOS0wMS0wMSIsICIyMDA5LTAxLTAyIiwgIjIwMDktMDEtMDMiKQpfX18oeCkKCiMgRm9ybWF0IHRvIGRpc3BsYXkgZGF0ZSBhcyAiMjAxOC0wMS0wMSIKX19fKDE4MDEwMSwgMTgwMTAyKQoKIyBGb3JtYXQgdG8gZGlzcGxheSAiMjAxMC0wMi0wMSIKX19fKDAxMDIxMCkKCiMgRm9ybWF0IHRvIGRpc3BsYXkgIjIwMTAtMDEtMDIiCl9fXygwMTAyMTApCgpgYGAKWW91IHNlZSB0aGF0IHRoZXNlIGZ1bmN0aW9ucyBjb250YWluIHRoZSBmb3JtYXR0aW5nIG9mIHRoZSBzdHJpbmcgaW4gdGhlaXIgbmFtZS4gSW4gYWRkaXRpb24sIHRoZXkgYXJlIHJvYnVzdCBpbiB3aGljaCBzZXBhcmF0b3JzIGFyZSB1c2VkIChlLmcuIDogb3IgIC0pLiBOb3RlIHRoYXQgeW91IGNhbiBzZXQgdGhlIHR6IGlucHV0IGFyZ3VtZW50IHRvIHNldCB0aGUgdGltZXpvbmUuCgojIyMgRXh0cmFjdGluZyBpbmZvcm1hdGlvbiBmcm9tIGRhdGUtdGltZXMKCmBgYHtyfQp3b21lbnNkYXk8LXltZF9obXMoIjIwMTgtMDgtMDktMTItMzAtMzAiLCB0ej0iR01UIikKd29tZW5zZGF5CgojIEV4dHJhY3Qgb25seSB0aGUgZGF0ZQpkYXRlKHdvbWVuc2RheSkKCiMgSXMgaXQgYSBsZWFwIHllYXI/CmxlYXBfeWVhcih3b21lbnNkYXkpCmBgYAoKIyMjIEdFVCBjb21wb25lbnRzIG9mIGRhdGUtdGltZXMKR2V0dGluZyB1c2VmdWwgaW5mb3JtYXRpb24gdXNpbmcgZnVuY3Rpb25zIHN1Y2ggYXMgeWVhcigpLCBtb250aCgpLCBtZGF5KCksIGhvdXIoKSwgbWludXRlKCkgYW5kIHNlY29uZCgpOgoKYGBge3J9CnllYXIod29tZW5zZGF5KQptb250aCh3b21lbnNkYXkpCndlZWsod29tZW5zZGF5KQp3ZGF5KHdvbWVuc2RheSkKd2RheSh3b21lbnNkYXksIGxhYmVsPVRSVUUpCnlkYXkod29tZW5zZGF5KQpob3VyKHdvbWVuc2RheSkKCmBgYApgYGB7ciBtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9CiMgWW91IHRyeQojIFNhdmUgeW91ciBiaXJ0aGRheSBhcyBhIGRhdGUtdGltZSBvYmplY3QgKG1ha2UgdXAgYSB0aW1lIGlmIHlvdSBkb24ndCBrbm93ISkKYmRheSA8LSBfX19fX19fKCkKCiMgV2hhdCBudW1iZXIgd2VlayBpbiB0aGUgeWVhciB3ZXJlIHlvdSBib3JuPwpfX19fXyhiZGF5KQoKIyBXaGF0IG51bWJlciBkYXkgaW4gdGhlIHllYXIgd2VyZSB5b3UgYm9ybgpfX19fKGJkYXkpCgojIFdlcmUgeW91IGJvcm4gaW4gYSBsZWFwIHllYXI/Cl9fX18oYmRheSkKCiMgV2hhdCBkYXkgb2YgdGhlIHdlZWsgd2VyZSB5b3UgYm9ybj8gUHJpbnQgb3V0IHRoZSBmdWxsLCB1bmFiYnJldmlhdGVkIHdlZWtkYXkKX19fXyhiZGF5KQoKCmBgYAoKIyMjIFNFVCBjb21wb25lbnRzIG9mIGRhdGUtdGltZQpMdWJyaWRhdGUgY2FuIGJlIHVzZWQgdG8gbm90IG9ubHkgZXh0cmFjdCBidXQgYWxzbyBjaGFuZ2UgcHJ0cyBvZiBkYXRlLXRpbWUgb2JqZWN0cwoKYGBge3J9Cm1vbnRoKHdvbWVuc2RheSkgPC0gMTIKd29tZW5zZGF5CgpzZWNvbmQod29tZW5zZGF5KSA8LTk5CndvbWVuc2RheQoKIyBVc2UgdXBkYXRlIHRvIGNoYW5nZSBtdWx0aXBsZSB2YWx1ZXMKeW91dGhkYXkgPC0gdXBkYXRlKHdvbWVuc2RheSwgeWVhciA9IDE5NzYsIGRheSA9IDE2LCBtb250aCA9IDYsIGhvdXIgPSA4KQoKYGBgCgpgYGB7cn0KIyBZb3UgdHJ5CiMgQ2hhbmdlIHlvdXIgYmlydGggZGF0ZSB0byB5b3VyIGJpcnRoZGF5IHRoaXMgeWVhcgoKYmRheV8yMDE4IDwtIF9fX18KYmRheV8yMDE4CgpgYGAKCiMjIyBBcml0aG1ldGljIHdpdGggZGF0ZXMKIyMjIyBTb21lIGltcG9ydGFudCB0aW1lIHNwYW5zCgoqKmludGVydmFscyoqCgotIFRoZSB0aW1lIGluZm9ybWF0aW9uIGJldHdlZW4gdHdvIHBvaW50cyBpbiB0aW1lLiAKLSBJbnRlcnZhbHMgYXJlIHNwZWNpZmljIHRpbWUgc3BhbnMgKGJlY2F1c2UgdGhleSBhcmUgdGllZCB0byBzcGVjaWZpYyBkYXRlcykuCi0gQ2FuIGJlIGNyZWF0ZWQgYnkgc3VidHJhY3RpbmcgdHdvIGluc3RhbnRzLCBvciB1c2luZyB0aGUgYG5ld19pbnRlcnZhbGAgZnVuY3Rpb24uCgpgYGB7cn0KIyBDcmVhdGUgYW4gaW50ZXJ2YWwgaW4gdGltZQpteV9pbnQgPC0gaW50ZXJ2YWwod29tZW5zZGF5LCBub3cpCm15X2ludAoKIyBBY2Nlc3MgdGhlIHN0YXJ0IG9mIGFuIGludGVydmFsCnN0YXJ0IDwtIGludF9zdGFydChteV9pbnQpCnN0YXJ0CgojIEFjY2VzcyB0aGUgZW5kIHBvaW50IG9mIGFuIGludGVydmFsCmVuZCA8LSBpbnRfZW5kKG15X2ludCkKZW5kCgojIEZsaXAgYW4gaW50ZXJ2YWwKdG5pX3ltIDwtIGludF9mbGlwKG15X2ludCkKdG5pX3ltCmBgYAoKCkx1YnJpZGF0ZSBhbHNvIHByb3ZpZGVzIHR3byBoZWxwZXIgZnVuY3Rpb25zIGZvciAqZ2VuZXJhbCogdGltZSBzcGFuczoKCioqZHVyYXRpb25zKiogCgotIFdoaWNoIG1lYXN1cmUgdGhlIGV4YWN0IGFtb3VudCBvZiB0aW1lIGJldHdlZW4gdHdvIHBvaW50cy4KLSBEaWQgeW91IGtub3cgc2Vjb25kcyBhcmUgdGhlIG9ubHkgdGltZSB1bml0IHdpdGggYSBjb25zaXN0ZW50IGxlbmd0aD8hIER1cmF0aW9ucyBhcmUgYWx3YXlzIG1lYXN1cmVkIGluIHNlY29uZHMuCi0gSGVscGVyIGZ1bmN0aW9ucyBmb3IgY3JlYXRpbmcgZHVyYXRpb25zIGFyZSBuYW1lZCBhZnRlciB0aGUgdW5pdHMgb2YgdGltZSAocGx1cmFsKSBidXQgYmVnaW4gd2l0aCBhIOKAnGTigJ0gKGZvciBkdXJhdGlvbikuCi0gRm9yIGV4YW1wbGUgYGRob3Vyc2AsIGBkc2Vjb25kc2AKCmBgYHtyfQojIENyZWF0ZSBkdXJhdGlvbnMKZCA8LSBkZGF5cygxNCkKZAp3IDwtIGR3ZWVrcygxMDQpCncKCiMgQ3JlYXRlIGR1cmF0aW9uIGZyb20gbnVtZXJpYwphcy5kdXJhdGlvbigxMDApCgojIENyZWF0ZSBkdXJhdGlvbiBmcm9tIGEgdGltZSBpbnRlcnZhbApteV9pbnRfZCA8LSBhcy5kdXJhdGlvbihteV9pbnQpCm15X2ludF9kCgpgYGAKCioqcGVyaW9kcyoqCgotIFdoaWNoIGFjY3VyYXRlbHkgdHJhY2sgY2xvY2sgdGltZXMgZGVzcGl0ZSBsZWFwIHllYXJzLCBsZWFwIHNlY29uZHMsIGFuZCBkYXkgbGlnaHQgc2F2aW5ncyB0aW1lCi0gSGVscGVyIGZ1bmN0aW9ucyBmb3IgY3JlYXRpbmcgcGVyaW9kcyBhcmUgbmFtZWQgYWZ0ZXIgdGhlIHVuaXRzIG9mIHRpbWUgKHBsdXJhbCkKLSBGb3IgZXhhbXBsZSwgeW91IHVzZSBmdW5jdGlvbnMgbGlrZSBgeWVhcnNgLCBgbW9udGhzYC4gTm90ZSB0aGF0IHRoZXNlIGFyZSBub3QgdGhlIHNhbWUgYXMgdGhlIGZ1bmN0aW9uIGB5ZWFyYCBhbmQgYG1vbnRoYC4KCmBgYHtyfQojIENyZWF0ZSBwZXJpb2RzCnAgPC0gbW9udGhzKDMpICsgZGF5cygxMikKcAoKIyBDcmVhdGUgYSBwZXJpb2QgZnJvbSBhIHRpbWUgaW50ZXJ2YWwKbXlfaW50X3AgPC0gYXMucGVyaW9kKG15X2ludCkKbXlfaW50X3AKCmBgYAoKKipXaHkgdHdvIGNsYXNzZXM/KioKCmBgYHtyfQptaW51dGVzKDIpICMgcGVyaW9kCiMjIDIgbWludXRlcwpkbWludXRlcygyKSAjIGR1cmF0aW9uCiMjIDEyMHMgKH4yIG1pbnV0ZXMpCgpteV9pbnRfZCA9PSBteV9pbnRfcAoKYGBgCgoqQ29tcGFyaW5nIHRoZSAidGltZWxpbmUiIGFuZCB0aGUgIm51bWJlciBsaW5lIioKCi0gVGhlIGR1cmF0aW9ucyBjbGFzcyB3aWxsIGFsd2F5cyBzdXBwbHkgbWF0aGVtYXRpY2FsbHkgcHJlY2lzZSByZXN1bHRzLiAKLSBBIGR1cmF0aW9uIHllYXIgd2lsbCBhbHdheXMgZXF1YWwgMzY1IGRheXMuIAotIFBlcmlvZHMgZmx1Y3R1YXRlIHRoZSBzYW1lIHdheSB0aGUgdGltZWxpbmUgZG9lcyB0byBnaXZlIGludHVpdGl2ZSByZXN1bHRzLiBUaGlzIG1ha2VzIHRoZW0gdXNlZnVsIGZvciBtb2RlbGxpbmcgY2xvY2sgdGltZXMuCi0gKGZyb20gaHR0cHM6Ly93d3cuci1zdGF0aXN0aWNzLmNvbS8yMDEyLzAzL2RvLW1vcmUtd2l0aC1kYXRlcy1hbmQtdGltZXMtaW4tci13aXRoLWx1YnJpZGF0ZS0xLTEtMC8pCgoKYGBge3J9CmxlYXBfeWVhcigyMDExKQojIyBGQUxTRQp5bWQoMjAxMTAxMDEpICsgZHllYXJzKDEpCiMjICIyMDEyLTAxLTAxIFVUQyIKeW1kKDIwMTEwMTAxKSArIHllYXJzKDEpCiMjICIyMDEyLTAxLTAxIFVUQyIKIApsZWFwX3llYXIoMjAxMikKIyMgVFJVRQp5bWQoMjAxMjAxMDEpICsgZHllYXJzKDEpCiMjICIyMDEyLTEyLTMxIFVUQyIKeW1kKDIwMTIwMTAxKSArIHllYXJzKDEpCiMjICIyMDEzLTAxLTAxIFVUQyIKYGBgCgpgYGB7cn0KIyBJcyBteSBkYXRlIHdpdGhpbiBhbiBpbnRlcnZhbD8KCmEgPSB5bWQoMjAxNzAxMDEpCmEKCmEgJXdpdGhpbiUgbXlfaW50CgpiID0gYSArIHllYXJzKDEpICsgbW9udGhzKDcpICsgZGF5cygxMCkKYgoKYiAld2l0aGluJSBteV9pbnQKYGBgCgpgYGB7cn0KIyBZb3UgdHJ5CgojIEhvdyBtYW55IHNlY29uZHMgaW4gNiBtb250aHM/CgoKIyBDcmVhdGUgYSBwZXJpb2Qgb2YgMiBzZWNvbmRzIGFuZCAzNCBtaWxsaXNlY29uZHMKCgojIEFkZCBmb3VyIGhvdXJzIG9udG8gbm93IGFuZCBzYXZlIGFzIGJlZHRpbWUKCgojIFdoYXQgaW50ZXJ2YWwgaW4gdGltZSBoYXMgcGFzc2VkIHNpbmNlIHlvdXIgYmlydGhkYXkgYW5kIG5vdz8KCgojIEhvdyBvbGQgYXJlIHlvdSBpbiBzZWNvbmRzIGF0IHRoaXMgcG9pbnQgaW4gdGltZT8KCgojIENyZWF0ZSBhbiBpbnRlcnZhbCBvZiB0aW1lIGZyb20gYmVnaW5uaW5nIG9mIHRoaXMgeWVhciB0byBub3cKCgojIEhhcyB5b3VyIGJpcnRoZGF5IHRoaXMgeWVhciBvY2N1cmVkIHdpdGhpbiB0aGlzIGludGVydmFsPwoKCiMgSG93IGxvbmcgdW50aWwgeW91IHR1cm4gMTAwIHllYXJzIG9sZD8hCgoKCmBgYAoKCg==