diff --git a/dist/habitrpg-shared.js b/dist/habitrpg-shared.js index 02481c688c..f4f1f54e23 100644 --- a/dist/habitrpg-shared.js +++ b/dist/habitrpg-shared.js @@ -14308,14 +14308,20 @@ api.startOfWeek = api.startOfWeek = function(options) { }; api.startOfDay = function(options) { - var o; + var dayStart, o; if (options == null) { options = {}; } o = sanitizeOptions(options); - return moment(o.now).startOf('day').add({ + dayStart = moment(o.now).startOf('day').add({ hours: o.dayStart }); + if (moment(o.now).hour() < o.dayStart) { + dayStart.subtract({ + days: 1 + }); + } + return dayStart; }; api.dayMapping = { @@ -14341,7 +14347,9 @@ api.daysSince = function(yesterday, options) { o = sanitizeOptions(options); return Math.abs(api.startOfDay(_.defaults({ now: yesterday - }, o)).diff(o.now, 'days')); + }, o)).diff(api.startOfDay(_.defaults({ + now: o.now + }, o)), 'days')); }; @@ -14350,7 +14358,7 @@ api.daysSince = function(yesterday, options) { */ api.shouldDo = function(day, repeat, options) { - var o, selected, yesterday; + var o, selected; if (options == null) { options = {}; } @@ -14361,17 +14369,7 @@ api.shouldDo = function(day, repeat, options) { selected = repeat[api.dayMapping[api.startOfDay(_.defaults({ now: day }, o)).day()]]; - if (!moment(day).zone(o.timezoneOffset).isSame(o.now, 'd')) { - return selected; - } - if (options.dayStart <= o.now.hour()) { - return selected; - } else { - yesterday = moment(o.now).subtract({ - days: 1 - }).day(); - return repeat[api.dayMapping[yesterday]]; - } + return selected; }; @@ -16570,5 +16568,5 @@ api.wrap = function(user, main) { }; -}).call(this,require("/Users/lefnire/Google Drive/Sync/Sites/habitrpg/modules/habitrpg-shared/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js")) -},{"./content.coffee":5,"./i18n.coffee":6,"/Users/lefnire/Google Drive/Sync/Sites/habitrpg/modules/habitrpg-shared/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":2,"lodash":3,"moment":4}]},{},[1]) \ No newline at end of file +}).call(this,require("/home/matteo/Dev/habitrpg/habitrpg-shared/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js")) +},{"./content.coffee":5,"./i18n.coffee":6,"/home/matteo/Dev/habitrpg/habitrpg-shared/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":2,"lodash":3,"moment":4}]},{},[1]) \ No newline at end of file diff --git a/script/index.coffee b/script/index.coffee index e69b8e00c2..e214cecf6b 100644 --- a/script/index.coffee +++ b/script/index.coffee @@ -57,7 +57,10 @@ api.startOfWeek = api.startOfWeek = (options={}) -> api.startOfDay = (options={}) -> o = sanitizeOptions(options) - moment(o.now).startOf('day').add({hours:o.dayStart}) + dayStart = moment(o.now).startOf('day').add({hours:o.dayStart}) + if moment(o.now).hour() < o.dayStart + dayStart.subtract({days:1}) + dayStart api.dayMapping = {0:'su',1:'m',2:'t',3:'w',4:'th',5:'f',6:'s'} @@ -66,7 +69,7 @@ api.dayMapping = {0:'su',1:'m',2:'t',3:'w',4:'th',5:'f',6:'s'} ### api.daysSince = (yesterday, options = {}) -> o = sanitizeOptions options - Math.abs api.startOfDay(_.defaults {now:yesterday}, o).diff(o.now, 'days') + Math.abs api.startOfDay(_.defaults {now:yesterday}, o).diff(api.startOfDay(_.defaults {now:o.now}, o), 'days') ### Should the user do this taks on this date, given the task's repeat options and user.preferences.dayStart? @@ -75,12 +78,7 @@ api.shouldDo = (day, repeat, options={}) -> return false unless repeat o = sanitizeOptions options selected = repeat[api.dayMapping[api.startOfDay(_.defaults {now:day}, o).day()]] - return selected unless moment(day).zone(o.timezoneOffset).isSame(o.now,'d') - if options.dayStart <= o.now.hour() # we're past the dayStart mark, is it due today? - return selected - else # we're not past dayStart mark, check if it was due "yesterday" - yesterday = moment(o.now).subtract({days:1}).day() # have to wrap o.now so as not to modify original - return repeat[api.dayMapping[yesterday]] # FIXME is this correct?? Do I need to do any timezone calcaulation here? + return selected ### ------------------------------------------------------ diff --git a/test/algos.mocha.coffee b/test/algos.mocha.coffee index 7bd4864744..bf706e0266 100644 --- a/test/algos.mocha.coffee +++ b/test/algos.mocha.coffee @@ -56,6 +56,9 @@ expectStrings = (obj, paths) -> _.each paths, (path) -> expect(obj[path]).to.be.ok() # options.daysAgo: days ago when the last cron was executed +# cronAfterStart: moves the lastCron to be after the dayStart. +# This way the daysAgo works as expected if the test case +# makes the assumption that the lastCron was after dayStart. beforeAfter = (options={}) -> user = newUser() [before, after] = [user, _.cloneDeep(user)] @@ -66,7 +69,9 @@ beforeAfter = (options={}) -> if options.limitOne before["#{options.limitOne}s"] = [before["#{options.limitOne}s"][0]] after["#{options.limitOne}s"] = [after["#{options.limitOne}s"][0]] - lastCron = +(moment(options.now || +new Date).subtract(options.daysAgo, 'days')) if options.daysAgo + lastCron = moment(options.now || +new Date).subtract( {days:options.daysAgo} ) if options.daysAgo + lastCron.add( {hours:options.dayStart, minutes:1} ) if options.daysAgo and options.cronAfterStart + lastCron = +lastCron if options.daysAgo _.each [before,after], (obj) -> obj.lastCron = lastCron if options.daysAgo {before:before, after:after} @@ -517,6 +522,59 @@ describe 'Cron', -> expect(after.todos[0].value).to.be.lessThan before.todos[0].value expect(after.history.todos).to.have.length 1 + # I used hard-coded dates here instead of 'now' so the tests don't fail + # when you run them between midnight and dayStart. Nothing worse than + # intermittent failures. + describe 'cron day calculations', -> + dayStart = 4 + fstr = "YYYY-MM-DD HH:mm:ss" + + it 'startOfDay before dayStart', -> + # If the time is before dayStart, then we expect the start of the day to be yesterday at dayStart + start = shared.startOfDay {now: moment('2014-10-09 02:30:00'), dayStart} + expect(start.format(fstr)).to.eql '2014-10-08 04:00:00' + + it 'startOfDay after dayStart', -> + # If the time is after dayStart, then we expect the start of the day to be today at dayStart + start = shared.startOfDay {now: moment('2014-10-09 05:30:00'), dayStart} + expect(start.format(fstr)).to.eql '2014-10-09 04:00:00' + + it 'daysSince cron before, now after', -> + # If the lastCron was before dayStart, then a time on the same day after dayStart + # should be 1 day later than lastCron + lastCron = moment('2014-10-09 02:30:00') + days = shared.daysSince(lastCron, {now: moment('2014-10-09 11:30:00'), dayStart}) + expect(days).to.eql 1 + + it 'daysSince cron before, now before', -> + # If the lastCron was before dayStart, then a time on the same day also before dayStart + # should be 0 days later than lastCron + lastCron = moment('2014-10-09 02:30:00') + days = shared.daysSince(lastCron, {now: moment('2014-10-09 03:30:00'), dayStart}) + expect(days).to.eql 0 + + it 'daysSince cron after, now after', -> + # If the lastCron was after dayStart, then a time on the same day also after dayStart + # should be 0 days later than lastCron + lastCron = moment('2014-10-09 05:30:00') + days = shared.daysSince(lastCron, {now: moment('2014-10-09 06:30:00'), dayStart}) + expect(days).to.eql 0 + + it 'daysSince cron after, now tomorrow before', -> + # If the lastCron was after dayStart, then a time on the following day but before dayStart + # should be 0 days later than lastCron + lastCron = moment('2014-10-09 12:30:00') + days = shared.daysSince(lastCron, {now: moment('2014-10-10 01:30:00'), dayStart}) + expect(days).to.eql 0 + + it 'daysSince cron after, now tomorrow after', -> + # If the lastCron was after dayStart, then a time on the following day and after dayStart + # should be 1 day later than lastCron + lastCron = moment('2014-10-09 12:30:00') + days = shared.daysSince(lastCron, {now: moment('2014-10-10 10:30:00'), dayStart}) + expect(days).to.eql 1 + + describe 'dailies', -> describe 'new day', -> @@ -530,7 +588,7 @@ describe 'Cron', -> runCron = (options) -> _.each [480, 240, 0, -120], (timezoneOffset) -> # test different timezones now = shared.startOfWeek({timezoneOffset}).add(options.currentHour||0, 'hours') - {before,after} = beforeAfter({now, timezoneOffset, daysAgo:1, dayStart:options.dayStart||0, limitOne:'daily'}) + {before,after} = beforeAfter({now, timezoneOffset, daysAgo:1, cronAfterStart:options.cronAfterStart||true, dayStart:options.dayStart||0, limitOne:'daily'}) before.dailys[0].repeat = after.dailys[0].repeat = options.repeat if options.repeat before.dailys[0].streak = after.dailys[0].streak = 10 before.dailys[0].completed = after.dailys[0].completed = true if options.checked @@ -544,11 +602,14 @@ describe 'Cron', -> when 'noDamage' then expectDayResetNoDamage(before,after) {before,after} + # These test cases were written assuming that lastCron was run after dayStart + # even if currentHour < dayStart and lastCron = yesterday at currentHour. + # cronAfterStart makes sure that lastCron is moved to be after dayStart. cronMatrix = steps: 'due yesterday': - defaults: {daysAgo:1, limitOne: 'daily'} + defaults: {daysAgo:1, cronAfterStart:true, limitOne: 'daily'} steps: '(simple)': {expect:'losePoints'} @@ -599,15 +660,8 @@ describe 'Cron', -> it "#{options.text}", -> runCron(_.defaults(obj,options)) recurseCronMatrix(cronMatrix) - it 'calculates day differences with dayStart properly', -> - dayStart = 4 - yesterday = shared.startOfDay {now: moment().subtract(1, 'd'), dayStart} - now = shared.startOfDay {dayStart: dayStart-1} - expect(shared.daysSince(yesterday, {now, dayStart})).to.eql 0 - now = moment().startOf('day').add(dayStart, 'h').add(1, 'm') - expect(shared.daysSince(yesterday, {now, dayStart})).to.eql 1 - describe 'Helper', -> + it 'calculates gold coins', -> expect(shared.gold(10)).to.eql 10 expect(shared.gold(1.957)).to.eql 1 @@ -626,9 +680,15 @@ describe 'Helper', -> expect(shared.tnl 99).to.eql 3580 it 'calculates the start of the day', -> - expect(shared.startOfDay({now: new Date(2013, 0, 1, 0)}).format('YYYY-MM-DD HH:mm')).to.eql '2013-01-01 00:00' - expect(shared.startOfDay({now: new Date(2013, 0, 1, 5)}).format('YYYY-MM-DD HH:mm')).to.eql '2013-01-01 00:00' - expect(shared.startOfDay({now: new Date(2013, 0, 1, 23, 59, 59)}).format('YYYY-MM-DD HH:mm')).to.eql '2013-01-01 00:00' + fstr = 'YYYY-MM-DD HH:mm:ss' + today = '2013-01-01 00:00:00' + # get the timezone for the day, so the test case doesn't fail + # if you run it during daylight savings time because by default + # it uses moment().zone() which is the current minute offset + zone = moment(today).zone() + expect(shared.startOfDay({now: new Date(2013, 0, 1, 0)}, timezoneOffset:zone).format(fstr)).to.eql today + expect(shared.startOfDay({now: new Date(2013, 0, 1, 5)}, timezoneOffset:zone).format(fstr)).to.eql today + expect(shared.startOfDay({now: new Date(2013, 0, 1, 23, 59, 59), timezoneOffset:zone}).format(fstr)).to.eql today it 'counts pets', -> pets = {}