Created
February 20, 2013 21:24
-
-
Save 19h/4999764 to your computer and use it in GitHub Desktop.
An implementation of the ECMA-262 (sector 15.9) date specification and custom modification improving the overall algorithms' precision.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| _date | |
| by Kenan Sulayman in 2013. | |
| -is-browser-compatible- | |
| */ | |
| log = console.log; | |
| DaysPerWeek = 7; | |
| HoursPerDay = 24; | |
| MinutesPerHour = 60; | |
| MinutesPerDay = MinutesPerHour * HoursPerDay; | |
| SecondsPerMinute = 60; | |
| SecondsPerHour = SecondsPerMinute * MinutesPerHour; | |
| SeondsPerDay = SecondsPerHour * 24; | |
| msPerSecond = 1000; | |
| msPerMinute = msPerSecond * SecondsPerMinute; | |
| msPerHour = msPerMinute * MinutesPerHour; | |
| msPerDay = msPerHour * HoursPerDay; | |
| // Date: Year Number | |
| _DIY = function (y) { return ( y % 4 != 0 ) ? 365 : ( y % 100 != 0 ) ? 366 : ( y % 400 != 0 ) ? 365 : 366; } | |
| // This is just wrong. | |
| //_DFY = function (y) { return _DIY(y) * (y-1970) + Math.floor((y/1969)/4) - Math.floor((y/1901)/100) + Math.floor((y-1601)/400) } | |
| // Not static, but yields right values | |
| _DFY = function (y) { for ( i = 1, j = 0; i <= y - 1970; j += _DIY(i), ++i ); return j; } | |
| _TFY = function (y) { return msPerDay * _DFY(y) } | |
| _YFT = function (t) { for ( var i = 0; i <= +Infinity; ++i ) if ( _TFY ( i ) > t ) return i - 1 } | |
| _ILY = function (t) { return _DIY( _YFT( t ) ) == 365 ? false : true } | |
| // Date: Month Number | |
| _MFT = function (t) { | |
| var ILY = _ILY(t); | |
| var DWY = _DWY(t); | |
| return ( 0 <= DWY && DWY < 31 ) ? 0 : | |
| ( 31 <= DWY && DWY < ( 59 + ILY ) ) ? 1 : | |
| ( ( 59 + ILY ) <= DWY && DWY < ( 90 + ILY ) ) ? 2 : | |
| ( ( 90 + ILY ) <= DWY && DWY < ( 120 + ILY ) ) ? 3 : | |
| ( ( 120 + ILY ) <= DWY && DWY < ( 151 + ILY ) ) ? 4 : | |
| ( ( 151 + ILY ) <= DWY && DWY < ( 181 + ILY ) ) ? 5 : | |
| ( ( 181 + ILY ) <= DWY && DWY < ( 212 + ILY ) ) ? 6 : | |
| ( ( 212 + ILY ) <= DWY && DWY < ( 243 + ILY ) ) ? 7 : | |
| ( ( 243 + ILY ) <= DWY && DWY < ( 273 + ILY ) ) ? 8 : | |
| ( ( 273 + ILY ) <= DWY && DWY < ( 304 + ILY ) ) ? 9 : | |
| ( ( 304 + ILY ) <= DWY && DWY < ( 334 + ILY ) ) ? 10 : | |
| ( ( 334 + ILY ) <= DWY && DWY <= ( 365 + ILY ) ) ? 11 : 0; | |
| } | |
| _DFT = function (t) { | |
| var MFT = _MFT(t); | |
| var DWY = _DWY(t); | |
| return ( MFT == 0 ) ? DWY + 1 : | |
| ( MFT == 1 ) ? DWY - 30 : | |
| ( MFT == 2 ) ? DWY - 58 : | |
| ( MFT == 3 ) ? DWY - 89 : | |
| ( MFT == 4 ) ? DWY - 119 : | |
| ( MFT == 5 ) ? DWY - 150 : | |
| ( MFT == 6 ) ? DWY - 180 : | |
| ( MFT == 7 ) ? DWY - 211 : | |
| ( MFT == 8 ) ? DWY - 242 : | |
| ( MFT == 9 ) ? DWY - 272 : | |
| ( MFT == 10 ) ? DWY - 313 : // ---| | |
| ( MFT == 11 ) ? DWY - 343 : 0 // ---|---> typo in specification? specd' value is yielding wrong values | |
| } | |
| // Date: Day Number | |
| _DWY = function (t) { return _DAY( t ) - _DFY( _YFT( t ) ) } | |
| _DAY = function (t) { return ((t / msPerDay) - ((t / msPerDay) % 1)) } | |
| _TWD = function (t) { return t % msPerDay } | |
| _WD = function (t, _fromSunday) { var _t=( _DAY(t) + 4 ) % 7; return !_fromSunday ? _t == 0 ? 7 : ( _t - 1 ) : _t } | |
| // Day: Partials, Hour, Minute, Second, Millisecond | |
| _HFT = function (t) { return Math.floor( t / msPerHour ) } | |
| // Tests | |
| tests = 2; | |
| forceFirst = 1; | |
| tests = forceFirst = 0; | |
| _t = +new Date; | |
| t = _t + (new Date).getTimezoneOffset() * -60 * 1E3; | |
| //t = 1241621986000; | |
| if ( tests == 1 || forceFirst ) { | |
| log("=== Dump ===") | |
| log("[" + _TWD(t) + "ms of " + msPerDay + "ms ( " + ((_TWD(t)/msPerDay)*100).toPrecision(6) + "% )]",(_HFT(t)/24/365).toPrecision(5) + "y",_ILY(t),_DFT(t),_DWY(t)) | |
| log("\n=== Approx. Time ====") | |
| log( | |
| ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'][_WD(t)] + ", " | |
| + ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'][_MFT(t)] + " " | |
| + (function (a){return a==1?a+"st":a==2?a+"nd":a+"th"})(_DFT(t)) + ", " | |
| + _YFT(t) | |
| + ".\n" | |
| ) | |
| } | |
| if ( tests == 2 ) { // test every function | |
| reqUT = [ '_YFT', '_ILY', '_MFT', | |
| '_DFT', '_DWY', '_DAY', | |
| '_TWD', '_WD', '_HFT' ]; | |
| reqY = [ '_DIY', '_DFY', '_TFY' ] // REQ: t ==YFT==> reqY.k | |
| console.log( "=== reqUT ===" ); | |
| for ( f in reqUT ) console.log( reqUT[f] + ": " + ( new Function ("return " + reqUT[f] + "(" + t + ");") )() ); | |
| console.log( "\n=== reqY ===" ); | |
| for ( f in reqY ) console.log( reqY[f] + ": " + ( new Function ("return " + reqY[f] + "(" + _YFT(t) + ");") )() ); | |
| // Dummys | |
| Array.prototype.f = function () { return this[0]; }; | |
| Array.prototype.l = function () { return this[this.length - 1] }; | |
| Array.prototype.p = function () { return !!arguments[0] && (this[this.length] = arguments[0]); }; | |
| console.log( "\n=== Tests ===" ); | |
| // real - approx time | |
| // math | |
| z = []; | |
| z.p(t - _TFY(_YFT(t))); // ms offset | |
| z.p(z.l()/1E3); // mso >> s | |
| z.p(z.l()/SecondsPerHour); // Hours | |
| z.p(z.l()/HoursPerDay); // Days | |
| z.p(z.l()/DaysPerWeek); // Weeks | |
| // format | |
| _z = 0, __z = "\t=== Offset t <-> T[Y[t]_d0] ===\n"; z__ = []; | |
| z_ = [ "ms", "s", "h", "d", "w" ].map(function (v) { return z[_z++] + v; }).forEach(function (v) { z__.push("\t\t" + v); }); | |
| console.log(__z + z__.join("\n")); | |
| } | |
| if ( false ) { // generate list of years, enabling "no-math" calculations | |
| z = []; for ( i = 1970, j = 0; i <= 2500; ++i ) z.push( j = ( _DIY(i) + j ) ); | |
| console.log(JSON.stringify(z)) | |
| } | |
| // TODO: obtain offset ( summertime, et al. ) | |
| //t += 3600000; | |
| /* | |
| CUSTOM REIMPLEMENTATION OF THE IN ECMA-262 (SECTOR 15.9) SPECIFIED FUNCTIONS AS LOCAL VARIABLES. | |
| */ | |
| if ( true ) { | |
| // t is time in ms | |
| // t/1E3 / 86400 is d | |
| // Offset ( t, UT_0 ) is d | |
| // Map d => Year. | |
| z = ( t / 1E3 ) / 86400; | |
| // construct incremental days per year map | |
| for ( _DPY_INCR = [], i = 1970, j = 0; i < 1970 + z; (_DPY_INCR.push( j = ( _DIY(i) + j ) )), ++i ); | |
| // approximate index in map of static DPY | |
| i = ((z / 365) - ((z / 365) % 1)) - 1; | |
| // iterate until match is found | |
| while ( _DPY_INCR [ i++ + 1 ] < z ); | |
| // match found; the year is 1970 + i | |
| // get offset to amount of days equal to floor of year 1970 + q | |
| // 1 is added as this is the index of the day in a month to which it respectively maps | |
| o = z - _DPY_INCR [ i - 1 ] + 1; | |
| // identify leap from map by the comparision of the amount of days offset from year k - 1 to year k and 356 | |
| l = ( _DPY_INCR [ i ] - _DPY_INCR [ i - 1 ] ) == 365; | |
| // length of months | |
| // element 0 is a hypothetical shift enabling easier calculation | |
| // however, the shift eases the determination of the amount of days until the beginning of a month | |
| lm = [ 0, 31, 59 + l, 90 + l, 120 + l, 151 + l, 181 + l, 212 + l, 243 + l, 273 + l, 304 + l, 334 + l, 365 + l ]; | |
| // get month as offset of day from floor of respective year | |
| m = ( 0 <= o && o < 31 ) ? 0 : | |
| ( 31 <= o && o < ( 59 + l ) ) ? 1 : | |
| ( ( 59 + l ) <= o && o < ( 90 + l ) ) ? 2 : | |
| ( ( 90 + l ) <= o && o < ( 120 + l ) ) ? 3 : | |
| ( ( 120 + l ) <= o && o < ( 151 + l ) ) ? 4 : | |
| ( ( 151 + l ) <= o && o < ( 181 + l ) ) ? 5 : | |
| ( ( 181 + l ) <= o && o < ( 212 + l ) ) ? 6 : | |
| ( ( 212 + l ) <= o && o < ( 243 + l ) ) ? 7 : | |
| ( ( 243 + l ) <= o && o < ( 273 + l ) ) ? 8 : | |
| ( ( 273 + l ) <= o && o < ( 304 + l ) ) ? 9 : | |
| ( ( 304 + l ) <= o && o < ( 334 + l ) ) ? 10 : | |
| ( ( 334 + l ) <= o && o <= ( 365 + l ) ) ? 11 : 0; | |
| // get month as word | |
| m_ = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'][m]; | |
| // get day in month | |
| // || for ( e = 0; lm [ e ] <= o; ++e ); | |
| // || == m + 1 | |
| e = o - lm [ m ]; | |
| // get weekday within a map from Monday to Sunday | |
| wd = ( ( ((t / msPerDay) - ((t / msPerDay) % 1)) + 4 ) % 7 ); | |
| wd = wd == 0 ? 6 : wd - 1; // 0, 1, .. 6 => 1, 2, .. 7 | |
| // get weekday as word | |
| wd_ = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'][ wd ]; | |
| // intra-day offset | |
| id = o - ( o - (o % 1) ); | |
| // intra day seconds | |
| is = 86400 * id; | |
| // intra day hours | |
| ih = is / 3600; // hours, when floored | |
| // intra hour seconds | |
| ih_ = is % 3600; | |
| // intra hour minutes | |
| im = ih_ / 60; | |
| // intra minute seconds | |
| // coefficient multiplied by 60 () | |
| im_ = ih_ - 60 * ((ih_ / 60) - ((ih_ / 60) % 1)); | |
| console.log( // EC: Error Correction | |
| "Using " + _t + "ms, EC " + ( (new Date).getTimezoneOffset() * -60 * 1E3 ) + "ms.\n" | |
| ) | |
| console.log( | |
| wd_ + ", " + | |
| m_ + " " + | |
| (e - (e % 1)) + | |
| ( ( e < 2 ) ? "st" : ( e < 3 ) ? "nd" : ( e < 4 ) ? "rd" : "th" ) + | |
| ", " + ( 1970 + i ) + | |
| "." | |
| ) | |
| _ct = [Math.floor(ih), im, im_].map(function (v) { // floor & fill | |
| v = v - (v % 1); | |
| return v < 10 ? "0" + v : v; | |
| }); | |
| console.log(_ct.join(":")); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment