Skip to content

Commit

Permalink
Merge d0d4ce1 into 62ba303
Browse files Browse the repository at this point in the history
  • Loading branch information
seanh committed May 9, 2016
2 parents 62ba303 + d0d4ce1 commit 81e5cfe
Show file tree
Hide file tree
Showing 2 changed files with 205 additions and 55 deletions.
96 changes: 79 additions & 17 deletions h/static/scripts/test/time-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,20 @@ var month = day * 30;
var year = day * 365;

var FIXTURES_TO_FUZZY_STRING = [
[10, 'moments ago'],
[29, 'moments ago'],
[49, '49 seconds ago'],
[minute + 5, 'a minute ago'],
[3 * minute + 5, '3 minutes ago'],
[4 * hour, '4 hours ago'],
[27 * hour, 'a day ago'],
[3 * day + 30 * minute, '3 days ago'],
[6 * month + 2 * day, '6 months ago'],
[1 * year, 'one year ago'],
[1 * year + 2 * month, 'one year ago'],
[2 * year, '2 years ago'],
[8 * year, '8 years ago']
[10, 'Just now'],
[29, 'Just now'],
[49, '49 sec'],
[minute + 5, '1 min'],
[3 * minute + 5, '3 min'],
[hour, '1 hr'],
[4 * hour, '4 hr'],
[27 * hour, '01 Jan'],
[3 * day + 30 * minute, '01 Jan'],
[6 * month + 2 * day, '01 Jan'],
[1 * year, '01 Jan 1970'],
[1 * year + 2 * month, '01 Jan 1970'],
[2 * year, '01 Jan 1970'],
[8 * year, '01 Jan 1970']
];

