Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Solidity Gas-Efficient DateTime Library

License

NotificationsYou must be signed in to change notification settings

jzaki/BokkyPooBahsDateTimeLibrary

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

21 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Status: I'm currently trying to get this library audited, so don't use in production mode yet. 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.


If you find this library useful for your project,especially commercial projects, please donate to0xb6dAC2C5A0222f6794265249ACE15568B750c2d1. I hope to cover my cost to get this library 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.



Table Of Contents



History

VersionDateNotes
v1.00-pre-releaseMay 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-aJun 2 2018"Rarefaction" pre-release. Added thecontracts/BokkyPooBahsDateTimeContract.sol wrapper for convenience
v1.00-pre-release-bJun 4 2018"Rarefaction" pre-release. Replaced public function with internal for easier EtherScan verification -a83e13b


Deployment

TODO



Questions And Answers

Questions by_dredge

User/u/_dredge asked thefollowing questions:

Some Muslim countries have a Friday/Saturday weekend. Workday(1-7) may be more useful.

I presume leap seconds and such details are taken care of in the Unix timecode.

Some additional ideas for functions below.

Quarter calculations

weekNumber(1-53)

Complete years / months / weeks / days

Nearest years / months / weeks / days

Regarding regions and systems where Friday/Saturday are weekends, please use the functiongetDayOfWeek(timestamp) that returns 1 (= Monday, ..., 7 (= Sunday) to determine whether you should treat a day as a weekday or weekend.

See the next question regarding the leap seconds.

Regarding the additional ideas, thanks!


What about the leap second?

Asked byOleksii Matiiasevych and/u/_dredge above.

For example, aleap second was inserted on Jan 01 1999.

From the first answer toUnix time and leap seconds:

The number of seconds per day are fixed with Unix timestamps.

The Unix time number is zero at the Unix epoch, and increases by exactly 86400 per day since the epoch.

So it cannot represent leap seconds. The OS will slow down the clock to accommodate for this. The leap seconds is simply not existent as far a Unix timestamps are concerned.

And from the second answer toUnix time and leap seconds:

Unix time is easy to work with, but some timestamps are not real times, and some timestamps are not unique times.

That is, there are some duplicate timestamps representing two different seconds in time, because in unix time the sixtieth second might have to repeat itself (as there can't be a sixty-first second). Theoretically, they could also be gaps in the future because the sixtieth second doesn't have to exist, although no skipping leap seconds have been issued so far.

Rationale for unix time: it's defined so that it's easy to work with. Adding support for leap seconds to the standard libraries is very tricky....

This library aims to replicate theUnix time functionality and assumes that leap seconds are handled by the underlying operating system.



Conventions

All dates, times and Unix timestamps areUTC.

UnitRangeNotes
timestamp>= 0Unix timestamp, number of seconds since 1970/01/01 00:00:00 UTC
year1970 ... 2345
month1 ... 12
day1 ... 31
hour0 ... 23
minute0 ... 59
second0 ... 59
dayOfWeek1 ... 71 = Monday, ..., 7 = Sunday
year/month/day1970/01/01 ... 2345/12/31

_days,_months and_years variable names are_-prefixed as the non-prefixed versions are reserve words in Solidity.

All functions operate on theuint timestamp data type, except for functions prefixed with_.



Functions

_daysFromDate

Calculate the number of days_days from 1970/01/01 toyear/month/day.

function_daysFromDate(uintyear,uintmonth,uintday)publicpurereturns(uint_days)

_daysToDate

Calculateyear/month/day from the number of days_days since 1970/01/01 .

function_daysToDate(uint_days)publicpurereturns(uintyear,uintmonth,uintday)

timestampFromDate

Calculate thetimestamp toyear/month/day.

functiontimestampFromDate(uintyear,uintmonth,uintday)publicpurereturns(uinttimestamp)

timestampFromDateTime

Calculate thetimestamp toyear/month/dayhour:minute:second UTC.

functiontimestampFromDateTime(uintyear,uintmonth,uintday,uinthour,uintminute,uintsecond)publicpurereturns(uinttimestamp)

timestampToDate

Calculateyear/month/day fromtimestamp.

functiontimestampToDate(uinttimestamp)publicpurereturns(uintyear,uintmonth,uintday)

timestampToDateTime

Calculateyear/month/dayhour:minute:second fromtimestamp.

functiontimestampToDateTime(uinttimestamp)publicpurereturns(uintyear,uintmonth,uintday,uinthour,uintminute,uintsecond)

isLeapYear

Is the year specified bytimestamp a leap year?

function isLeapYear(uint timestamp) public pure returns (bool leapYear)

_isLeapYear

Is the specifiedyear (e.g. 2018) a leap year?

function _isLeapYear(uint year) public pure returns (bool leapYear)

isWeekDay

Is the day specified bytimestamp a weekday (Monday, ..., Friday)?

functionisWeekDay(uinttimestamp)publicpurereturns(boolweekDay)

isWeekEnd

Is the day specified bytimestamp a weekend (Saturday, Sunday)?

functionisWeekEnd(uinttimestamp)publicpurereturns(boolweekEnd)

getDaysInMonth

Return the day in the monthdaysInMonth for the month specified bytimestamp.

functiongetDaysInMonth(uinttimestamp)publicpurereturns(uintdaysInMonth)

_getDaysInMonth

Return the day in the monthdaysInMonth (1, ..., 31) for the month specified by theyear/month.

function_getDaysInMonth(uintyear,uintmonth)publicpurereturns(uintdaysInMonth)

getDayOfWeek

Return the day of the weekdayOfWeek (1 = Monday, ..., 7 = Sunday) for the date specified bytimestamp.

functiongetDayOfWeek(uinttimestamp)publicpurereturns(uintdayOfWeek)

getYear

Get theyear of the date specified bytimestamp.

functiongetYear(uinttimestamp)publicpurereturns(uintyear)

getMonth

Get themonth of the date specified bytimestamp.

functiongetMonth(uinttimestamp)publicpurereturns(uintmonth)

getDay

Get the day of the monthday (1, ..., 31) of the date specifiedtimestamp.

functiongetDay(uinttimestamp)publicpurereturns(uintday)

getHour

Get thehour of the date and time specified bytimestamp.

functiongetHour(uinttimestamp)publicpurereturns(uinthour)

getMinute

Get theminute of the date and time specified bytimestamp.

functiongetMinute(uinttimestamp)publicpurereturns(uintminute)

getSecond

Get thesecond of the date and time specified bytimestamp.

functiongetSecond(uinttimestamp)publicpurereturns(uintsecond)

addYears

Add_years years to the date and time specified bytimestamp.

Note that the resulting day of the month will be adjusted if it exceeds the valid number of days in the month. For example, if the original date is 2020/02/29 and an additional year is added to this date, the resulting date will be an invalid date of 2021/02/29. The resulting date is then adjusted to 2021/02/28.

functionaddYears(uinttimestamp,uint_years)publicpurereturns(uintnewTimestamp)

addMonths

Add_months months to the date and time specified bytimestamp.

Note that the resulting day of the month will be adjusted if it exceeds the valid number of days in the month. For example, if the original date is 2019/01/31 and an additional month is added to this date, the resulting date will be an invalid date of 2019/02/31. The resulting date is then adjusted to 2019/02/28.

functionaddMonths(uinttimestamp,uint_months)publicpurereturns(uintnewTimestamp)

addDays

Add_days days to the date and time specified bytimestamp.

functionaddDays(uinttimestamp,uint_days)publicpurereturns(uintnewTimestamp)

addHours

Add_hours hours to the date and time specified bytimestamp.

functionaddHours(uinttimestamp,uint_hours)publicpurereturns(uintnewTimestamp)

addMinutes

Add_minutes minutes to the date and time specified bytimestamp.

functionaddMinutes(uinttimestamp,uint_minutes)publicpurereturns(uintnewTimestamp)

addSeconds

Add_seconds seconds to the date and time specified bytimestamp.

functionaddSeconds(uinttimestamp,uint_seconds)publicpurereturns(uintnewTimestamp)

subYears

Subtract_years years from the date and time specified bytimestamp.

Note that the resulting day of the month will be adjusted if it exceeds the valid number of days in the month. For example, if the original date is 2020/02/29 and a year is subtracted from this date, the resulting date will be an invalid date of 2019/02/29. The resulting date is then adjusted to 2019/02/28.

functionsubYears(uinttimestamp,uint_years)publicpurereturns(uintnewTimestamp)

subMonths

Subtract_months months from the date and time specified bytimestamp.

Note that the resulting day of the month will be adjusted if it exceeds the valid number of days in the month. For example, if the original date is 2019/03/31 and a month is subtracted from this date, the resulting date will be an invalid date of 2019/02/31. The resulting date is then adjusted to 2019/02/28.

functionsubMonths(uinttimestamp,uint_months)publicpurereturns(uintnewTimestamp)

subDays

Subtract_days days from the date and time specified bytimestamp.

functionsubDays(uinttimestamp,uint_days)publicpurereturns(uintnewTimestamp)

subHours

Subtract_hours hours from the date and time specified bytimestamp.

functionsubHours(uinttimestamp,uint_hours)publicpurereturns(uintnewTimestamp)

subMinutes

Subtract_minutes minutes from the date and time specified bytimestamp.

functionsubMinutes(uinttimestamp,uint_minutes)publicpurereturns(uintnewTimestamp)

subSeconds

Subtract_seconds seconds from the date and time specified bytimestamp.

functionsubSeconds(uinttimestamp,uint_seconds)publicpurereturns(uintnewTimestamp)

diffYears

Calculate the number of years between the dates specified byfromTimeStamp andtoTimestamp.

Note that this calculation is computed asgetYear(toTimestamp) - getYear(fromTimestamp), rather that subtracting the years (since 1970/01/01) represented by both{to|from}Timestamp.

functiondiffYears(uintfromTimestamp,uinttoTimestamp)publicpurereturns(uint_years)

diffMonths

Calculate the number of months between the dates specified byfromTimeStamp andtoTimestamp.

Note that this calculation is computed asgetYear(toTimestamp) * 12 + getMonth(toTimestamp) - getYear(fromTimestamp) * 12 - getMonth(fromTimestamp), rather that subtracting the months (since 1970/01/01) represented by both{to|from}Timestamp.

functiondiffMonths(uintfromTimestamp,uinttoTimestamp)publicpurereturns(uint_months)

diffDays

Calculate the number of days between the dates specified byfromTimeStamp andtoTimestamp.

Note that this calculation is computed as(toTimestamp - fromTimestamp) / SECONDS_PER_DAY, rather that subtracting the days (since 1970/01/01) represented by both{to|from}Timestamp.

functiondiffDays(uintfromTimestamp,uinttoTimestamp)publicpurereturns(uint_days)

diffHours

Calculate the number of hours between the dates specified byfromTimeStamp andtoTimestamp.

Note that this calculation is computed as(toTimestamp - fromTimestamp) / SECONDS_PER_HOUR, rather that subtracting the hours (since 1970/01/01) represented by both{to|from}Timestamp.

functiondiffHours(uintfromTimestamp,uinttoTimestamp)publicpurereturns(uint_hours)

diffMinutes

Calculate the number of minutes between the dates specified byfromTimeStamp andtoTimestamp.

Note that this calculation is computed as(toTimestamp - fromTimestamp) / SECONDS_PER_MINUTE, rather that subtracting the minutes (since 1970/01/01) represented by both{to|from}Timestamp.

functiondiffMinutes(uintfromTimestamp,uinttoTimestamp)publicpurereturns(uint_minutes)

diffSeconds

Calculate the number of seconds between the dates specified byfromTimeStamp andtoTimestamp.

Note that this calculation is computed astoTimestamp - fromTimestamp.

functiondiffSeconds(uintfromTimestamp,uinttoTimestamp)publicpurereturns(uint_seconds)


Gas Cost

timestampToDateTime(...) Gas Cost

From executing the following function, the transaction gas cost is 24,693

>testDateTime.timestampToDateTime(1527120000)[2018,5,24,0,0,0]>testDateTime.timestampToDateTime.estimateGas(1527120000)24693

From Remix, the execution gas cost is 3,101 .


timestampFromDateTime(...) Gas Cost

From executing the following function, the transaction gas cost is 25,054

>testDateTime.timestampFromDateTime(2018,05,24,1,2,3)1527123723>testDateTime.timestampFromDateTime.estimateGas(2018,05,24,1,2,3)25054

From Remix, the execution gas cost is 2,566 .



Algorithm

The formulae to convert year/month/day hour:minute:second to a Unix timestamp and back use the algorithms fromConverting Between Julian Dates and Gregorian Calendar Dates.

Note that these algorithms depend on negative numbers, so Solidity unsigned integersuint are converted to signed integersint to compute the date conversions and the results are converted back touint for general use.


Converting YYYYMMDD to Unix Timestamp

The Fortran algorithm follows:

    INTEGER FUNCTION JD (YEAR,MONTH,DAY)CC---COMPUTES THE JULIAN DATE (JD) GIVEN A GREGORIAN CALENDARC   DATE (YEAR,MONTH,DAY).C    INTEGER YEAR,MONTH,DAY,I,J,KC    I= YEAR    J= MONTH    K= DAYC    JD= K-32075+1461*(I+4800+(J-14)/12)/4+367*(J-2-(J-14)/12*12)   2    /12-3*((I+4900+(J-14)/12)/100)/4C    RETURN    END

Translating this formula, and subtracting an offset (2,440,588) so 1970/01/01 is day 0:

days = day     - 32075     + 1461 * (year + 4800 + (month - 14) / 12) / 4     + 367 * (month - 2 - (month - 14) / 12 * 12) / 12     - 3 * ((year + 4900 + (month - 14) / 12) / 100) / 4     - offset

Converting Unix Timestamp To YYYYMMDD

The Fortran algorithm follows:

    SUBROUTINE GDATE (JD, YEAR,MONTH,DAY)CC---COMPUTES THE GREGORIAN CALENDAR DATE (YEAR,MONTH,DAY)C   GIVEN THE JULIAN DATE (JD).C    INTEGER JD,YEAR,MONTH,DAY,I,J,KC    L= JD+68569    N= 4*L/146097    L= L-(146097*N+3)/4    I= 4000*(L+1)/1461001    L= L-1461*I/4+31    J= 80*L/2447    K= L-2447*J/80    L= J/11    J= J+2-12*L    I= 100*(N-49)+I+LC    YEAR= I    MONTH= J    DAY= KC    RETURN    END

Translating this formula and adding an offset (2,440,588) so 1970/01/01 is day 0:

int L = days + 68569 + offsetint N = 4 * L / 146097L = L - (146097 * N + 3) / 4year = 4000 * (L + 1) / 1461001L = L - 1461 * year / 4 + 31month = 80 * L / 2447dd = L - 2447 * month / 80L = month / 11month = month + 2 - 12 * Lyear = 100 * (N - 49) + year + L


Testing

Details of the testing environment can be found intest.

The DateTime library calculations have been tested for the date range 1970/01/01 to 2345/12/01 for periodically sampled dates.

The following functions were tested using the scripttest/01_test1.sh with the summary results savedintest/test1results.txt and the detailed output saved intest/test1output.txt:

  • Deploycontracts/BokkyPooBahsDateTimeLibrary.sol library
  • Deploycontracts/TestDateTime.sol contract
  • TestisLeapYear(...)
  • Test_isLeapYear(...)
  • TestisWeekDay(...)
  • TestisWeekEnd(...)
  • TestgetDaysInMonth(...)
  • Test_getDaysInMonth(...)
  • TestgetDayOfWeek(...)
  • Testget{Year|Month|Day|Hour|Minute|Second}(...)
  • Testadd{Years|Months|Days|Hours|Minutes|Seconds}(...)
  • Testsub{Years|Months|Days|Hours|Minutes|Seconds}(...)
  • Testdiff{Years|Months|Days|Hours|Minutes|Seconds}(...)
  • For a range of Unix timestamps from 1970/01/01 to 2345/12/21
    • Generate the year/month/day hour/minute/second from the Unix timestamp usingtimestampToDateTime(...)
    • Generate the Unix timestamp from the calculated year/month/day hour/minute/second usingtimestampFromDateTime(...)
    • Compare the year/month/day hour/minute/second to the JavaScriptDate calculation


References

A copy of the webpage with the algorithmConverting Between Julian Dates and Gregorian Calendar Dates has been saved todocs/ConvertingBetweenJulianDatesAndGregorianCalendarDates.pdf as some people have had difficulty accessing this page.



(c) BokkyPooBah / Bok Consulting Pty Ltd - Jun 2 2018.GNU Lesser General Public License 3.0

About

Solidity Gas-Efficient DateTime Library

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Solidity43.3%
  • Shell30.6%
  • JavaScript26.1%

[8]ページ先頭

©2009-2025 Movatter.jp