Introduction
Dates are a separate data type in R and SQL. A date object can be used in “date aware” calculations, for example in BETWEEN clauses in SQL. Dates have a different format in R and SQL. A text column or character variable, even if they are formatted like a date, are not actual dates.
Converting Text to Date Objects
To convert text to a date object in R, the text string must be in the format YYYY-MM-DD or YYYY/MM/DD.
s2 <- "2021-02-15"
d1 <- as.Date(s2)
print(d1)
## [1] "2021-02-15"
Note that the class (type) of the above variable d1 is of type Date.
Alternatively, the date can be specified as YYYY/MM/DD as demonstrated below.
s3 <- "2019/06/30"
d2 <- as.Date(s3)
print(d2)
## [1] "2019-06-30"
Current System Time and Date
To get the current date from the system’s clock, use Sys.Date()
. To get the current date and time, use date()
.
# get current date and time
date()
## [1] "Wed Feb 14 09:48:52 2024"
# get current date only as YYYY-MM-DD
Sys.Date()
## [1] "2024-02-14"
lubridate Package
The lubridate package has many more functions for converting from various other formats, but these are addressed in a different tutorial (See Also).
Dates in SQLite
Let’s create a simple in-memory database with one table (txns), three columns, where the first column in an integer primary key, the second is a date, and the third is text.
library(RSQLite)
db <- dbConnect(RSQLite::SQLite(), ":memory:")
create table txns (
tid integer primary key,
tdate date not null,
tprod text
)
Now that the table is created, let’s insert some values to ensure that the table definition is correct and we can retrieve the data with their correct data type.
insert into txns values
(100, '2022-03-31', 'Alvarex'),
(200, '2022-03-30', 'Prosilvas'),
(300, '2022-03-30', 'Alvarex'),
(400, '2022-02-09', 'Kitomaron'),
(500, '2022-01-14', 'Blevarex');
Let’s quickly retrieve all rows to ensure they are in the database.
Table 1: 5 records
100 |
2022-03-31 |
Alvarex |
200 |
2022-03-30 |
Prosilvas |
300 |
2022-03-30 |
Alvarex |
400 |
2022-02-09 |
Kitomaron |
500 |
2022-01-14 |
Blevarex |
Indeed, they are. But note that the date column tdate is returned as text (<chr>). To determine if SQLite actually stored them as type date, we can run a SQL query that requires date column type.
select *
from txns
where tdate between '2022-03-01' and '2022-03-31'
Table 2: 3 records
100 |
2022-03-31 |
Alvarex |
200 |
2022-03-30 |
Prosilvas |
300 |
2022-03-30 |
Alvarex |
The above query works, so we can be confident that SQLite stored the dates correctly even though R convert them to text.
And, finally, let’s retrieve some data into a data frame and list the data along with the types of the columns in the data frame. Recall that all result sets of queries are tables.
rs <- dbGetQuery(db, 'select * from txns')
print(rs)
## tid tdate tprod
## 1 100 2022-03-31 Alvarex
## 2 200 2022-03-30 Prosilvas
## 3 300 2022-03-30 Alvarex
## 4 400 2022-02-09 Kitomaron
## 5 500 2022-01-14 Blevarex
Note that the date column came back from the query as text; the data frame has the data type <chr> for the tdate column. To use them as dates, you need to coerce the column type of the data frame to Date.
rs$tdate <- as.Date(rs$tdate)
print(rs)
## tid tdate tprod
## 1 100 2022-03-31 Alvarex
## 2 200 2022-03-30 Prosilvas
## 3 300 2022-03-30 Alvarex
## 4 400 2022-02-09 Kitomaron
## 5 500 2022-01-14 Blevarex
Notice that now the tdate column is of the correct R Date type and we can apply R date functions to that column.
Saving Date Data to SQLite
In the above discussion, we saw that SQL dates are converted to text when retrieved into a data frame and an explicit coercion is required if we want to treat dates as actual R Date objects. A similar issue occurs when we save Date columns in data frames to R. We will demonstrate the issues by creating a data frame which we will then save into a new table in SQLite using dbWriteTable()
and then we will save the same data frame to an already created table, again using dbWriteTable()
. The difference is that in one case, the table does not yet exist, while in the other it does.
We will use the data frame rs that we retrieved above. To review, we had to coerce the data column explicitly to a Date object. As you can see, the data frame has three columns of types int (integer), date (Date) and chr (text characters), respectively.
## tid tdate tprod
## 1 100 2022-03-31 Alvarex
## 2 200 2022-03-30 Prosilvas
## 3 300 2022-03-30 Alvarex
## 4 400 2022-02-09 Kitomaron
## 5 500 2022-01-14 Blevarex
Let’s save this entire data frame to a new table in the in-memory SQLite database and then inspect the data types of the columns of the table in the database.
dbWriteTable(db, 'newTxns', rs)
Now, if we retrieve we know from the experiments above that the date column will be text, but we want to know what the database stores them as. The code below uses a PRAGMA to query the database itself.
PRAGMA table_info(newTxns);
Table 3: 3 records
0 |
tid |
INTEGER |
0 |
NA |
0 |
1 |
tdate |
REAL |
0 |
NA |
0 |
2 |
tprod |
TEXT |
0 |
NA |
0 |
Let’s compare that to the structure of a table that we create manually.
create table newManTxns (
tid integer primary key,
tdate date not null,
tprod text
)
PRAGMA table_info(newManTxns);
Table 4: 3 records
0 |
tid |
INTEGER |
0 |
NA |
1 |
1 |
tdate |
date |
1 |
NA |
0 |
2 |
tprod |
TEXT |
0 |
NA |
0 |
Notice how the table created by dbWriteTable()
has REAL as the data type for the tdate column while the manually created table has the correct SQL date data type.
So, we know that saving a data frame to SQLite will change the date column data type from Date to REAL – not very friendly and useful. Let’s see what happens when we write the data frame to an existing table. As a reminder, the columns of the data frame must be in the same order as in the table definition. Because, the table already exists, we need to specify append=T; be careful not to specify overwrite=T as that will wipe out the table and re-create it.
dbWriteTable(db, 'newManTxns', rs, append=T)
And, again, let’s inspect the table.
PRAGMA table_info(newManTxns);
Table 5: 3 records
0 |
tid |
INTEGER |
0 |
NA |
1 |
1 |
tdate |
date |
1 |
NA |
0 |
2 |
tprod |
TEXT |
0 |
NA |
0 |
Now, it has the correct data type. Of course, let’s check the data itself, too:
select * from newManTxns;
Table 6: 5 records
100 |
19082 |
Alvarex |
200 |
19081 |
Prosilvas |
300 |
19081 |
Alvarex |
400 |
19032 |
Kitomaron |
500 |
19006 |
Blevarex |
The tade column has an int data type, but let’s be sure that is correct in the database. We’ll run the same query as we did above that requires the data to be of a date type.
select *
from newManTxns
where tdate between '2022-03-01' and '2022-03-31'
And, as you can see, that did not work – which implies that R or SQLite modified the data and that a Date object is not correctly written to a SQLite database to a date column.
The final experiment uses an R data frame that has the date as text and writes it to a table in the database that has a date column.
We need to first remove all rows from the newManTxns table so we can work with an empty table.
rs2 <- dbGetQuery(db, 'select * from txns')
print(rs2)
## tid tdate tprod
## 1 100 2022-03-31 Alvarex
## 2 200 2022-03-30 Prosilvas
## 3 300 2022-03-30 Alvarex
## 4 400 2022-02-09 Kitomaron
## 5 500 2022-01-14 Blevarex
So, the data frame rs2 has dates from the database as characters.
dbWriteTable(db, 'newManTxns', rs2, append=T)
Let’s look at the data in the database first:
select * from newManTxns;
Table 8: 5 records
100 |
2022-03-31 |
Alvarex |
200 |
2022-03-30 |
Prosilvas |
300 |
2022-03-30 |
Alvarex |
400 |
2022-02-09 |
Kitomaron |
500 |
2022-01-14 |
Blevarex |
And, finally, let’s run the date query again:
select *
from newManTxns
where tdate between '2022-03-01' and '2022-03-31'
Table 9: 3 records
100 |
2022-03-31 |
Alvarex |
200 |
2022-03-30 |
Prosilvas |
300 |
2022-03-30 |
Alvarex |
Now it worked. So what did we learn? Date columns in data frames do not correctly map to date columns in the database. In order for dbWriteTable()
to properly write dates to date columns in the database, the column for the dates must be characters in R; they cannot be Date objects. Counterinituitive, but that’s the way it works.
Understanding R to SQL Data Mappings
To understand how R maps objects to SQL, we can use the dbDataType()
function.
## [1] "REAL"
This tells us R will convert the column to REAL when written to the database, which is what we saw above.
Conclusion
Dates must be properly encoded for them to be processed correctly. R and SQL use dates differently and converting between the two formats require care. Date columns in data frames do not get converted properly to columns of type date in SQLite.
Tutorial
The video tutorial demonstrates the constructs introduced in this lesson.
Errata
None collected yet. Let us know.
LS0tDQp0aXRsZTogIkRhdGVzIGluIFIgYW5kIFNRTGl0ZSINCnBhcmFtczoNCiAgY2F0ZWdvcnk6IDYNCiAgbnVtYmVyOiAzMDYNCiAgdGltZTogNjANCiAgbGV2ZWw6IGJlZ2lubmVyDQogIHRhZ3M6ICJyLGRhdGVzLHNxbCxzcWxpdGUsbHVicmlkYXRlIg0KICBkZXNjcmlwdGlvbjogIlJlYWRpbmcgZGF0ZXMgZnJvbSByYXcgZGF0YSBhbmQgdXNpbmcgdGhlbSBpbiBSIGFuZCBzYXZpbmcgdGhlbQ0KICAgICAgICAgICAgICAgIG9yIGxvYWRpbmcgdGhlbSBmcm9tIFNRTGl0ZSBjYW4gYmUgY2hhbGxlbmdpbmcuIFRoaXMgdHV0b3JpYWwNCiAgICAgICAgICAgICAgICBleHBsYWlucyBob3cgdG8gd29yayB3aXRoIGRhdGVzIGluIFIsIFNRTCwgYW5kIFNRTGl0ZS4iDQpkYXRlOiAiPHNtYWxsPmByIFN5cy5EYXRlKClgPC9zbWFsbD4iDQphdXRob3I6ICI8c21hbGw+TWFydGluIFNjaGVkbGJhdWVyPC9zbWFsbD4iDQplbWFpbDogIm0uc2NoZWRsYmF1ZXJAbmV1LmVkdSINCmFmZmlsaXRhdGlvbjogIk5vcnRoZWFzdGVybiBVbml2ZXJzaXR5Ig0Kb3V0cHV0OiANCiAgYm9va2Rvd246Omh0bWxfZG9jdW1lbnQyOg0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KICAgIGNvbGxhcHNlZDogZmFsc2UNCiAgICBudW1iZXJfc2VjdGlvbnM6IGZhbHNlDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIHRoZW1lOiBzcGFjZWxhYg0KICAgIGhpZ2hsaWdodDogdGFuZ28NCi0tLQ0KDQotLS0NCnRpdGxlOiAiPHNtYWxsPmByIHBhcmFtcyRjYXRlZ29yeWAuYHIgcGFyYW1zJG51bWJlcmA8L3NtYWxsPjxici8+PHNwYW4gc3R5bGU9J2NvbG9yOiAjMkU0MDUzOyBmb250LXNpemU6IDAuOWVtJz5gciBybWFya2Rvd246Om1ldGFkYXRhJHRpdGxlYDwvc3Bhbj4iDQotLS0NCg0KYGBge3IgY29kZT14ZnVuOjpyZWFkX3V0ZjgocGFzdGUwKGhlcmU6OmhlcmUoKSwnL1IvX2luc2VydDJEQi5SJykpLCBpbmNsdWRlID0gRkFMU0V9DQpgYGANCg0KIyMgSW50cm9kdWN0aW9uDQoNCkRhdGVzIGFyZSBhIHNlcGFyYXRlIGRhdGEgdHlwZSBpbiBSIGFuZCBTUUwuIEEgZGF0ZSBvYmplY3QgY2FuIGJlIHVzZWQgaW4gImRhdGUgYXdhcmUiIGNhbGN1bGF0aW9ucywgZm9yIGV4YW1wbGUgaW4gKkJFVFdFRU4qIGNsYXVzZXMgaW4gU1FMLiBEYXRlcyBoYXZlIGEgZGlmZmVyZW50IGZvcm1hdCBpbiBSIGFuZCBTUUwuIEEgdGV4dCBjb2x1bW4gb3IgY2hhcmFjdGVyIHZhcmlhYmxlLCBldmVuIGlmIHRoZXkgYXJlIGZvcm1hdHRlZCBsaWtlIGEgZGF0ZSwgYXJlIG5vdCBhY3R1YWwgZGF0ZXMuDQoNCiMjIENvbnZlcnRpbmcgVGV4dCB0byBEYXRlIE9iamVjdHMNCg0KVG8gY29udmVydCB0ZXh0IHRvIGEgZGF0ZSBvYmplY3QgaW4gUiwgdGhlIHRleHQgc3RyaW5nIG11c3QgYmUgaW4gdGhlIGZvcm1hdCAqWVlZWS1NTS1ERCogb3IgKllZWVkvTU0vREQqLg0KDQpgYGB7cn0NCnMyIDwtICIyMDIxLTAyLTE1Ig0KZDEgPC0gYXMuRGF0ZShzMikNCg0KcHJpbnQoZDEpDQpgYGANCg0KTm90ZSB0aGF0IHRoZSAqY2xhc3MqICh0eXBlKSBvZiB0aGUgYWJvdmUgdmFyaWFibGUgKmQxKiBpcyBvZiB0eXBlIGByIGNsYXNzKGQxKWAuDQoNCkFsdGVybmF0aXZlbHksIHRoZSBkYXRlIGNhbiBiZSBzcGVjaWZpZWQgYXMgKllZWVkvTU0vREQqIGFzIGRlbW9uc3RyYXRlZCBiZWxvdy4NCg0KYGBge3J9DQpzMyA8LSAiMjAxOS8wNi8zMCINCmQyIDwtIGFzLkRhdGUoczMpDQoNCnByaW50KGQyKQ0KYGBgDQoNCiMjIyBEYXRlIEZvcm1hdCBTcGVjaWZpY2F0aW9uDQoNClRvIGNvbnZlcnQgdGV4dCB0byBhIGRhdGUgb2JqZWN0IGluIFIsIHRoZSB0ZXh0IHN0cmluZyBtdXN0IGJlIGluIHRoZSBmb3JtYXQgKllZWVktTU0tREQqIHVubGVzcyB0aGUgKmZvcm1hdCogcGFyYW1ldGVyIGlzIHNwZWNpZmllZC4NCg0KYGBge3J9DQphcy5EYXRlKCIwNy8wNC8yMCIsIGZvcm1hdCA9ICIlbS8lZC8leSIpDQphcy5EYXRlKCJKVUwgMDQgMjAyMSIsIGZvcm1hdCA9ICIlYiAlZCAlWSIpDQphcy5EYXRlKCJGZWJydWFyeSAxMSA4MCIsIGZvcm1hdCA9ICIlQiAlZCAleSIpDQpgYGANCg0KSGVyZSBhcmUgdGhlIHZhcmlvdXMgZGF0ZSAqZm9ybWF0KiBzcGVjaWZpY2F0aW9uczoNCg0KfCBTeW1ib2wgfCBNZWFuaW5nICAgICAgICAgICAgICAgICB8IEV4YW1wbGUgfA0KfC0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tfA0KfCAlZCAgICAgfCBkYXkgYXMgYSBudW1iZXIgKDAxLTMxKSB8IDA2ICAgICAgfA0KfCAlYSAgICAgfCBhYmJyZXZpYXRlZCB3ZWVrZGF5ICAgICB8IE1vbiAgICAgfA0KfCAlQSAgICAgfCBmdWxsIHdlZWtkYXkgICAgICAgICAgICB8IE1vbmRheSAgfA0KfCAlbSAgICAgfCBtb250aCAoMDAtMTIpICAgICAgICAgICB8IDExICAgICAgfA0KfCAlYiAgICAgfCBhYmJyZXZpYXRlZCBtb250aCAgICAgICB8IE5vdiAgICAgfA0KfCAlQiAgICAgfCBmdWxsIG1vbnRoICAgICAgICAgICAgICB8IE1hcmNoICAgfA0KfCAleSAgICAgfCB0d28tZGlnaXQgeWVhciAgICAgICAgICB8IDIwICAgICAgfA0KfCAlWSAgICAgfCBmb3VyLWRpZ2l0IHllYXIgICAgICAgICB8IDIwMjEgICAgfA0KDQojIyBDdXJyZW50IFN5c3RlbSBUaW1lIGFuZCBEYXRlDQoNClRvIGdldCB0aGUgY3VycmVudCBkYXRlIGZyb20gdGhlIHN5c3RlbSdzIGNsb2NrLCB1c2UgPGNvZGU+U3lzLkRhdGUoKTwvY29kZT4uIFRvIGdldCB0aGUgY3VycmVudCBkYXRlIGFuZCB0aW1lLCB1c2UgPGNvZGU+ZGF0ZSgpPC9jb2RlPi4NCg0KYGBge3J9DQojIGdldCBjdXJyZW50IGRhdGUgYW5kIHRpbWUNCmRhdGUoKQ0KDQojIGdldCBjdXJyZW50IGRhdGUgb25seSBhcyBZWVlZLU1NLUREDQpTeXMuRGF0ZSgpDQpgYGANCg0KIyMgKipsdWJyaWRhdGUqKiBQYWNrYWdlDQoNClRoZSAqKmx1YnJpZGF0ZSoqIHBhY2thZ2UgaGFzIG1hbnkgbW9yZSBmdW5jdGlvbnMgZm9yIGNvbnZlcnRpbmcgZnJvbSB2YXJpb3VzIG90aGVyIGZvcm1hdHMsIGJ1dCB0aGVzZSBhcmUgYWRkcmVzc2VkIGluIGEgZGlmZmVyZW50IHR1dG9yaWFsIChbU2VlIEFsc29dKCNTZWVfQWxzbykpLg0KDQojIyBEYXRlcyBpbiBTUUxpdGUNCg0KTGV0J3MgY3JlYXRlIGEgc2ltcGxlIGluLW1lbW9yeSBkYXRhYmFzZSB3aXRoIG9uZSB0YWJsZSAoKnR4bnMqKSwgdGhyZWUgY29sdW1ucywgd2hlcmUgdGhlIGZpcnN0IGNvbHVtbiBpbiBhbiBpbnRlZ2VyIHByaW1hcnkga2V5LCB0aGUgc2Vjb25kIGlzIGEgZGF0ZSwgYW5kIHRoZSB0aGlyZCBpcyB0ZXh0Lg0KDQpgYGB7cn0NCmxpYnJhcnkoUlNRTGl0ZSkNCmRiIDwtIGRiQ29ubmVjdChSU1FMaXRlOjpTUUxpdGUoKSwgIjptZW1vcnk6IikNCmBgYA0KDQpgYGB7c3FsIGNvbm5lY3Rpb249ZGJ9DQpjcmVhdGUgdGFibGUgdHhucyAoDQogIHRpZCBpbnRlZ2VyIHByaW1hcnkga2V5LA0KICB0ZGF0ZSBkYXRlIG5vdCBudWxsLA0KICB0cHJvZCB0ZXh0DQopDQpgYGANCg0KTm93IHRoYXQgdGhlIHRhYmxlIGlzIGNyZWF0ZWQsIGxldCdzIGluc2VydCBzb21lIHZhbHVlcyB0byBlbnN1cmUgdGhhdCB0aGUgdGFibGUgZGVmaW5pdGlvbiBpcyBjb3JyZWN0IGFuZCB3ZSBjYW4gcmV0cmlldmUgdGhlIGRhdGEgd2l0aCB0aGVpciBjb3JyZWN0IGRhdGEgdHlwZS4NCg0KYGBge3NxbCBjb25uZWN0aW9uPWRifQ0KaW5zZXJ0IGludG8gdHhucyB2YWx1ZXMgDQooMTAwLCAnMjAyMi0wMy0zMScsICdBbHZhcmV4JyksDQooMjAwLCAnMjAyMi0wMy0zMCcsICdQcm9zaWx2YXMnKSwNCigzMDAsICcyMDIyLTAzLTMwJywgJ0FsdmFyZXgnKSwNCig0MDAsICcyMDIyLTAyLTA5JywgJ0tpdG9tYXJvbicpLA0KKDUwMCwgJzIwMjItMDEtMTQnLCAnQmxldmFyZXgnKTsNCg0KYGBgDQoNCkxldCdzIHF1aWNrbHkgcmV0cmlldmUgYWxsIHJvd3MgdG8gZW5zdXJlIHRoZXkgYXJlIGluIHRoZSBkYXRhYmFzZS4NCg0KYGBge3NxbCBjb25uZWN0aW9uPWRifQ0Kc2VsZWN0ICogZnJvbSB0eG5zOw0KYGBgDQoNCkluZGVlZCwgdGhleSBhcmUuIEJ1dCBub3RlIHRoYXQgdGhlIGRhdGUgY29sdW1uICp0ZGF0ZSogaXMgcmV0dXJuZWQgYXMgdGV4dCAoKlw8Y2hyXD4qKS4gVG8gZGV0ZXJtaW5lIGlmIFNRTGl0ZSBhY3R1YWxseSBzdG9yZWQgdGhlbSBhcyB0eXBlICpkYXRlKiwgd2UgY2FuIHJ1biBhIFNRTCBxdWVyeSB0aGF0IHJlcXVpcmVzIGRhdGUgY29sdW1uIHR5cGUuDQoNCmBgYHtzcWwgY29ubmVjdGlvbj1kYn0NCnNlbGVjdCAqIA0KICBmcm9tIHR4bnMNCiB3aGVyZSB0ZGF0ZSBiZXR3ZWVuICcyMDIyLTAzLTAxJyBhbmQgJzIwMjItMDMtMzEnDQpgYGANCg0KVGhlIGFib3ZlIHF1ZXJ5IHdvcmtzLCBzbyB3ZSBjYW4gYmUgY29uZmlkZW50IHRoYXQgU1FMaXRlIHN0b3JlZCB0aGUgZGF0ZXMgY29ycmVjdGx5IGV2ZW4gdGhvdWdoIFIgY29udmVydCB0aGVtIHRvIHRleHQuDQoNCkFuZCwgZmluYWxseSwgbGV0J3MgcmV0cmlldmUgc29tZSBkYXRhIGludG8gYSBkYXRhIGZyYW1lIGFuZCBsaXN0IHRoZSBkYXRhIGFsb25nIHdpdGggdGhlIHR5cGVzIG9mIHRoZSBjb2x1bW5zIGluIHRoZSBkYXRhIGZyYW1lLiBSZWNhbGwgdGhhdCBhbGwgcmVzdWx0IHNldHMgb2YgcXVlcmllcyBhcmUgdGFibGVzLg0KDQpgYGB7cn0NCnJzIDwtIGRiR2V0UXVlcnkoZGIsICdzZWxlY3QgKiBmcm9tIHR4bnMnKQ0KDQpwcmludChycykNCmBgYA0KDQpOb3RlIHRoYXQgdGhlIGRhdGUgY29sdW1uIGNhbWUgYmFjayBmcm9tIHRoZSBxdWVyeSBhcyB0ZXh0OyB0aGUgZGF0YSBmcmFtZSBoYXMgdGhlIGRhdGEgdHlwZSAqXDxjaHJcPiogZm9yIHRoZSAqdGRhdGUqIGNvbHVtbi4gVG8gdXNlIHRoZW0gYXMgZGF0ZXMsIHlvdSBuZWVkIHRvIGNvZXJjZSB0aGUgY29sdW1uIHR5cGUgb2YgdGhlIGRhdGEgZnJhbWUgdG8gKkRhdGUqLg0KDQpgYGB7cn0NCnJzJHRkYXRlIDwtIGFzLkRhdGUocnMkdGRhdGUpDQoNCnByaW50KHJzKQ0KYGBgDQoNCk5vdGljZSB0aGF0IG5vdyB0aGUgKnRkYXRlKiBjb2x1bW4gaXMgb2YgdGhlIGNvcnJlY3QgUiAqRGF0ZSogdHlwZSBhbmQgd2UgY2FuIGFwcGx5IFIgZGF0ZSBmdW5jdGlvbnMgdG8gdGhhdCBjb2x1bW4uDQoNCiMjIFNhdmluZyBEYXRlIERhdGEgdG8gU1FMaXRlDQoNCkluIHRoZSBhYm92ZSBkaXNjdXNzaW9uLCB3ZSBzYXcgdGhhdCBTUUwgZGF0ZXMgYXJlIGNvbnZlcnRlZCB0byB0ZXh0IHdoZW4gcmV0cmlldmVkIGludG8gYSBkYXRhIGZyYW1lIGFuZCBhbiBleHBsaWNpdCBjb2VyY2lvbiBpcyByZXF1aXJlZCBpZiB3ZSB3YW50IHRvIHRyZWF0IGRhdGVzIGFzIGFjdHVhbCBSICpEYXRlKiBvYmplY3RzLiBBIHNpbWlsYXIgaXNzdWUgb2NjdXJzIHdoZW4gd2Ugc2F2ZSAqRGF0ZSogY29sdW1ucyBpbiBkYXRhIGZyYW1lcyB0byBSLiBXZSB3aWxsIGRlbW9uc3RyYXRlIHRoZSBpc3N1ZXMgYnkgY3JlYXRpbmcgYSBkYXRhIGZyYW1lIHdoaWNoIHdlIHdpbGwgdGhlbiBzYXZlIGludG8gYSBuZXcgdGFibGUgaW4gU1FMaXRlIHVzaW5nIDxjb2RlPmRiV3JpdGVUYWJsZSgpPC9jb2RlPiBhbmQgdGhlbiB3ZSB3aWxsIHNhdmUgdGhlIHNhbWUgZGF0YSBmcmFtZSB0byBhbiBhbHJlYWR5IGNyZWF0ZWQgdGFibGUsIGFnYWluIHVzaW5nIDxjb2RlPmRiV3JpdGVUYWJsZSgpPC9jb2RlPi4gVGhlIGRpZmZlcmVuY2UgaXMgdGhhdCBpbiBvbmUgY2FzZSwgdGhlIHRhYmxlIGRvZXMgbm90IHlldCBleGlzdCwgd2hpbGUgaW4gdGhlIG90aGVyIGl0IGRvZXMuDQoNCldlIHdpbGwgdXNlIHRoZSBkYXRhIGZyYW1lICpycyogdGhhdCB3ZSByZXRyaWV2ZWQgYWJvdmUuIFRvIHJldmlldywgd2UgaGFkIHRvIGNvZXJjZSB0aGUgZGF0YSBjb2x1bW4gZXhwbGljaXRseSB0byBhICpEYXRlKiBvYmplY3QuIEFzIHlvdSBjYW4gc2VlLCB0aGUgZGF0YSBmcmFtZSBoYXMgdGhyZWUgY29sdW1ucyBvZiB0eXBlcyAqaW50KiAoKmludGVnZXIqKSwgKmRhdGUqICgqRGF0ZSopIGFuZCAqY2hyKiAodGV4dCBjaGFyYWN0ZXJzKSwgcmVzcGVjdGl2ZWx5Lg0KDQpgYGB7cn0NCnByaW50KHJzKQ0KYGBgDQoNCkxldCdzIHNhdmUgdGhpcyBlbnRpcmUgZGF0YSBmcmFtZSB0byBhIG5ldyB0YWJsZSBpbiB0aGUgaW4tbWVtb3J5IFNRTGl0ZSBkYXRhYmFzZSBhbmQgdGhlbiBpbnNwZWN0IHRoZSBkYXRhIHR5cGVzIG9mIHRoZSBjb2x1bW5zIG9mIHRoZSB0YWJsZSBpbiB0aGUgZGF0YWJhc2UuDQoNCmBgYHtyfQ0KZGJXcml0ZVRhYmxlKGRiLCAnbmV3VHhucycsIHJzKQ0KYGBgDQoNCk5vdywgaWYgd2UgcmV0cmlldmUgd2Uga25vdyBmcm9tIHRoZSBleHBlcmltZW50cyBhYm92ZSB0aGF0IHRoZSBkYXRlIGNvbHVtbiB3aWxsIGJlIHRleHQsIGJ1dCB3ZSB3YW50IHRvIGtub3cgd2hhdCB0aGUgZGF0YWJhc2Ugc3RvcmVzIHRoZW0gYXMuIFRoZSBjb2RlIGJlbG93IHVzZXMgYSAqUFJBR01BKiB0byBxdWVyeSB0aGUgZGF0YWJhc2UgaXRzZWxmLg0KDQpgYGB7c3FsIGNvbm5lY3Rpb249ZGJ9DQpQUkFHTUEgdGFibGVfaW5mbyhuZXdUeG5zKTsNCmBgYA0KDQpMZXQncyBjb21wYXJlIHRoYXQgdG8gdGhlIHN0cnVjdHVyZSBvZiBhIHRhYmxlIHRoYXQgd2UgY3JlYXRlIG1hbnVhbGx5Lg0KDQpgYGB7c3FsIGNvbm5lY3Rpb249ZGJ9DQpjcmVhdGUgdGFibGUgbmV3TWFuVHhucyAoDQogIHRpZCBpbnRlZ2VyIHByaW1hcnkga2V5LA0KICB0ZGF0ZSBkYXRlIG5vdCBudWxsLA0KICB0cHJvZCB0ZXh0DQopDQpgYGANCg0KYGBge3NxbCBjb25uZWN0aW9uPWRifQ0KUFJBR01BIHRhYmxlX2luZm8obmV3TWFuVHhucyk7DQpgYGANCg0KTm90aWNlIGhvdyB0aGUgdGFibGUgY3JlYXRlZCBieSA8Y29kZT5kYldyaXRlVGFibGUoKTwvY29kZT4gaGFzICpSRUFMKiBhcyB0aGUgZGF0YSB0eXBlIGZvciB0aGUgKnRkYXRlKiBjb2x1bW4gd2hpbGUgdGhlIG1hbnVhbGx5IGNyZWF0ZWQgdGFibGUgaGFzIHRoZSBjb3JyZWN0IFNRTCBkYXRlIGRhdGEgdHlwZS4NCg0KU28sIHdlIGtub3cgdGhhdCBzYXZpbmcgYSBkYXRhIGZyYW1lIHRvIFNRTGl0ZSB3aWxsIGNoYW5nZSB0aGUgZGF0ZSBjb2x1bW4gZGF0YSB0eXBlIGZyb20gKkRhdGUqIHRvICpSRUFMKiAtLSBub3QgdmVyeSBmcmllbmRseSBhbmQgdXNlZnVsLiBMZXQncyBzZWUgd2hhdCBoYXBwZW5zIHdoZW4gd2Ugd3JpdGUgdGhlIGRhdGEgZnJhbWUgdG8gYW4gZXhpc3RpbmcgdGFibGUuIEFzIGEgcmVtaW5kZXIsIHRoZSBjb2x1bW5zIG9mIHRoZSBkYXRhIGZyYW1lIG11c3QgYmUgaW4gdGhlIHNhbWUgb3JkZXIgYXMgaW4gdGhlIHRhYmxlIGRlZmluaXRpb24uIEJlY2F1c2UsIHRoZSB0YWJsZSBhbHJlYWR5IGV4aXN0cywgd2UgbmVlZCB0byBzcGVjaWZ5ICphcHBlbmQ9VCo7IGJlIGNhcmVmdWwgbm90IHRvIHNwZWNpZnkgKm92ZXJ3cml0ZT1UKiBhcyB0aGF0IHdpbGwgd2lwZSBvdXQgdGhlIHRhYmxlIGFuZCByZS1jcmVhdGUgaXQuDQoNCmBgYHtyfQ0KZGJXcml0ZVRhYmxlKGRiLCAnbmV3TWFuVHhucycsIHJzLCBhcHBlbmQ9VCkNCmBgYA0KDQpBbmQsIGFnYWluLCBsZXQncyBpbnNwZWN0IHRoZSB0YWJsZS4NCg0KYGBge3NxbCBjb25uZWN0aW9uPWRifQ0KUFJBR01BIHRhYmxlX2luZm8obmV3TWFuVHhucyk7DQpgYGANCg0KTm93LCBpdCBoYXMgdGhlIGNvcnJlY3QgZGF0YSB0eXBlLiBPZiBjb3Vyc2UsIGxldCdzIGNoZWNrIHRoZSBkYXRhIGl0c2VsZiwgdG9vOg0KDQpgYGB7c3FsIGNvbm5lY3Rpb249ZGJ9DQpzZWxlY3QgKiBmcm9tIG5ld01hblR4bnM7DQpgYGANCg0KVGhlICp0YWRlKiBjb2x1bW4gaGFzIGFuICppbnQqIGRhdGEgdHlwZSwgYnV0IGxldCdzIGJlIHN1cmUgdGhhdCBpcyBjb3JyZWN0IGluIHRoZSBkYXRhYmFzZS4gV2UnbGwgcnVuIHRoZSBzYW1lIHF1ZXJ5IGFzIHdlIGRpZCBhYm92ZSB0aGF0IHJlcXVpcmVzIHRoZSBkYXRhIHRvIGJlIG9mIGEgZGF0ZSB0eXBlLg0KDQpgYGB7c3FsIGNvbm5lY3Rpb249ZGJ9DQpzZWxlY3QgKiANCiAgZnJvbSBuZXdNYW5UeG5zDQogd2hlcmUgdGRhdGUgYmV0d2VlbiAnMjAyMi0wMy0wMScgYW5kICcyMDIyLTAzLTMxJw0KYGBgDQoNCkFuZCwgYXMgeW91IGNhbiBzZWUsIHRoYXQgZGlkIG5vdCB3b3JrIC0tIHdoaWNoIGltcGxpZXMgdGhhdCBSIG9yIFNRTGl0ZSBtb2RpZmllZCB0aGUgZGF0YSBhbmQgdGhhdCBhICpEYXRlKiBvYmplY3QgaXMgbm90IGNvcnJlY3RseSB3cml0dGVuIHRvIGEgU1FMaXRlIGRhdGFiYXNlIHRvIGEgZGF0ZSBjb2x1bW4uDQoNClRoZSBmaW5hbCBleHBlcmltZW50IHVzZXMgYW4gUiBkYXRhIGZyYW1lIHRoYXQgaGFzIHRoZSBkYXRlIGFzIHRleHQgYW5kIHdyaXRlcyBpdCB0byBhIHRhYmxlIGluIHRoZSBkYXRhYmFzZSB0aGF0IGhhcyBhICpkYXRlKiBjb2x1bW4uDQoNCldlIG5lZWQgdG8gZmlyc3QgcmVtb3ZlIGFsbCByb3dzIGZyb20gdGhlICpuZXdNYW5UeG5zKiB0YWJsZSBzbyB3ZSBjYW4gd29yayB3aXRoIGFuIGVtcHR5IHRhYmxlLg0KDQpgYGB7c3FsIGNvbm5lY3Rpb249ZGJ9DQpkZWxldGUgZnJvbSBuZXdNYW5UeG5zOw0KYGBgDQoNCmBgYHtyfQ0KcnMyIDwtIGRiR2V0UXVlcnkoZGIsICdzZWxlY3QgKiBmcm9tIHR4bnMnKQ0KDQpwcmludChyczIpDQpgYGANCg0KU28sIHRoZSBkYXRhIGZyYW1lICpyczIqIGhhcyBkYXRlcyBmcm9tIHRoZSBkYXRhYmFzZSBhcyBjaGFyYWN0ZXJzLg0KDQpgYGB7cn0NCmRiV3JpdGVUYWJsZShkYiwgJ25ld01hblR4bnMnLCByczIsIGFwcGVuZD1UKQ0KYGBgDQoNCkxldCdzIGxvb2sgYXQgdGhlIGRhdGEgaW4gdGhlIGRhdGFiYXNlIGZpcnN0Og0KDQpgYGB7c3FsIGNvbm5lY3Rpb249ZGJ9DQpzZWxlY3QgKiBmcm9tIG5ld01hblR4bnM7DQpgYGANCg0KQW5kLCBmaW5hbGx5LCBsZXQncyBydW4gdGhlIGRhdGUgcXVlcnkgYWdhaW46DQoNCmBgYHtzcWwgY29ubmVjdGlvbj1kYn0NCnNlbGVjdCAqIA0KICBmcm9tIG5ld01hblR4bnMNCiB3aGVyZSB0ZGF0ZSBiZXR3ZWVuICcyMDIyLTAzLTAxJyBhbmQgJzIwMjItMDMtMzEnDQpgYGANCg0KTm93IGl0IHdvcmtlZC4gU28gd2hhdCBkaWQgd2UgbGVhcm4/IERhdGUgY29sdW1ucyBpbiBkYXRhIGZyYW1lcyBkbyBub3QgY29ycmVjdGx5IG1hcCB0byBkYXRlIGNvbHVtbnMgaW4gdGhlIGRhdGFiYXNlLiBJbiBvcmRlciBmb3IgPGNvZGU+ZGJXcml0ZVRhYmxlKCk8L2NvZGU+IHRvIHByb3Blcmx5IHdyaXRlIGRhdGVzIHRvICpkYXRlKiBjb2x1bW5zIGluIHRoZSBkYXRhYmFzZSwgdGhlIGNvbHVtbiBmb3IgdGhlIGRhdGVzIG11c3QgYmUgY2hhcmFjdGVycyBpbiBSOyB0aGV5IGNhbm5vdCBiZSAqRGF0ZSogb2JqZWN0cy4gQ291bnRlcmluaXR1aXRpdmUsIGJ1dCB0aGF0J3MgdGhlIHdheSBpdCB3b3Jrcy4NCg0KIyMgVW5kZXJzdGFuZGluZyBSIHRvIFNRTCBEYXRhIE1hcHBpbmdzDQoNClRvIHVuZGVyc3RhbmQgaG93IFIgbWFwcyBvYmplY3RzIHRvIFNRTCwgd2UgY2FuIHVzZSB0aGUgPGNvZGU+ZGJEYXRhVHlwZSgpPC9jb2RlPiBmdW5jdGlvbi4NCg0KYGBge3J9DQpkYkRhdGFUeXBlKGRiLCBycyR0ZGF0ZSkNCmBgYA0KDQpUaGlzIHRlbGxzIHVzIFIgd2lsbCBjb252ZXJ0IHRoZSBjb2x1bW4gdG8gKlJFQUwqIHdoZW4gd3JpdHRlbiB0byB0aGUgZGF0YWJhc2UsIHdoaWNoIGlzIHdoYXQgd2Ugc2F3IGFib3ZlLg0KDQojIyBDb25jbHVzaW9uDQoNCkRhdGVzIG11c3QgYmUgcHJvcGVybHkgZW5jb2RlZCBmb3IgdGhlbSB0byBiZSBwcm9jZXNzZWQgY29ycmVjdGx5LiBSIGFuZCBTUUwgdXNlIGRhdGVzIGRpZmZlcmVudGx5IGFuZCBjb252ZXJ0aW5nIGJldHdlZW4gdGhlIHR3byBmb3JtYXRzIHJlcXVpcmUgY2FyZS4gRGF0ZSBjb2x1bW5zIGluIGRhdGEgZnJhbWVzIGRvIG5vdCBnZXQgY29udmVydGVkIHByb3Blcmx5IHRvIGNvbHVtbnMgb2YgdHlwZSAqZGF0ZSogaW4gU1FMaXRlLg0KDQojIyBUdXRvcmlhbA0KDQpUaGUgdmlkZW8gdHV0b3JpYWwgZGVtb25zdHJhdGVzIHRoZSBjb25zdHJ1Y3RzIGludHJvZHVjZWQgaW4gdGhpcyBsZXNzb24uDQoNCmBgYHs9aHRtbH0NCjxpZnJhbWUgc3JjPSJodHRwczovL3BsYXllci52aW1lby5jb20vdmlkZW8vNzA0MjYwODM2P2g9NmRiNTM4NjhlZCZhbXA7YmFkZ2U9MCZhbXA7YXV0b3BhdXNlPTAmYW1wO3BsYXllcl9pZD0wJmFtcDthcHBfaWQ9NTg0NzkiIGZyYW1lYm9yZGVyPSIwIiBhbGxvdz0iYXV0b3BsYXk7IGZ1bGxzY3JlZW47IHBpY3R1cmUtaW4tcGljdHVyZSIgYWxsb3dmdWxsc2NyZWVuIHdpZHRoPSI0ODAiIGhlaWdodD0iMjcwIiB0aXRsZT0iRGF0ZXMgaW4gUiBhbmQgU1FMaXRlIiBkYXRhLWV4dGVybmFsPSIxIj48L2lmcmFtZT4NCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgRmlsZXMgJiBSZXNvdXJjZXMNCg0KYGBge3IgemlwRmlsZXMsIGVjaG89RkFMU0V9DQp6aXBOYW1lID0gc3ByaW50ZigiTGVzc29uRmlsZXMtJXMtJXMuemlwIiwgDQogICAgICAgICAgICAgICAgIHBhcmFtcyRjYXRlZ29yeSwNCiAgICAgICAgICAgICAgICAgcGFyYW1zJG51bWJlcikNCg0KdGV4dEFMaW5rID0gcGFzdGUwKCJBbGwgRmlsZXMgZm9yIExlc3NvbiAiLCANCiAgICAgICAgICAgICAgIHBhcmFtcyRjYXRlZ29yeSwiLiIscGFyYW1zJG51bWJlcikNCg0KIyBkb3dubG9hZEZpbGVzTGluaygpIGlzIGluY2x1ZGVkIGZyb20gX2luc2VydDJEQi5SDQprbml0cjo6cmF3X2h0bWwoZG93bmxvYWRGaWxlc0xpbmsoIi4iLCB6aXBOYW1lLCB0ZXh0QUxpbmspKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyBTZWUgQWxzbw0KDQotICAgWzYuMTEyIEJhc2ljcyBvZiBUZXh0ICYgU3RyaW5nIFByb2Nlc3NpbmcgaW4gUl0oaHR0cDovL2FydGlmaWNpdW0udXMvbGVzc29ucy8wNi5yL2wtNi0xMTItdGV4dC1wcm9jL2wtNi0xMTIuaHRtbCkNCi0gICBbNi4xMDAgQmVnaW5uaW5nIFJdKGh0dHA6Ly9hcnRpZmljaXVtLnVzL2wtNi0xMDAuaHRtbCkNCg0KIyMgRnVydGhlciBTdHVkeQ0KDQotICAgW0RhdGVzIGFuZCBUaW1lcyBpbiBSXShodHRwczovL3d3dy5zdGF0LmJlcmtlbGV5LmVkdS9+czEzMy9kYXRlcy5odG1sKQ0KLSAgIFtEYXRlIEZvcm1hdHMgaW4gUl0oaHR0cHM6Ly93d3cuci1ibG9nZ2Vycy5jb20vMjAxMy8wOC9kYXRlLWZvcm1hdHMtaW4tci8pDQotICAgW2RiRGF0YVR5cGU6IERldGVybWluZSB0aGUgU1FMIGRhdGEgdHlwZSBvZiBhbiBSIG9iamVjdF0oaHR0cHM6Ly9yZHJyLmlvL2NyYW4vREJJL21hbi9kYkRhdGFUeXBlLmh0bWwpDQoNCiMjIEVycmF0YQ0KDQpOb25lIGNvbGxlY3RlZCB5ZXQuIExldCB1cyBrbm93Lg0KDQpgYGB7PWh0bWx9DQo8c2NyaXB0IHNyYz0iaHR0cHM6Ly9mb3JtLmpvdGZvcm0uY29tL3N0YXRpYy9mZWVkYmFjazIuanMiIHR5cGU9InRleHQvamF2YXNjcmlwdCI+DQogIG5ldyBKb3Rmb3JtRmVlZGJhY2soew0KICAgIGZvcm1JZDogIjIxMjE4NzA3Mjc4NDE1NyIsDQogICAgYnV0dG9uVGV4dDogIkZlZWRiYWNrIiwNCiAgICBiYXNlOiAiaHR0cHM6Ly9mb3JtLmpvdGZvcm0uY29tLyIsDQogICAgYmFja2dyb3VuZDogIiNGNTkyMDIiLA0KICAgIGZvbnRDb2xvcjogIiNGRkZGRkYiLA0KICAgIGJ1dHRvblNpZGU6ICJsZWZ0IiwNCiAgICBidXR0b25BbGlnbjogImNlbnRlciIsDQogICAgdHlwZTogZmFsc2UsDQogICAgd2lkdGg6IDcwMCwNCiAgICBoZWlnaHQ6IDUwMCwNCiAgICBpc0NhcmRGb3JtOiBmYWxzZQ0KICB9KTsNCjwvc2NyaXB0Pg0KYGBgDQpgYGB7ciBjb2RlPXhmdW46OnJlYWRfdXRmOChwYXN0ZTAoaGVyZTo6aGVyZSgpLCcvUi9fZGVwbG95S25pdC5SJykpLCBpbmNsdWRlID0gRkFMU0V9DQpgYGANCg==