var FIXTURES_NEXT_FUZZY_UPDATE = [
Expand All @@ -31,10 +32,10 @@ var FIXTURES_NEXT_FUZZY_UPDATE = [
[minute + 5, minute],
[3 * minute + 5, minute],
[4 * hour, hour],
[27 * hour, day],
[3 * day + 30 * minute, day],
[6 * month + 2 * day, 24 * day], // longer times are not supported
[8 * year, 24 * day] // by setTimout
[27 * hour, null],
[3 * day + 30 * minute, null],
[6 * month + 2 * day, null],
[8 * year, null]
];

describe('time', function () {
Expand All @@ -50,6 +51,34 @@ describe('time', function () {
});

describe('.toFuzzyString', function () {

var originalIntl;

before(function () {

// Intl isn't implemented in PhantomJS so we have to mock it in these
// tests. The mock version returns the results we'd expect from the real
// one for the test values that we use.
originalIntl = window.Intl;
window.Intl = {
DateTimeFormat: function () {
return {
format: function () {
if (new Date().getYear() === 70) {
return '01 Jan';
} else {
return '01 Jan 1970';
}
}
};
}
};
});

after(function () {
window.Intl = originalIntl;
});

it('Handles empty dates', function () {
var t = null;
var expect = '';
Expand All @@ -70,6 +99,27 @@ describe('time', function () {
it('creates correct fuzzy string for fixture ' + i,
testFixture(f));
}

it('falls back to simple strings for >24hrs ago', function () {
// If window.Intl is not available then the date formatting for dates
// more than one day ago falls back to a simple date string.
window.Intl = undefined;
var d = new Date();
sandbox.clock.tick(day * 2 * 1000);

assert.equal(time.toFuzzyString(d), 'Thu Jan 01 1970');
});

it('falls back to simple strings for >1yr ago', function () {
// If window.Intl is not available then the date formatting for dates
// more than one year ago falls back to a simple date string.
window.Intl = undefined;
var d = new Date();
sandbox.clock.tick(year * 2 * 1000);

assert.equal(time.toFuzzyString(d), 'Thu Jan 01 1970');
});

});

describe('.decayingInterval', function () {
Expand Down Expand Up @@ -105,6 +155,18 @@ describe('time', function () {
sandbox.clock.tick(minute * 1000);
assert.notCalled(callback);
});

it('does not set a timeout for dates > 24hrs ago', function() {
var date = new Date();
var ONE_DAY = day * 1000;
sandbox.clock.tick(10 * ONE_DAY);
var callback = sandbox.stub();

time.decayingInterval(date, callback);
sandbox.clock.tick(ONE_DAY * 2);

assert.notCalled(callback);
});
});

describe('.nextFuzzyUpdate', function () {
Expand Down
164 changes: 126 additions & 38 deletions h/static/scripts/time.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,53 +2,144 @@

var minute = 60;
var hour = minute * 60;
var day = hour * 24;
var month = day * 30;
var year = day * 365;

function lessThanThirtySecondsAgo(date, now) {
return ((now - date) < 30 * 1000);
}

function lessThanOneMinuteAgo(date, now) {
return ((now - date) < 60 * 1000);
}

function lessThanOneHourAgo(date, now) {
return ((now - date) < (60 * 60 * 1000));
}

function lessThanOneDayAgo(date, now) {
return ((now - date) < (24 * 60 * 60 * 1000));
}

function thisYear(date, now) {
return date.getFullYear() === now.getFullYear();
}

function delta(date, now) {
return Math.round((now - date) / 1000);
}

function nSec(date, now) {
return '{} sec'.replace('{}', Math.floor(delta(date, now)));
}

function nMin(date, now) {
return '{} min'.replace('{}', Math.floor(delta(date, now) / minute));
}

function nHr(date, now) {
return '{} hr'.replace('{}', Math.floor(delta(date, now) / hour));
}

// Cached DateTimeFormat instances,
// because instantiating a DateTimeFormat is expensive.
var formatters = {};

/**
* Efficiently return `date` formatted with `options`.
*
* This is a wrapper for Intl.DateTimeFormat.format() that caches
* DateTimeFormat instances because they're expensive to create.
* Calling Date.toLocaleDateString() lots of times is also expensive in some
* browsers as it appears to create a new formatter for each call.
*
* @returns {string}
*
*/
function format(date, options) {
if (typeof Intl !== 'undefined' && Intl.DateTimeFormat) {
var key = JSON.stringify(options);
var formatter = formatters[key];

if (!formatter) {
formatter = formatters[key] = new Intl.DateTimeFormat(undefined,
options);
}

return formatter.format(date);
} else {
// IE < 11, Safari <= 9.0.
return date.toDateString();
}
}

function dayAndMonth(date) {
return format(date, {
month: 'short',
day: '2-digit',
});
}

function dayAndMonthAndYear(date) {
return format(date, {
day: '2-digit',
month: 'short',
year: 'numeric'
});
}

var BREAKPOINTS = [
[30, 'moments ago', 1],
[minute, '{} seconds ago', 1],
[2 * minute, 'a minute ago', minute],
[hour, '{} minutes ago', minute],
[2 * hour, 'an hour ago', hour],
[day, '{} hours ago', hour],
[2 * day, 'a day ago', day],
[month, '{} days ago', day],
[year, '{} months ago', month],
[2 * year, 'one year ago', year],
[Infinity, '{} years ago', year]
{
test: lessThanThirtySecondsAgo,
format: function () {return 'Just now';},
nextUpdate: 1
},
{
test: lessThanOneMinuteAgo,
format: nSec,
nextUpdate: 1
},
{
test: lessThanOneHourAgo,
format: nMin,
nextUpdate: minute
},
{
test: lessThanOneDayAgo,
format: nHr,
nextUpdate: hour
},
{
test: thisYear,
format: dayAndMonth,
nextUpdate: null
},
{
test: function () {return true;},
format: dayAndMonthAndYear,
nextUpdate: null
}
];

function getBreakpoint(date) {
var delta = Math.round((new Date() - new Date(date)) / 1000);
function getBreakpoint(date, now) {
var breakpoint;

for (var i = 0; i < BREAKPOINTS.length; i++) {
if (BREAKPOINTS[i][0] > delta) {
breakpoint = BREAKPOINTS[i];
break;
breakpoint = BREAKPOINTS[i];
if (breakpoint.test(date, now)) {
return breakpoint;
}
}

return {
delta: delta,
breakpoint: breakpoint,
};
}

function nextFuzzyUpdate(date) {
if (!date) {
return null;
}

var breakpoint = getBreakpoint(date).breakpoint;
if (!breakpoint) {
var secs = getBreakpoint(date, new Date()).nextUpdate;

if (secs === null) {
return null;
}

var secs = breakpoint[2];

// We don't want to refresh anything more often than 5 seconds
secs = Math.max(secs, 5);

Expand All @@ -73,6 +164,9 @@ function decayingInterval(date, callback) {
var timer;
var update = function () {
var fuzzyUpdate = nextFuzzyUpdate(date);
if (fuzzyUpdate === null) {
return;
}
var nextUpdate = (1000 * fuzzyUpdate) + 500;
timer = setTimeout(function () {
callback(date);
Expand All @@ -96,15 +190,9 @@ function toFuzzyString(date) {
if (!date) {
return '';
}
var breakpointInfo = getBreakpoint(date);
var breakpoint = breakpointInfo.breakpoint;
var delta = breakpointInfo.delta;
if (!breakpoint) {
return '';
}
var template = breakpoint[1];
var resolution = breakpoint[2];
return template.replace('{}', String(Math.floor(delta / resolution)));
var now = new Date();

return getBreakpoint(date, now).format(new Date(date), now);
}

module.exports = {
Expand Down

0 comments on commit 81e5cfe

Please sign in to comment.