-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Dates as dates #1078
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dates as dates #1078
Changes from 1 commit
d4bb89d
9e27955
00138aa
9784b3b
1db4448
5ff9722
c045bc5
3dd294f
451ee24
9ccde7e
51a0563
5ccd083
e63ea3b
2e9dbad
37d52fe
55b313c
b7121c3
03b4a0c
dd0940a
f629f71
a29ce33
065ac8a
4a0a866
7679483
0d61fcc
5e11571
d807259
05c762d
1b0e133
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -658,9 +658,18 @@ axes.calcTicks = function calcTicks(ax) { | |
// show the exponent only on the last one | ||
ax._tmax = vals[vals.length - 1]; | ||
|
||
// for showing date suffixes: ax._prevSuffix holds what we showed most | ||
// recently. Start with it cleared and mark that we're in calcTicks (ie | ||
// calculating a whole string of these so we should care what the previous | ||
// suffix was!) | ||
ax._prevSuffix = ''; | ||
ax._inCalcTicks = true; | ||
|
||
var ticksOut = new Array(vals.length); | ||
for(var i = 0; i < vals.length; i++) ticksOut[i] = axes.tickText(ax, vals[i]); | ||
|
||
ax._inCalcTicks = false; | ||
|
||
return ticksOut; | ||
}; | ||
|
||
|
@@ -815,6 +824,11 @@ axes.autoTicks = function(ax, roughDTick) { | |
} | ||
}; | ||
|
||
var ONEDAY = 86400000, | ||
ONEHOUR = 3600000, | ||
ONEMIN = 60000, | ||
ONESEC = 1000; | ||
|
||
// after dtick is already known, find tickround = precision | ||
// to display in tick labels | ||
// for numeric ticks, integer # digits after . to round to | ||
|
@@ -831,33 +845,43 @@ function autoTickRound(ax) { | |
if(ax.type === 'category') { | ||
ax._tickround = null; | ||
} | ||
if(ax.type === 'date') { | ||
// If tick0 is unusual, give tickround a bit more information | ||
// not necessarily *all* the information in tick0 though, if it's really odd | ||
// minimal string length for tick0: 'd' is 10, 'M' is 16, 'S' is 19 | ||
// take off a leading minus (year < 0 so length is consistent) | ||
var tick0str = Lib.ms2DateTime(Lib.dateTime2ms(ax.tick0)).replace(/^-/, ''), | ||
tick0len = tick0str.length; | ||
|
||
if(String(dtick).charAt(0) === 'M') { | ||
// any tick0 more specific than a year: alway show the full date | ||
if(tick0len > 10 || tick0str.substr(5) !== '01-01') ax._tickround = 'd'; | ||
// show the month unless ticks are full multiples of a year | ||
else ax._tickround = (+(dtick.substr(1)) % 12 === 0) ? 'y' : 'm'; | ||
} | ||
else if((dtick >= ONEDAY && tick0len <= 10) || (dtick >= ONEDAY * 15)) ax._tickround = 'd'; | ||
else if((dtick >= ONEMIN && tick0len <= 16) || (dtick >= ONEHOUR)) ax._tickround = 'M'; | ||
else if((dtick >= ONESEC && tick0len <= 19) || (dtick >= ONEMIN)) ax._tickround = 'S'; | ||
else ax._tickround = Math.max(3 - Math.round(Math.log(dtick / 2) / Math.LN10), tick0len - 20); | ||
} | ||
else if(isNumeric(dtick) || dtick.charAt(0) === 'L') { | ||
if(ax.type === 'date') { | ||
if(dtick >= 86400000) ax._tickround = 'd'; | ||
else if(dtick >= 3600000) ax._tickround = 'H'; | ||
else if(dtick >= 60000) ax._tickround = 'M'; | ||
else if(dtick >= 1000) ax._tickround = 'S'; | ||
else ax._tickround = 3 - Math.round(Math.log(dtick / 2) / Math.LN10); | ||
} | ||
else { | ||
// linear or log | ||
var rng = ax.range.map(ax.r2d || Number); | ||
if(!isNumeric(dtick)) dtick = Number(dtick.substr(1)); | ||
// 2 digits past largest digit of dtick | ||
ax._tickround = 2 - Math.floor(Math.log(dtick) / Math.LN10 + 0.01); | ||
|
||
var maxend = Math.max(Math.abs(rng[0]), Math.abs(rng[1])); | ||
|
||
var rangeexp = Math.floor(Math.log(maxend) / Math.LN10 + 0.01); | ||
if(Math.abs(rangeexp) > 3) { | ||
if(ax.exponentformat === 'SI' || ax.exponentformat === 'B') { | ||
ax._tickexponent = 3 * Math.round((rangeexp - 1) / 3); | ||
} | ||
else ax._tickexponent = rangeexp; | ||
// linear or log (except D1, D2) | ||
var rng = ax.range.map(ax.r2d || Number); | ||
if(!isNumeric(dtick)) dtick = Number(dtick.substr(1)); | ||
// 2 digits past largest digit of dtick | ||
ax._tickround = 2 - Math.floor(Math.log(dtick) / Math.LN10 + 0.01); | ||
|
||
var maxend = Math.max(Math.abs(rng[0]), Math.abs(rng[1])); | ||
|
||
var rangeexp = Math.floor(Math.log(maxend) / Math.LN10 + 0.01); | ||
if(Math.abs(rangeexp) > 3) { | ||
if(ax.exponentformat === 'SI' || ax.exponentformat === 'B') { | ||
ax._tickexponent = 3 * Math.round((rangeexp - 1) / 3); | ||
} | ||
else ax._tickexponent = rangeexp; | ||
} | ||
} | ||
else if(dtick.charAt(0) === 'M') ax._tickround = (dtick.length === 2) ? 'm' : 'y'; | ||
// D1 or D2 (log) | ||
else ax._tickround = null; | ||
} | ||
|
||
|
@@ -961,7 +985,7 @@ axes.tickFirst = function(ax) { | |
var yearFormat = d3.time.format('%Y'), | ||
monthFormat = d3.time.format('%b %Y'), | ||
dayFormat = d3.time.format('%b %-d'), | ||
hourFormat = d3.time.format('%b %-d %Hh'), | ||
yearMonthDayFormat = d3.time.format('%b %-d, %Y'), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can tell someone is about to add There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
minuteFormat = d3.time.format('%H:%M'), | ||
secondFormat = d3.time.format(':%S'); | ||
|
||
|
@@ -1053,10 +1077,12 @@ function tickTextObj(ax, x, text) { | |
function formatDate(ax, out, hover, extraPrecision) { | ||
var x = out.x, | ||
tr = ax._tickround, | ||
trOriginal = tr, | ||
d = new Date(x), | ||
// suffix completes the full date info, to be included | ||
// with only the first tick | ||
suffix = '', | ||
// with only the first tick or if any info before what's | ||
// shown has changed | ||
suffix, | ||
tt; | ||
if(hover && ax.hoverformat) { | ||
tt = modDateFormat(ax.hoverformat, x); | ||
|
@@ -1069,21 +1095,18 @@ function formatDate(ax, out, hover, extraPrecision) { | |
else { | ||
if(extraPrecision) { | ||
if(isNumeric(tr)) tr += 2; | ||
else tr = {y: 'm', m: 'd', d: 'H', H: 'M', M: 'S', S: 2}[tr]; | ||
else tr = {y: 'm', m: 'd', d: 'M', M: 'S', S: 2}[tr]; | ||
} | ||
if(tr === 'y') tt = yearFormat(d); | ||
else if(tr === 'm') tt = monthFormat(d); | ||
else { | ||
if(x === ax._tmin && !hover) { | ||
suffix = '<br>' + yearFormat(d); | ||
} | ||
if(tr === 'd') { | ||
if(!hover) suffix = '<br>' + yearFormat(d); | ||
|
||
if(tr === 'd') tt = dayFormat(d); | ||
else if(tr === 'H') tt = hourFormat(d); | ||
tt = dayFormat(d); | ||
} | ||
else { | ||
if(x === ax._tmin && !hover) { | ||
suffix = '<br>' + dayFormat(d) + ', ' + yearFormat(d); | ||
} | ||
if(!hover) suffix = '<br>' + yearMonthDayFormat(d); | ||
|
||
tt = minuteFormat(d); | ||
if(tr !== 'M') { | ||
|
@@ -1093,10 +1116,19 @@ function formatDate(ax, out, hover, extraPrecision) { | |
.substr(1); | ||
} | ||
} | ||
else if(trOriginal === 'd') { | ||
// for hover on axes with day ticks, minuteFormat (which | ||
// only includes %H:%M) isn't enough, you want the date too | ||
tt = dayFormat(d) + ' ' + tt; | ||
} | ||
} | ||
} | ||
} | ||
out.text = tt + suffix; | ||
if(suffix && (!ax._inCalcTicks || (suffix !== ax._prevSuffix))) { | ||
tt += suffix; | ||
ax._prevSuffix = suffix; | ||
} | ||
out.text = tt; | ||
} | ||
|
||
function formatLog(ax, out, hover, extraPrecision, hideexp) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1392,4 +1392,88 @@ describe('Test axes', function() { | |
expect(ax._max).toEqual([{val: 6, pad: 15}]); | ||
}); | ||
}); | ||
|
||
describe('calcTicks', function() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Amazing tests. So clear. |
||
function mockCalc(ax) { | ||
Axes.setConvert(ax); | ||
ax.tickfont = {}; | ||
return Axes.calcTicks(ax).map(function(v) { return v.text; }); | ||
} | ||
|
||
it('provides a new date suffix whenever the suffix changes', function() { | ||
var textOut = mockCalc({ | ||
type: 'date', | ||
tickmode: 'linear', | ||
tick0: '2000-01-01', | ||
dtick: 14 * 24 * 3600 * 1000, // 14 days | ||
range: ['1999-12-01', '2000-02-15'] | ||
}); | ||
|
||
var expectedText = [ | ||
'Dec 4<br>1999', | ||
'Dec 18', | ||
'Jan 1<br>2000', | ||
'Jan 15', | ||
'Jan 29', | ||
'Feb 12' | ||
]; | ||
expect(textOut).toEqual(expectedText); | ||
|
||
textOut = mockCalc({ | ||
type: 'date', | ||
tickmode: 'linear', | ||
tick0: '2000-01-01', | ||
dtick: 12 * 3600 * 1000, // 12 hours | ||
range: ['2000-01-03 11:00', '2000-01-06'] | ||
}); | ||
|
||
expectedText = [ | ||
'12:00<br>Jan 3, 2000', | ||
'00:00<br>Jan 4, 2000', | ||
'12:00', | ||
'00:00<br>Jan 5, 2000', | ||
'12:00', | ||
'00:00<br>Jan 6, 2000' | ||
]; | ||
expect(textOut).toEqual(expectedText); | ||
|
||
textOut = mockCalc({ | ||
type: 'date', | ||
tickmode: 'linear', | ||
tick0: '2000-01-01', | ||
dtick: 1000, // 1 sec | ||
range: ['2000-02-03 23:59:57', '2000-02-04 00:00:02'] | ||
}); | ||
|
||
expectedText = [ | ||
'23:59:57<br>Feb 3, 2000', | ||
'23:59:58', | ||
'23:59:59', | ||
'00:00:00<br>Feb 4, 2000', | ||
'00:00:01', | ||
'00:00:02' | ||
]; | ||
expect(textOut).toEqual(expectedText); | ||
}); | ||
|
||
it('should give dates extra precision if tick0 is weird', function() { | ||
var textOut = mockCalc({ | ||
type: 'date', | ||
tickmode: 'linear', | ||
tick0: '2000-01-01 00:05', | ||
dtick: 14 * 24 * 3600 * 1000, // 14 days | ||
range: ['1999-12-01', '2000-02-15'] | ||
}); | ||
|
||
var expectedText = [ | ||
'00:05<br>Dec 4, 1999', | ||
'00:05<br>Dec 18, 1999', | ||
'00:05<br>Jan 1, 2000', | ||
'00:05<br>Jan 15, 2000', | ||
'00:05<br>Jan 29, 2000', | ||
'00:05<br>Feb 12, 2000' | ||
]; | ||
expect(textOut).toEqual(expectedText); | ||
}); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's put these
src/constants/numerical
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
blocking change (bis)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
05c762d