Skip to content

Commit

Permalink
#2 Added isValidDate and isValidDateTime
Browse files Browse the repository at this point in the history
  • Loading branch information
bokkypoobah committed Sep 1, 2018
1 parent d6123cf commit 06b1d47
Show file tree
Hide file tree
Showing 12 changed files with 262 additions and 36 deletions.
57 changes: 53 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# BokkyPooBah's DateTime Library

**Status: I'm currently trying to get this library audited, so don't use in production mode yet. Feedback welcome.**
**Status: This library is currently being tested and audited. Feedback welcome.**

A gas-efficient Solidity date and time library.

Instead of using loops and lookup tables, this date conversions library uses formulae to convert year/month/day hour:minute:second to a Unix timestamp and back.

<br />

If you find this library useful for your project, **especially commercial projects**, please donate to [0xb6dAC2C5A0222f6794265249ACE15568B750c2d1](https://etherscan.io/address/0xb6dAC2C5A0222f6794265249ACE15568B750c2d1). I hope to cover my cost to get this library audited.
If you find this library useful for your project, **especially commercial projects**, please donate to [0xb6dAC2C5A0222f6794265249ACE15568B750c2d1](https://etherscan.io/address/0xb6dAC2C5A0222f6794265249ACE15568B750c2d1). I hope to cover my cost of getting this library independently audited.

If there is sufficient interest and donations, this library will be extended (or built upon) to handle financial date calculations like cashflow generation, days basis (ACT/ACT, ACT/365, 30/360, ...), regional holidays in a shared smart contract database, potentially with a Decentralised Autonomous Organisation as the keeper of this database.

Expand All @@ -29,6 +29,8 @@ If there is sufficient interest and donations, this library will be extended (or
* [timestampFromDateTime](#timestampfromdatetime)
* [timestampToDate](#timestamptodate)
* [timestampToDateTime](#timestamptodatetime)
* [isValidDate](#isvaliddate)
* [isValidDateTime](#isvaliddatetime)
* [isLeapYear](#isleapyear)
* [_isLeapYear](#_isleapyear)
* [isWeekDay](#isweekday)
Expand Down Expand Up @@ -76,7 +78,8 @@ Version | Date | Notes
v1.00-pre-release | May 25 2018 | "Rarefaction" pre-release. I'm currently trying to get this library audited, so don't use in production mode yet.
v1.00-pre-release-a | Jun 2 2018 | "Rarefaction" pre-release a. Added the [contracts/BokkyPooBahsDateTimeContract.sol](contracts/BokkyPooBahsDateTimeContract.sol) wrapper for convenience.<br />[Alex Kampa](https://github.com/alex-kampa) conducted a range of [tests](https://github.com/alex-kampa/test_BokkyPooBahsDateTimeLibrary) on the library.
v1.00-pre-release-b | Jun 4 2018 | "Rarefaction" pre-release b. Replaced public function with internal for easier EtherScan verification - [a83e13b](https://github.com/bokkypoobah/BokkyPooBahsDateTimeLibrary/commit/a83e13bef31e8ef399007dd237e42bd5cdf479e6) .<br /> Deployed [contracts/BokkyPooBahsDateTimeContract.sol](contracts/BokkyPooBahsDateTimeContract.sol) with the inlined [contracts/BokkyPooBahsDateTimeLibrary.sol](contracts/BokkyPooBahsDateTimeLibrary.sol) to the [Ropsten network](deployment/deployment-v1.00-prerelease.md) at address [0x07239bb079094481bfaac91ca842426860021aaa](https://ropsten.etherscan.io/address/0x07239bb079094481bfaac91ca842426860021aaa#code)
v1.00-pre-release-c | June 8 2018 | "Rarefaction" pre-release c. Added [check](https://github.com/bokkypoobah/BokkyPooBahsDateTimeLibrary/commit/4002b278d1779fcd4f3f4527a60a5887ee6c20ba) `require(year >= 1970)` as highlighted in JZaki's audit
v1.00-pre-release-c | June 8 2018 | "Rarefaction" pre-release c. Added `require(year >= 1970)` to `_daysFromDate(...)` in [4002b27](https://github.com/bokkypoobah/BokkyPooBahsDateTimeLibrary/commit/4002b278d1779fcd4f3f4527a60a5887ee6c20ba) as highlighted in [James Zaki](https://github.com/jzaki)'s audit
v1.00-pre-release-d | Sep 1 2018 | "Rarefaction" pre-release d. Added [isValidDate(...)](#isvaliddate) and [isValidDateTime(...)](#isvaliddatetime) in [380061b](https://github.com/bokkypoobah/BokkyPooBahsDateTimeLibrary/commit/380061b9d20c83450ee303f709fe58e973c5f4a9) as highlighted in [Adrian Guerrera](https://github.com/apguerrera)'s audit

<br />

Expand Down Expand Up @@ -145,6 +148,22 @@ This library aims to replicate the [Unix time](https://en.wikipedia.org/wiki/Uni

<br />

### What is the maximum year 2345?

Asked by [Adrian Guerrera](https://github.com/apguerrera).

**2345** is just an arbitrary number chosen for the year limit to test to. The algorithm should still work beyond this date.

<br />

### Why are there no input validations to some of the functions?

Asked by [Adrian Guerrera](https://github.com/apguerrera). Specifically, the functions [_daysFromDate](#_daysfromdate), [timestampFromDate](#timestampfromdate) and [timestampFromDateTime](#timestampfromdatetime).

The date and time inputs should be validated before the values are passed to these functions. The validation functions [isValidDate(...)](#isvaliddate) and [isValidDateTime(...)](#isvaliddatetime) have now been added for this purpose.

<br />

<hr />

## Conventions
Expand Down Expand Up @@ -181,6 +200,8 @@ Calculate the number of days `_days` from 1970/01/01 to `year`/`month`/`day`.
function _daysFromDate(uint year, uint month, uint day) public pure returns (uint _days)
```

**NOTE** This function does not validate the `year`/`month`/`day` input. Use [`isValidDate(...)`](#isvaliddate) to validate the input if necessary.

<br />

### _daysToDate
Expand All @@ -201,6 +222,8 @@ Calculate the `timestamp` to `year`/`month`/`day`.
function timestampFromDate(uint year, uint month, uint day) public pure returns (uint timestamp)
```

**NOTE** This function does not validate the `year`/`month`/`day` input. Use [`isValidDate(...)`](#isvaliddate) to validate the input if necessary.

<br />

### timestampFromDateTime
Expand All @@ -211,6 +234,8 @@ Calculate the `timestamp` to `year`/`month`/`day` `hour`:`minute`:`second` UTC.
function timestampFromDateTime(uint year, uint month, uint day, uint hour, uint minute, uint second) public pure returns (uint timestamp)
```

**NOTE** This function does not validate the `year`/`month`/`day` `hour`:`minute`:`second` input. Use [`isValidDateTime(...)`](#isvaliddatetime) to validate the input if necessary.

<br />

### timestampToDate
Expand All @@ -233,6 +258,28 @@ function timestampToDateTime(uint timestamp) public pure returns (uint year, uin

<br />

### isValidDate

Is the date specified by `year`/`month`/`day` a valid date?

```javascript_
function isValidDate(uint year, uint month, uint day) internal pure returns (bool valid)
```

<br />

<br />

### isValidDateTime

Is the date/time specified by `year`/`month`/`day` `hour`:`minute`:`second` a valid date/time?

```javascript_
function isValidDateTime(uint year, uint month, uint day, uint hour, uint minute, uint second) internal pure returns (bool valid)
```

<br />

### isLeapYear

Is the year specified by `timestamp` a leap year?
Expand Down Expand Up @@ -704,6 +751,8 @@ in [test/test1results.txt](test/test1results.txt) and the detailed output saved

* [x] Deploy [contracts/BokkyPooBahsDateTimeLibrary.sol](contracts/BokkyPooBahsDateTimeLibrary.sol) library
* [x] Deploy [contracts/TestDateTime.sol](contracts/TestDateTime.sol) contract
* [x] Test `isValidDate(...)`
* [x] Test `isValidDateTime(...)`
* [x] Test `isLeapYear(...)`
* [x] Test `_isLeapYear(...)`
* [x] Test `isWeekDay(...)`
Expand Down Expand Up @@ -732,4 +781,4 @@ A copy of the webpage with the algorithm [Converting Between Julian Dates and Gr
<br />
(c) BokkyPooBah / Bok Consulting Pty Ltd - Jun 2 2018. [GNU Lesser General Public License 3.0](https://www.gnu.org/licenses/lgpl-3.0.en.html)
(c) BokkyPooBah / Bok Consulting Pty Ltd - Sep 1 2018. [GNU Lesser General Public License 3.0](https://www.gnu.org/licenses/lgpl-3.0.en.html)
8 changes: 7 additions & 1 deletion contracts/BokkyPooBahsDateTimeContract.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pragma solidity ^0.4.23;
pragma solidity ^0.4.24;

// ----------------------------------------------------------------------------
// BokkyPooBah's DateTime Library v1.00 - Contract Instance
Expand Down Expand Up @@ -69,6 +69,12 @@ contract BokkyPooBahsDateTimeContract {
(year, month, day, hour, minute, second) = BokkyPooBahsDateTimeLibrary.timestampToDateTime(timestamp);
}

function isValidDate(uint year, uint month, uint day) public pure returns (bool valid) {
valid = BokkyPooBahsDateTimeLibrary.isValidDate(year, month, day);
}
function isValidDateTime(uint year, uint month, uint day, uint hour, uint minute, uint second) public pure returns (bool valid) {
valid = BokkyPooBahsDateTimeLibrary.isValidDateTime(year, month, day, hour, minute, second);
}
function isLeapYear(uint timestamp) public pure returns (bool leapYear) {
leapYear = BokkyPooBahsDateTimeLibrary.isLeapYear(timestamp);
}
Expand Down
17 changes: 16 additions & 1 deletion contracts/BokkyPooBahsDateTimeLibrary.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pragma solidity ^0.4.23;
pragma solidity ^0.4.24;

// ----------------------------------------------------------------------------
// BokkyPooBah's DateTime Library v1.00
Expand Down Expand Up @@ -126,6 +126,21 @@ library BokkyPooBahsDateTimeLibrary {
second = secs % SECONDS_PER_MINUTE;
}

function isValidDate(uint year, uint month, uint day) internal pure returns (bool valid) {
if (year >= 1970 && month > 0 && month <= 12) {
uint daysInMonth = _getDaysInMonth(year, month);
if (day > 0 && day <= daysInMonth) {
valid = true;
}
}
}
function isValidDateTime(uint year, uint month, uint day, uint hour, uint minute, uint second) internal pure returns (bool valid) {
if (isValidDate(year, month, day)) {
if (hour < 24 && minute < 60 && second < 60) {
valid = true;
}
}
}
function isLeapYear(uint timestamp) internal pure returns (bool leapYear) {
uint year;
uint month;
Expand Down
7 changes: 7 additions & 0 deletions contracts/TestDateTime.sol
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ contract TestDateTime {
dayOfWeek = BokkyPooBahsDateTimeLibrary.getDayOfWeek(timestamp);
}

function isValidDate(uint year, uint month, uint day) public pure returns (bool valid) {
valid = BokkyPooBahsDateTimeLibrary.isValidDate(year, month, day);
}
function isValidDateTime(uint year, uint month, uint day, uint hour, uint minute, uint second) public pure returns (bool valid) {
valid = BokkyPooBahsDateTimeLibrary.isValidDateTime(year, month, day, hour, minute, second);
}

function getYear(uint timestamp) public pure returns (uint year) {
year = BokkyPooBahsDateTimeLibrary.getYear(timestamp);
}
Expand Down
47 changes: 47 additions & 0 deletions test/01_test1.sh
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,53 @@ if ("$MODE" == "full") {
console.log("RESULT: ");
}
if ("$MODE" == "full") {
console.log("RESULT: ---------- Test isValidDate and isValidDateTime ----------");
if (!assert(testDateTime.isValidDate(1969, 1, 1) == false, "testDateTime.isValidDate(1969, 1, 1) is false")) {
failureDetected = true;
}
if (!assert(testDateTime.isValidDate(1970, 1, 1) == true, "testDateTime.isValidDate(1970, 1, 1) is true")) {
failureDetected = true;
}
if (!assert(testDateTime.isValidDate(2000, 2, 29) == true, "testDateTime.isValidDate(2000, 2, 29) is true")) {
failureDetected = true;
}
if (!assert(testDateTime.isValidDate(2001, 2, 29) == false, "testDateTime.isValidDate(2001, 2, 29) is false")) {
failureDetected = true;
}
if (!assert(testDateTime.isValidDate(2001, 0, 1) == false, "testDateTime.isValidDate(2001, 0, 1) is false")) {
failureDetected = true;
}
if (!assert(testDateTime.isValidDate(2001, 1, 0) == false, "testDateTime.isValidDate(2001, 1, 0) is false")) {
failureDetected = true;
}
if (!assert(testDateTime.isValidDateTime(2000, 2, 29, 0, 0, 0) == true, "testDateTime.isValidDateTime(2000, 2, 29, 0, 0, 0) is true")) {
failureDetected = true;
}
if (!assert(testDateTime.isValidDateTime(2000, 2, 29, 1, 1, 1) == true, "testDateTime.isValidDateTime(2000, 2, 29, 1, 1, 1) is true")) {
failureDetected = true;
}
if (!assert(testDateTime.isValidDateTime(2000, 2, 29, 23, 1, 1) == true, "testDateTime.isValidDateTime(2000, 2, 29, 23, 1, 1) is true")) {
failureDetected = true;
}
if (!assert(testDateTime.isValidDateTime(2000, 2, 29, 24, 1, 1) == false, "testDateTime.isValidDateTime(2000, 2, 29, 24, 1, 1) is false")) {
failureDetected = true;
}
if (!assert(testDateTime.isValidDateTime(2000, 2, 29, 1, 59, 1) == true, "testDateTime.isValidDateTime(2000, 2, 29, 1, 59, 1) is true")) {
failureDetected = true;
}
if (!assert(testDateTime.isValidDateTime(2000, 2, 29, 1, 60, 1) == false, "testDateTime.isValidDateTime(2000, 2, 29, 1, 60, 1) is false")) {
failureDetected = true;
}
if (!assert(testDateTime.isValidDateTime(2000, 2, 29, 1, 1, 59) == true, "testDateTime.isValidDateTime(2000, 2, 29, 1, 1, 59) is true")) {
failureDetected = true;
}
if (!assert(testDateTime.isValidDateTime(2000, 2, 29, 1, 1, 60) == false, "testDateTime.isValidDateTime(2000, 2, 29, 1, 1, 60) is false")) {
failureDetected = true;
}
console.log("RESULT: ");
}
if ("$MODE" == "full") {
console.log("RESULT: ---------- Test _isLeapYear ----------");
if (!assert(testDateTime._isLeapYear(2000), "2000 is a leap year")) {
Expand Down
2 changes: 1 addition & 1 deletion test/BokkyPooBahsDateTimeLibrary.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ var dateTimeLibOutput={
"BokkyPooBahsDateTimeLibrary.sol:BokkyPooBahsDateTimeLibrary" :
{
"abi" : "[]",
"bin" : "604c602c600b82828239805160001a60731460008114601c57601e565bfe5b5030600052607381538281f30073000000000000000000000000000000000000000030146080604052600080fd00a165627a7a72305820a2a3b476a308eda99d9a7ddb817096b88033e41a0fd6df223e0f8b8b29061d7a0029"
"bin" : "604c602c600b82828239805160001a60731460008114601c57601e565bfe5b5030600052607381538281f30073000000000000000000000000000000000000000030146080604052600080fd00a165627a7a72305820023d965885621706b89668644481d0959048ec8c69bef6e9d7a75aae7c6ea6b80029"
}
},
"version" : "0.4.24+commit.e67f0147.Darwin.appleclang"
Expand Down
17 changes: 16 additions & 1 deletion test/BokkyPooBahsDateTimeLibrary.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pragma solidity ^0.4.23;
pragma solidity ^0.4.24;

// ----------------------------------------------------------------------------
// BokkyPooBah's DateTime Library v1.00
Expand Down Expand Up @@ -126,6 +126,21 @@ library BokkyPooBahsDateTimeLibrary {
second = secs % SECONDS_PER_MINUTE;
}

function isValidDate(uint year, uint month, uint day) internal pure returns (bool valid) {
if (year >= 1970 && month > 0 && month <= 12) {
uint daysInMonth = _getDaysInMonth(year, month);
if (day > 0 && day <= daysInMonth) {
valid = true;
}
}
}
function isValidDateTime(uint year, uint month, uint day, uint hour, uint minute, uint second) internal pure returns (bool valid) {
if (isValidDate(year, month, day)) {
if (hour < 24 && minute < 60 && second < 60) {
valid = true;
}
}
}
function isLeapYear(uint timestamp) internal pure returns (bool leapYear) {
uint year;
uint month;
Expand Down
Loading

0 comments on commit 06b1d47

Please sign in to comment.