Merge pull request #6333 from LenaBarinova/fix/streak-seems-broken-because-of-timezone
Fixed streak and completion dates to be showed using users timezone
This commit is contained in:
@ -18,6 +18,7 @@
|
|||||||
"bootstrap": "~3.3.4",
|
"bootstrap": "~3.3.4",
|
||||||
"font-awesome": "~4.3.0",
|
"font-awesome": "~4.3.0",
|
||||||
"moment": "~2.10.2",
|
"moment": "~2.10.2",
|
||||||
|
"moment-timezone": "~0.5.0",
|
||||||
"jshint": "~2.9.0",
|
"jshint": "~2.9.0",
|
||||||
"lightbox2": "~2.8.1",
|
"lightbox2": "~2.8.1",
|
||||||
"rxjs": "~4.0.6",
|
"rxjs": "~4.0.6",
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
window.common = (function(global) {
|
window.common = (function(global) {
|
||||||
const {
|
const {
|
||||||
$,
|
$,
|
||||||
|
moment,
|
||||||
ga = (() => {}),
|
ga = (() => {}),
|
||||||
common = { init: [] }
|
common = { init: [] }
|
||||||
} = global;
|
} = global;
|
||||||
@ -51,7 +52,8 @@ window.common = (function(global) {
|
|||||||
name: common.challengeName,
|
name: common.challengeName,
|
||||||
completedWith: didCompleteWith,
|
completedWith: didCompleteWith,
|
||||||
challengeType: common.challengeType,
|
challengeType: common.challengeType,
|
||||||
solution
|
solution,
|
||||||
|
timezone: moment.tz.guess()
|
||||||
};
|
};
|
||||||
|
|
||||||
$.post('/completed-challenge/', data, function(res) {
|
$.post('/completed-challenge/', data, function(res) {
|
||||||
|
@ -173,6 +173,9 @@
|
|||||||
},
|
},
|
||||||
"tshirtVote": {
|
"tshirtVote": {
|
||||||
"type": "number"
|
"type": "number"
|
||||||
|
},
|
||||||
|
"timezone": {
|
||||||
|
"type": "string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"validations": [],
|
"validations": [],
|
||||||
|
@ -99,6 +99,7 @@ var paths = {
|
|||||||
'public/bower_components/bootstrap/dist/js/bootstrap.min.js',
|
'public/bower_components/bootstrap/dist/js/bootstrap.min.js',
|
||||||
'public/bower_components/d3/d3.min.js',
|
'public/bower_components/d3/d3.min.js',
|
||||||
'public/bower_components/moment/min/moment.min.js',
|
'public/bower_components/moment/min/moment.min.js',
|
||||||
|
'public/bower_components/moment-timezone/builds/moment-timezone-with-data.min.js',
|
||||||
'public/bower_components/mousetrap/mousetrap.min.js',
|
'public/bower_components/mousetrap/mousetrap.min.js',
|
||||||
'public/bower_components/lightbox2/dist/js/lightbox.min.js',
|
'public/bower_components/lightbox2/dist/js/lightbox.min.js',
|
||||||
'public/bower_components/rxjs/dist/rx.all.min.js'
|
'public/bower_components/rxjs/dist/rx.all.min.js'
|
||||||
|
@ -94,6 +94,7 @@
|
|||||||
"merge-stream": "^1.0.0",
|
"merge-stream": "^1.0.0",
|
||||||
"method-override": "^2.3.0",
|
"method-override": "^2.3.0",
|
||||||
"moment": "^2.10.2",
|
"moment": "^2.10.2",
|
||||||
|
"moment-timezone": "^0.5.0",
|
||||||
"mongodb": "^2.0.33",
|
"mongodb": "^2.0.33",
|
||||||
"morgan": "^1.6.1",
|
"morgan": "^1.6.1",
|
||||||
"node-uuid": "^1.4.3",
|
"node-uuid": "^1.4.3",
|
||||||
|
@ -505,7 +505,8 @@ module.exports = function(app) {
|
|||||||
id,
|
id,
|
||||||
name,
|
name,
|
||||||
challengeType,
|
challengeType,
|
||||||
solution
|
solution,
|
||||||
|
timezone
|
||||||
} = req.body;
|
} = req.body;
|
||||||
|
|
||||||
const { alreadyCompleted } = updateUserProgress(
|
const { alreadyCompleted } = updateUserProgress(
|
||||||
@ -521,6 +522,10 @@ module.exports = function(app) {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (timezone && (!req.user.timezone || req.user.timezone != timezone)) {
|
||||||
|
req.user.timezone = timezone;
|
||||||
|
}
|
||||||
|
|
||||||
let user = req.user;
|
let user = req.user;
|
||||||
saveUser(req.user)
|
saveUser(req.user)
|
||||||
.subscribe(
|
.subscribe(
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import dedent from 'dedent';
|
import dedent from 'dedent';
|
||||||
import moment from 'moment';
|
import moment from 'moment-timezone';
|
||||||
import { Observable } from 'rx';
|
import { Observable } from 'rx';
|
||||||
import debugFactory from 'debug';
|
import debugFactory from 'debug';
|
||||||
|
|
||||||
@ -38,6 +38,8 @@ const certText = {
|
|||||||
[certTypes.fullStack]: 'Full Stack Certified'
|
[certTypes.fullStack]: 'Full Stack Certified'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const dateFormat = 'MMM DD, YYYY';
|
||||||
|
|
||||||
function replaceScriptTags(value) {
|
function replaceScriptTags(value) {
|
||||||
return value
|
return value
|
||||||
.replace(/<script>/gi, 'fccss')
|
.replace(/<script>/gi, 'fccss')
|
||||||
@ -183,6 +185,12 @@ module.exports = function(app) {
|
|||||||
}
|
}
|
||||||
profileUser = profileUser.toJSON();
|
profileUser = profileUser.toJSON();
|
||||||
|
|
||||||
|
// timezone of signed-in account
|
||||||
|
// to show all date related components
|
||||||
|
// using signed-in account's timezone
|
||||||
|
// not of the profile she is viewing
|
||||||
|
const timezone = req.user && req.user.timezone ? req.user.timezone : 'UTC';
|
||||||
|
|
||||||
var cals = profileUser
|
var cals = profileUser
|
||||||
.progressTimestamps
|
.progressTimestamps
|
||||||
.map(objOrNum => {
|
.map(objOrNum => {
|
||||||
@ -192,8 +200,8 @@ module.exports = function(app) {
|
|||||||
})
|
})
|
||||||
.sort();
|
.sort();
|
||||||
|
|
||||||
profileUser.currentStreak = calcCurrentStreak(cals);
|
profileUser.currentStreak = calcCurrentStreak(cals, timezone);
|
||||||
profileUser.longestStreak = calcLongestStreak(cals);
|
profileUser.longestStreak = calcLongestStreak(cals, timezone);
|
||||||
|
|
||||||
const data = profileUser
|
const data = profileUser
|
||||||
.progressTimestamps
|
.progressTimestamps
|
||||||
@ -223,9 +231,20 @@ module.exports = function(app) {
|
|||||||
+challenge.challengeType === 4;
|
+challenge.challengeType === 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
const completedChallenges = profileUser.completedChallenges.filter(
|
const completedChallenges = profileUser.completedChallenges
|
||||||
({ name }) => typeof name === 'string'
|
.filter(({ name }) => typeof name === 'string')
|
||||||
);
|
.map(challenge => {
|
||||||
|
challenge = { ...challenge };
|
||||||
|
if (challenge.completedDate) {
|
||||||
|
challenge.completedDate =
|
||||||
|
moment.tz(challenge.completedDate, timezone).format(dateFormat);
|
||||||
|
}
|
||||||
|
if (challenge.lastUpdated) {
|
||||||
|
challenge.lastUpdated =
|
||||||
|
moment.tz(challenge.lastUpdated, timezone).format(dateFormat);
|
||||||
|
}
|
||||||
|
return challenge;
|
||||||
|
});
|
||||||
|
|
||||||
const projects = completedChallenges.filter(filterProjects);
|
const projects = completedChallenges.filter(filterProjects);
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import moment from 'moment';
|
import moment from 'moment-timezone';
|
||||||
|
|
||||||
// day count between two epochs (inclusive)
|
// day count between two epochs (inclusive)
|
||||||
export function dayCount([head, tail]) {
|
export function dayCount([head, tail], timezone = 'UTC') {
|
||||||
return Math.ceil(
|
return Math.ceil(
|
||||||
moment(moment(head).endOf('day')).diff(
|
moment(moment(head).tz(timezone).endOf('day')).tz(timezone).diff(
|
||||||
moment(tail).startOf('day'),
|
moment(tail).tz(timezone).startOf('day'),
|
||||||
'days',
|
'days',
|
||||||
true)
|
true)
|
||||||
);
|
);
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import moment from 'moment';
|
import moment from 'moment-timezone';
|
||||||
import { dayCount } from '../utils/date-utils';
|
import { dayCount } from '../utils/date-utils';
|
||||||
|
|
||||||
const daysBetween = 1.5;
|
const daysBetween = 1.5;
|
||||||
|
|
||||||
export function calcCurrentStreak(cals) {
|
export function calcCurrentStreak(cals, timezone = 'UTC') {
|
||||||
const revCals = cals.slice().reverse();
|
const revCals = cals.slice().reverse();
|
||||||
|
|
||||||
if (dayCount([moment(), revCals[0]]) > daysBetween) {
|
if (dayCount([moment().tz(timezone), revCals[0]], timezone) > daysBetween) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ export function calcCurrentStreak(cals) {
|
|||||||
const before = revCals[index === 0 ? 0 : index - 1];
|
const before = revCals[index === 0 ? 0 : index - 1];
|
||||||
if (
|
if (
|
||||||
!streakBroken &&
|
!streakBroken &&
|
||||||
moment(before).diff(cal, 'days', true) < daysBetween
|
moment(before).tz(timezone).diff(cal, 'days', true) < daysBetween
|
||||||
) {
|
) {
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
@ -25,22 +25,22 @@ export function calcCurrentStreak(cals) {
|
|||||||
}, 0);
|
}, 0);
|
||||||
|
|
||||||
const lastTimestamp = revCals[lastDayInStreak];
|
const lastTimestamp = revCals[lastDayInStreak];
|
||||||
return dayCount([moment(), lastTimestamp]);
|
return dayCount([moment().tz(timezone), lastTimestamp], timezone);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function calcLongestStreak(cals) {
|
export function calcLongestStreak(cals, timezone = 'UTC') {
|
||||||
let tail = cals[0];
|
let tail = cals[0];
|
||||||
const longest = cals.reduce((longest, head, index) => {
|
const longest = cals.reduce((longest, head, index) => {
|
||||||
const last = cals[index === 0 ? 0 : index - 1];
|
const last = cals[index === 0 ? 0 : index - 1];
|
||||||
// is streak broken
|
// is streak broken
|
||||||
if (moment(head).diff(last, 'days', true) > daysBetween) {
|
if (moment(head).tz(timezone).diff(last, 'days', true) > daysBetween) {
|
||||||
tail = head;
|
tail = head;
|
||||||
}
|
}
|
||||||
if (dayCount(longest) < dayCount([head, tail])) {
|
if (dayCount(longest, timezone) < dayCount([head, tail], timezone)) {
|
||||||
return [head, tail];
|
return [head, tail];
|
||||||
}
|
}
|
||||||
return longest;
|
return longest;
|
||||||
}, [cals[0], cals[0]]);
|
}, [cals[0], cals[0]]);
|
||||||
|
|
||||||
return dayCount(longest);
|
return dayCount(longest, timezone);
|
||||||
}
|
}
|
||||||
|
@ -134,8 +134,8 @@ block content
|
|||||||
tr
|
tr
|
||||||
td.col-xs-5.hidden-xs
|
td.col-xs-5.hidden-xs
|
||||||
a(href='/challenges/' + removeOldTerms(challenge.name), target='_blank')= removeOldTerms(challenge.name)
|
a(href='/challenges/' + removeOldTerms(challenge.name), target='_blank')= removeOldTerms(challenge.name)
|
||||||
td.col-xs-2.hidden-xs= challenge.completedDate ? moment(challenge.completedDate, 'x').format("MMM DD, YYYY") : 'Not Available'
|
td.col-xs-2.hidden-xs= challenge.completedDate ? challenge.completedDate : 'Not Available'
|
||||||
td.col-xs-2.hidden-xs= challenge.lastUpdated ? moment(challenge.lastUpdated, 'x').format("MMM DD, YYYY") : ''
|
td.col-xs-2.hidden-xs= challenge.lastUpdated ? challenge.lastUpdated : ''
|
||||||
td.col-xs-2.hidden-xs
|
td.col-xs-2.hidden-xs
|
||||||
a(href=challenge.solution, target='_blank') View my project
|
a(href=challenge.solution, target='_blank') View my project
|
||||||
td.col-xs-12.visible-xs
|
td.col-xs-12.visible-xs
|
||||||
@ -152,8 +152,8 @@ block content
|
|||||||
for challenge in algos
|
for challenge in algos
|
||||||
tr
|
tr
|
||||||
td.col-xs-5.hidden-xs= removeOldTerms(challenge.name)
|
td.col-xs-5.hidden-xs= removeOldTerms(challenge.name)
|
||||||
td.col-xs-2.hidden-xs= challenge.completedDate ? moment(challenge.completedDate, 'x').format("MMM DD, YYYY") : 'Not Available'
|
td.col-xs-2.hidden-xs= challenge.completedDate ? challenge.completedDate : 'Not Available'
|
||||||
td.col-xs-2.hidden-xs= challenge.lastUpdated ? moment(challenge.lastUpdated, 'x').format("MMM DD, YYYY") : ''
|
td.col-xs-2.hidden-xs= challenge.lastUpdated ? challenge.lastUpdated : ''
|
||||||
td.col-xs-2.hidden-xs
|
td.col-xs-2.hidden-xs
|
||||||
if (challenge.solution)
|
if (challenge.solution)
|
||||||
a(href='/challenges/' + removeOldTerms(challenge.name) + '?solution=' + encodeURIComponent(encodeFcc(challenge.solution)), target='_blank') View my solution
|
a(href='/challenges/' + removeOldTerms(challenge.name) + '?solution=' + encodeURIComponent(encodeFcc(challenge.solution)), target='_blank') View my solution
|
||||||
@ -176,8 +176,8 @@ block content
|
|||||||
for challenge in challenges
|
for challenge in challenges
|
||||||
tr
|
tr
|
||||||
td.col-xs-5.hidden-xs= removeOldTerms(challenge.name)
|
td.col-xs-5.hidden-xs= removeOldTerms(challenge.name)
|
||||||
td.col-xs-2.hidden-xs= challenge.completedDate ?moment(challenge.completedDate, 'x').format("MMM DD, YYYY") : 'Not Available'
|
td.col-xs-2.hidden-xs= challenge.completedDate ? challenge.completedDate : 'Not Available'
|
||||||
td.col-xs-2.hidden-xs= challenge.lastUpdated ? moment(challenge.lastUpdated, 'x').format("MMM DD, YYYY") : ''
|
td.col-xs-2.hidden-xs= challenge.lastUpdated ? challenge.lastUpdated : ''
|
||||||
td.col-xs-2.hidden-xs
|
td.col-xs-2.hidden-xs
|
||||||
if (challenge.solution)
|
if (challenge.solution)
|
||||||
a(href='/challenges/' + removeOldTerms(challenge.name) + '?solution=' + encodeURIComponent(encodeFcc(challenge.solution)), target='_blank') View my solution
|
a(href='/challenges/' + removeOldTerms(challenge.name) + '?solution=' + encodeURIComponent(encodeFcc(challenge.solution)), target='_blank') View my solution
|
||||||
|
@ -1,34 +1,44 @@
|
|||||||
import moment from 'moment';
|
import moment from 'moment-timezone';
|
||||||
|
|
||||||
import { dayCount } from '../../../server/utils/date-utils';
|
import { dayCount } from '../../../server/utils/date-utils';
|
||||||
|
|
||||||
let test = require('tape');
|
let test = require('tape');
|
||||||
|
|
||||||
test('Day count between two epochs (inclusive) calculation', function (t) {
|
test('Day count between two epochs (inclusive) calculation', function (t) {
|
||||||
t.plan(5);
|
t.plan(7);
|
||||||
|
|
||||||
t.equal(dayCount([
|
t.equal(dayCount([
|
||||||
moment("8/3/2015 3:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("8/3/2015 3:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment("8/3/2015 2:00", "M/D/YYYY H:mm").valueOf()
|
moment.utc("8/3/2015 2:00", "M/D/YYYY H:mm").valueOf()
|
||||||
]), 1, "should return 1 day given epochs of the same day");
|
]), 1, "should return 1 day given epochs of the same day");
|
||||||
|
|
||||||
t.equal(dayCount([
|
t.equal(dayCount([
|
||||||
moment("8/3/2015 2:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("8/3/2015 2:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment("8/3/2015 2:00", "M/D/YYYY H:mm").valueOf()
|
moment.utc("8/3/2015 2:00", "M/D/YYYY H:mm").valueOf()
|
||||||
]), 1, "should return 1 day given same epochs");
|
]), 1, "should return 1 day given same epochs");
|
||||||
|
|
||||||
t.equal(dayCount([
|
t.equal(dayCount([
|
||||||
moment("8/4/2015 2:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("8/4/2015 2:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment("8/3/2015 2:00", "M/D/YYYY H:mm").valueOf()
|
moment.utc("8/3/2015 2:00", "M/D/YYYY H:mm").valueOf()
|
||||||
]), 2, "should return 2 days when there is a 24 hours difference between given dates");
|
]), 2, "should return 2 days when there is a 24 hours difference between given dates");
|
||||||
|
|
||||||
t.equal(dayCount([
|
t.equal(dayCount([
|
||||||
moment("8/4/2015 1:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("8/4/2015 1:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment("8/3/2015 23:00", "M/D/YYYY H:mm").valueOf()
|
moment.utc("8/3/2015 23:00", "M/D/YYYY H:mm").valueOf()
|
||||||
]), 2, "should return 2 days when the diff is less than 24h but days of the month are different");
|
]), 2, "should return 2 days when the diff is less than 24h but days are different in default timezone UTC");
|
||||||
|
|
||||||
t.equal(dayCount([
|
t.equal(dayCount([
|
||||||
moment("10/27/2015 1:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("8/4/2015 1:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment("5/12/1982 1:00", "M/D/YYYY H:mm").valueOf()
|
moment.utc("8/3/2015 23:00", "M/D/YYYY H:mm").valueOf()
|
||||||
|
], 'America/Los_Angeles'), 1, "should return 1 day when the diff is less than 24h and days are different in UTC, but given 'America/Los_Angeles' timezone");
|
||||||
|
|
||||||
|
t.equal(dayCount([
|
||||||
|
moment.utc("10/27/2015 1:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
|
moment.utc("5/12/1982 1:00", "M/D/YYYY H:mm").valueOf()
|
||||||
]), 12222, "should return correct count when there is very big period");
|
]), 12222, "should return correct count when there is very big period");
|
||||||
|
|
||||||
|
t.equal(dayCount([
|
||||||
|
moment.utc("8/4/2015 2:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
|
moment.utc("8/3/2015 2:00", "M/D/YYYY H:mm").valueOf()
|
||||||
|
], undefined), 2, "should return 2 days when there is a 24 hours difference between dates given 'undefined' timezone");
|
||||||
});
|
});
|
||||||
|
@ -1,132 +1,160 @@
|
|||||||
import moment from 'moment';
|
import moment from 'moment-timezone';
|
||||||
|
|
||||||
import { calcCurrentStreak, calcLongestStreak } from '../../../server/utils/user-stats';
|
import { calcCurrentStreak, calcLongestStreak } from '../../../server/utils/user-stats';
|
||||||
|
|
||||||
let test = require('tape');
|
let test = require('tape');
|
||||||
|
|
||||||
test('Current streak calculation', function (t) {
|
test('Current streak calculation', function (t) {
|
||||||
t.plan(7);
|
t.plan(9);
|
||||||
|
|
||||||
t.equal(calcCurrentStreak([
|
t.equal(calcCurrentStreak([
|
||||||
moment(moment().subtract(1, 'hours')).valueOf()
|
moment.utc(moment.utc().subtract(1, 'hours')).valueOf()
|
||||||
]), 1, "should return 1 day when today one challenge was completed");
|
]), 1, "should return 1 day when today one challenge was completed");
|
||||||
|
|
||||||
t.equal(calcCurrentStreak([
|
t.equal(calcCurrentStreak([
|
||||||
moment(moment().subtract(2, 'hours')).valueOf(),
|
moment.utc(moment.utc().subtract(1, 'hours')).valueOf(),
|
||||||
moment(moment().subtract(1, 'hours')).valueOf()
|
moment.utc(moment.utc().subtract(1, 'hours')).valueOf()
|
||||||
]), 1, "should return 1 day when today more than one challenge were completed");
|
]), 1, "should return 1 day when today more than one challenge was completed");
|
||||||
|
|
||||||
t.equal(calcCurrentStreak([
|
t.equal(calcCurrentStreak([
|
||||||
moment("9/11/2015 4:00", "M/D/YYYY H:mm").valueOf()
|
moment.utc("9/11/2015 4:00", "M/D/YYYY H:mm").valueOf()
|
||||||
]), 0, "should return 0 day when today 0 challenges were completed");
|
]), 0, "should return 0 day when today 0 challenges were completed");
|
||||||
|
|
||||||
t.equal(calcCurrentStreak([
|
t.equal(calcCurrentStreak([
|
||||||
moment(moment().subtract(1, 'days')).valueOf(),
|
moment.utc(moment.utc().subtract(1, 'days')).valueOf(),
|
||||||
moment(moment().subtract(1, 'hours')).valueOf()
|
moment.utc(moment.utc().subtract(1, 'hours')).valueOf()
|
||||||
]), 2, "should return 2 days when today and yesterday challenges were completed");
|
]), 2, "should return 2 days when today and yesterday challenges were completed");
|
||||||
|
|
||||||
t.equal(calcCurrentStreak([
|
t.equal(calcCurrentStreak([
|
||||||
moment("8/3/2015 2:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("8/3/2015 2:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment("9/11/2015 4:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("9/11/2015 4:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment("9/12/2015 1:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("9/12/2015 1:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment("9/12/2015 1:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("9/12/2015 1:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment("9/12/2015 2:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("9/12/2015 2:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment("9/12/2015 3:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("9/12/2015 3:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment("9/13/2015 4:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("9/13/2015 4:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment("9/14/2015 5:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("9/14/2015 5:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment(moment().subtract(2, 'days')).valueOf(),
|
moment.utc(moment.utc().subtract(2, 'days')).valueOf(),
|
||||||
moment(moment().subtract(1, 'days')).valueOf(),
|
moment.utc(moment.utc().subtract(1, 'days')).valueOf(),
|
||||||
moment(moment().subtract(1, 'hours')).valueOf()
|
moment.utc(moment.utc().subtract(1, 'hours')).valueOf()
|
||||||
]), 3, "should return 3 when today and for two days before user was activity");
|
]), 3, "should return 3 when today and for two days before user was activity");
|
||||||
|
|
||||||
t.equal(calcCurrentStreak([
|
t.equal(calcCurrentStreak([
|
||||||
moment(moment().subtract(37, 'hours')).valueOf(),
|
moment.utc(moment.utc().subtract(37, 'hours')).valueOf(),
|
||||||
moment(moment().subtract(1, 'hours')).valueOf()
|
moment.utc(moment.utc().subtract(1, 'hours')).valueOf()
|
||||||
]), 1, "should return 1 when between todays challenge completion and yesterdays there is a 1.5 day (36 hours) long break");
|
]), 1, "should return 1 when between todays challenge completion and yesterdays there is a 1.5 day (36 hours) long break");
|
||||||
|
|
||||||
|
t.ok(calcCurrentStreak([
|
||||||
|
moment.utc(moment.utc().subtract(35, 'hours')).valueOf(),
|
||||||
|
moment.utc(moment.utc().subtract(1, 'hours')).valueOf()
|
||||||
|
]) >= 2, "should return not less than 2 days when between todays challenge completion and yesterdays there is less than 1.5 day (36 hours) long break");
|
||||||
|
|
||||||
|
t.equal(calcCurrentStreak([
|
||||||
|
moment.utc(moment.utc().subtract(1, 'hours')).valueOf(),
|
||||||
|
moment.utc(moment.utc().subtract(1, 'hours')).valueOf()
|
||||||
|
], undefined), 1, "should return correct count in default timezone UTC given 'undefined' timezone");
|
||||||
|
|
||||||
t.equal(calcCurrentStreak([
|
t.equal(calcCurrentStreak([
|
||||||
moment(moment().subtract(35, 'hours')).valueOf(),
|
moment.utc(moment.utc().subtract(1, 'days')).valueOf(),
|
||||||
moment(moment().subtract(1, 'hours')).valueOf()
|
moment.utc(moment.utc().subtract(1, 'hours')).valueOf()
|
||||||
]), 2, "should return 2 days when between todays challenge completion and yesterdays there is less than 1.5 day (36 hours) long break");
|
], 'America/Los_Angeles'), 2, "should return 2 days when today and yesterday challenges were completed given 'America/Los_Angeles' timezone");
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Longest streak calculation', function (t) {
|
test('Longest streak calculation', function (t) {
|
||||||
t.plan(9);
|
t.plan(12);
|
||||||
|
|
||||||
t.equal(calcLongestStreak([
|
t.equal(calcLongestStreak([
|
||||||
moment("9/12/2015 4:00", "M/D/YYYY H:mm").valueOf()
|
moment.utc("9/12/2015 4:00", "M/D/YYYY H:mm").valueOf()
|
||||||
]), 1, "should return 1 when there is the only one one-day-long streak available");
|
]), 1, "should return 1 when there is the only one one-day-long streak available");
|
||||||
|
|
||||||
t.equal(calcLongestStreak([
|
t.equal(calcLongestStreak([
|
||||||
moment("9/11/2015 4:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("9/11/2015 4:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment("9/12/2015 2:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("9/12/2015 2:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment("9/13/2015 3:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("9/13/2015 3:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment("9/14/2015 1:00", "M/D/YYYY H:mm").valueOf()
|
moment.utc("9/14/2015 1:00", "M/D/YYYY H:mm").valueOf()
|
||||||
]), 4, "should return 4 when there is the only one more-than-one-days-long streak available");
|
]), 4, "should return 4 when there is the only one more-than-one-days-long streak available");
|
||||||
|
|
||||||
t.equal(calcLongestStreak([
|
t.equal(calcLongestStreak([
|
||||||
moment(moment().subtract(1, 'hours')).valueOf()
|
moment.utc(moment.utc().subtract(1, 'hours')).valueOf()
|
||||||
]), 1, "should return 1 when there is only one one-day-long streak and it is today");
|
]), 1, "should return 1 when there is only one one-day-long streak and it is today");
|
||||||
|
|
||||||
t.equal(calcLongestStreak([
|
t.equal(calcLongestStreak([
|
||||||
moment(moment().subtract(1, 'days')).valueOf(),
|
moment.utc(moment.utc().subtract(1, 'days')).valueOf(),
|
||||||
moment(moment().subtract(1, 'hours')).valueOf()
|
moment.utc(moment.utc().subtract(1, 'hours')).valueOf()
|
||||||
]), 2, "should return 2 when yesterday and today makes longest streak");
|
]), 2, "should return 2 when yesterday and today makes longest streak");
|
||||||
|
|
||||||
t.equal(calcLongestStreak([
|
t.equal(calcLongestStreak([
|
||||||
moment("8/3/2015 2:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("8/3/2015 2:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment("9/11/2015 4:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("9/11/2015 4:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment("9/12/2015 2:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("9/12/2015 2:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment("10/4/2015 1:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("10/4/2015 1:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment("10/5/2015 5:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("10/5/2015 5:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment("10/6/2015 4:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("10/6/2015 4:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment("10/7/2015 5:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("10/7/2015 5:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment("11/3/2015 2:00", "M/D/YYYY H:mm").valueOf()
|
moment.utc("11/3/2015 2:00", "M/D/YYYY H:mm").valueOf()
|
||||||
]), 4, "should return 4 when there is a month long break");
|
]), 4, "should return 4 when there is a month long break");
|
||||||
|
|
||||||
t.equal(calcLongestStreak([
|
t.equal(calcLongestStreak([
|
||||||
moment("8/3/2015 2:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("8/3/2015 2:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment("9/11/2015 4:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("9/11/2015 4:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment("9/12/2015 1:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("9/12/2015 1:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment(moment("9/12/2015 1:00", "M/D/YYYY H:mm").add(37, 'hours')).valueOf(),
|
moment.utc(moment.utc("9/12/2015 1:00", "M/D/YYYY H:mm").add(37, 'hours')).valueOf(),
|
||||||
moment("9/14/2015 22:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("9/14/2015 22:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment("9/15/2015 4:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("9/15/2015 4:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment("10/3/2015 2:00", "M/D/YYYY H:mm").valueOf()
|
moment.utc("10/3/2015 2:00", "M/D/YYYY H:mm").valueOf()
|
||||||
]), 3, "should return 3 when there is a more than 1.5 days long break of (36 hours)");
|
]), 3, "should return 3 when there is a more than 1.5 days long break of (36 hours)");
|
||||||
|
|
||||||
t.equal(calcLongestStreak([
|
t.equal(calcLongestStreak([
|
||||||
moment("8/3/2015 2:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("8/3/2015 2:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment("9/11/2015 4:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("9/11/2015 4:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment("9/12/2015 1:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("9/12/2015 1:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment("9/12/2015 1:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("9/12/2015 1:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment("9/12/2015 2:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("9/12/2015 2:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment("9/12/2015 3:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("9/12/2015 3:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment("9/13/2015 4:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("9/13/2015 4:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment("9/14/2015 5:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("9/14/2015 5:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment(moment().subtract(2, 'days')).valueOf(),
|
moment.utc(moment.utc().subtract(2, 'days')).valueOf(),
|
||||||
moment(moment().subtract(1, 'days')).valueOf(),
|
moment.utc(moment.utc().subtract(1, 'days')).valueOf(),
|
||||||
moment().valueOf()
|
moment.utc().valueOf()
|
||||||
]), 4, "should return 4 when the longest streak consist of several same day timestamps");
|
]), 4, "should return 4 when the longest streak consist of several same day timestamps");
|
||||||
|
|
||||||
t.equal(calcLongestStreak([
|
t.equal(calcLongestStreak([
|
||||||
moment("8/3/2015 2:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("8/3/2015 2:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment("9/11/2015 4:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("9/11/2015 4:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment("9/12/2015 1:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("9/12/2015 1:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment("9/13/2015 4:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("9/13/2015 4:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment("9/14/2015 5:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("9/14/2015 5:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment("10/11/2015 4:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("10/11/2015 4:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment("10/12/2015 1:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("10/12/2015 1:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment("10/13/2015 4:00", "M/D/YYYY H:mm").valueOf(),
|
moment.utc("10/13/2015 4:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
moment("10/14/2015 5:00", "M/D/YYYY H:mm").valueOf()
|
moment.utc("10/14/2015 5:00", "M/D/YYYY H:mm").valueOf()
|
||||||
]), 4, "should return 4 when there are several longest streaks (same length)");
|
]), 4, "should return 4 when there are several longest streaks (same length)");
|
||||||
|
|
||||||
let cals = [];
|
let cals = [];
|
||||||
const n = 100;
|
const n = 100;
|
||||||
for (var i = 0; i < n; i++) {
|
for (var i = 0; i < n; i++) {
|
||||||
cals.push(moment(moment().subtract(i, 'days')).valueOf());
|
cals.push(moment.utc(moment.utc().subtract(i, 'days')).valueOf());
|
||||||
}
|
}
|
||||||
cals.sort();
|
cals.sort();
|
||||||
t.equal(calcLongestStreak(cals), n, "should return correct longest streak when there is a very long period");
|
t.equal(calcLongestStreak(cals), n, "should return correct longest streak when there is a very long period");
|
||||||
|
|
||||||
|
t.equal(calcLongestStreak([
|
||||||
|
moment.utc(moment.utc().subtract(1, 'days')).valueOf(),
|
||||||
|
moment.utc(moment.utc().subtract(1, 'hours')).valueOf()
|
||||||
|
], undefined), 2, "should return correct longest streak in default timezone UTC given 'undefined' timezone");
|
||||||
|
|
||||||
|
t.equal(calcLongestStreak([
|
||||||
|
moment.utc("9/11/2015 4:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
|
moment.utc("9/12/2015 2:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
|
moment.utc("9/13/2015 3:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
|
moment.utc("9/14/2015 1:00", "M/D/YYYY H:mm").valueOf()
|
||||||
|
], 'America/Los_Angeles'), 4, "should return 4 when there is the only one more-than-one-days-long streak available given 'America/Los_Angeles' timezone");
|
||||||
|
|
||||||
|
t.equal(calcLongestStreak([
|
||||||
|
moment.utc("9/11/2015 23:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
|
moment.utc("9/12/2015 2:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
|
moment.utc("9/13/2015 2:00", "M/D/YYYY H:mm").valueOf(),
|
||||||
|
moment.utc("9/14/2015 1:00", "M/D/YYYY H:mm").valueOf()
|
||||||
|
], 'America/Los_Angeles'), 3, "should return 3 when longest streak is 3 in given 'America/Los_Angeles' timezone (but would be different in default timezone UTC)");
|
||||||
});
|
});
|
||||||
|
Reference in New Issue
Block a user