Skip to content

Commit

Permalink
separate monthly periods from daily periods defined by milliseconds
Browse files Browse the repository at this point in the history
  • Loading branch information
archmoj committed Sep 17, 2020
1 parent be52281 commit afa8965
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 72 deletions.
122 changes: 51 additions & 71 deletions src/plots/cartesian/align_period.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@ var Lib = require('../../lib');
var ms2DateTime = Lib.ms2DateTime;
var dateTime2ms = Lib.dateTime2ms;
var incrementMonth = Lib.incrementMonth;
var constants = require('../../constants/numerical');
var ONEDAY = constants.ONEDAY;
var ONEAVGMONTH = constants.ONEAVGMONTH;
var ONEAVGYEAR = constants.ONEAVGYEAR;
var ONEDAY = require('../../constants/numerical').ONEDAY;

module.exports = function alignPeriod(trace, ax, axLetter, vals) {
if(ax.type !== 'date') return vals;
Expand All @@ -25,84 +22,67 @@ module.exports = function alignPeriod(trace, ax, axLetter, vals) {
if(!alignment) return vals;

var period = trace[axLetter + 'period'];
var mPeriod;
var mPeriod, dPeriod;
if(isNumeric(period)) {
period = +period;
if(period <= 0) return vals;
dPeriod = +period;
dPeriod /= ONEDAY; // convert milliseconds to days
dPeriod = Math.round(dPeriod);
if(dPeriod <= 0) return vals;

This comment has been minimized.

Copy link
@alexcjohnson

alexcjohnson Sep 24, 2020

Collaborator

Why do we need to round to days here? Can't the numeric case just be something like:

base = ... // from below (and isStart and isEnd)
dPeriod = +period;
var offset = isStart ? 0 : (isEnd ? 1 : 0.5) * dPeriod;
return vals.map(function(v) {
    return Math.floor((v - base) / dPeriod) * dPeriod + base + shift;
});
} else if(typeof period === 'string' && period.charAt(0) === 'M') {
var n = +(period.substring(1));
if(n > 0 && Math.round(n) === n) {
mPeriod = n;
period = n * ONEAVGMONTH;
} else return vals;
}

if(period > 0) {
var calendar = ax.calendar;

var isStart = 'start' === alignment;
// var isMiddle = 'middle' === alignment;
var isEnd = 'end' === alignment;

var period0 = trace[axLetter + 'period0'];
var base = dateTime2ms(period0, calendar) || 0;

var newVals = [];
var len = vals.length;
for(var i = 0; i < len; i++) {
var v = vals[i] - base;

var dateStr = ms2DateTime(v, 0, calendar);
var d = new Date(dateStr);
var year = d.getUTCFullYear();
var month = d.getUTCMonth();
var day = d.getUTCDate();

var newD;
var startTime;
var endTime;

var nMonths = Math.floor(period / ONEAVGMONTH) % 12;
var nYears = Math.floor((period - nMonths * ONEAVGMONTH) / ONEAVGYEAR);
var nDays = Math.floor((period - nMonths * ONEAVGMONTH - nYears * ONEAVGYEAR) / ONEDAY);
if(nYears && nMonths) nDays = 0;

var y1 = year + nYears;
var m1 = month + nMonths;
var d1 = day + nDays;
if(nDays || nMonths || nYears) {
if(nDays) {
startTime = Date.UTC(year, month, day);
var monthDays = new Date(y1, m1 + 1, 0).getUTCDate();
if(d1 > monthDays) {
d1 -= monthDays;
m1 += 1;
if(m1 > 11) {
m1 -= 12;
y1 += 1;
}
}
endTime = Date.UTC(y1, m1, d1);
} else if(nMonths) {
startTime = Date.UTC(year, nYears ? month : roundMonth(month, nMonths));
endTime = incrementMonth(startTime, mPeriod ? mPeriod : nMonths, calendar);
} else {
startTime = Date.UTC(year, 0);
endTime = Date.UTC(y1, 0);
}

newD = new Date(
isStart ? startTime :
isEnd ? endTime :
(startTime + endTime) / 2
);
}
var calendar = ax.calendar;

var isStart = 'start' === alignment;
// var isMiddle = 'middle' === alignment;
var isEnd = 'end' === alignment;

var period0 = trace[axLetter + 'period0'];
var base = dateTime2ms(period0, calendar) || 0;

newVals[i] = newD ? newD.getTime() + base : vals[i];
var newVals = [];
var len = vals.length;
for(var i = 0; i < len; i++) {
var v = vals[i] - base;

var dateStr = ms2DateTime(v, 0, calendar);
var d = new Date(dateStr);
var year = d.getUTCFullYear();
var month = d.getUTCMonth();
var day = d.getUTCDate();

var startTime, endTime;
if(dPeriod) {
startTime = Date.UTC(year, month, day);
endTime = startTime + dPeriod * ONEDAY;
}
return newVals;

if(mPeriod) {
var nYears = Math.floor(mPeriod / 12);
var nMonths = mPeriod % 12;

if(nMonths) {
startTime = Date.UTC(year, nYears ? month : roundMonth(month, nMonths));
endTime = incrementMonth(startTime, mPeriod, calendar);
} else {
startTime = Date.UTC(year, 0);
endTime = Date.UTC(year + nYears, 0);
}
}

var newD = new Date(
isStart ? startTime :
isEnd ? endTime :
(startTime + endTime) / 2
);

newVals[i] = newD.getTime() + base;
}
return vals;
return newVals;
};

var monthSteps = [2, 3, 4, 6];
Expand Down
3 changes: 2 additions & 1 deletion src/traces/scatter/attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ function axisPeriod(axis) {
'Only relevant when the axis `type` is *date*.',
'Sets the period positioning in milliseconds or *M<n>* on the ' + axis + ' axis.',
'Special values in the form of *M<n>* could be used to declare',
'the number of "average" months. In this case `n` must be a positive integer.'
'the number of "average" months. In this case `n` must be a positive integer.',
'When using milliseconds the period is rounded and applied as the number of days.'
].join(' ')
};
}
Expand Down
Binary file modified test/image/baselines/period_positioning3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/period_positioning4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit afa8965

Please sign in to comment.