Skip to content

Instantly share code, notes, and snippets.

@19h
Created February 20, 2013 21:24
Show Gist options
  • Select an option

  • Save 19h/4999764 to your computer and use it in GitHub Desktop.

Select an option

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.
/*
_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