Skip to content

World calendars #1220

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

Merged
merged 33 commits into from
Dec 9, 2016
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
d2a5e4b
simpleMap utility so we don't pass unexpected args
alexcjohnson Dec 2, 2016
6151575
change gl2d_date_axes mock to date string axis range
alexcjohnson Nov 30, 2016
4b9edec
add world calendar support part 1
alexcjohnson Nov 30, 2016
4d8f79a
overhaul set_convert so I can tell where calendars go
alexcjohnson Dec 2, 2016
84a51c2
add world calendar support part 2
alexcjohnson Dec 2, 2016
13cf6b1
world-cals image mock
alexcjohnson Dec 4, 2016
165125b
fix 3d calendar attributes
alexcjohnson Dec 4, 2016
86b31ea
fix gl3d with world calendars
alexcjohnson Dec 5, 2016
78b6646
test world-cals in scattergl & gl2d axes
alexcjohnson Dec 5, 2016
1b563f5
test world calendars with finance charts
alexcjohnson Dec 5, 2016
4e9a632
remove mistaken paste
alexcjohnson Dec 6, 2016
c1c24e8
support world cals in rangesliders
alexcjohnson Dec 6, 2016
a435981
partial support for range selectors on world calendars
alexcjohnson Dec 6, 2016
6653da7
get the right date string replacement for candlestick mock
alexcjohnson Dec 6, 2016
8e1747f
bigger tolerance on updatemenus_test width test
alexcjohnson Dec 6, 2016
00ae2dd
fix and test layout.calendar inheritance
alexcjohnson Dec 6, 2016
3143099
move world-calendar logic in lib/dates.js to new 'calendars' component
etpinard Dec 6, 2016
5984106
coerce calendar attributes in calenders component
etpinard Dec 6, 2016
03ab34f
add requirable 'calendars' module + include it in main bundle
etpinard Dec 6, 2016
db3d18b
fix typo in getComponentMethod call
etpinard Dec 6, 2016
05b2f96
skip over calendar attribute in findArrayAttributes
etpinard Dec 6, 2016
61ecd42
generalise plot schema handling of component attributes
etpinard Dec 6, 2016
68af287
add schema attributes in calendars module
etpinard Dec 6, 2016
cb2c54b
move calendar defaults after early return
etpinard Dec 7, 2016
bc457a9
Merge pull request #1230 from plotly/world-cals-component
etpinard Dec 7, 2016
1444f55
use only the calendars we need from 'world-calendars'
etpinard Dec 7, 2016
08f18ca
Merge pull request #1237 from plotly/world-cals-trimmed
etpinard Dec 7, 2016
dcddcee
support chinese calendar
alexcjohnson Dec 8, 2016
bbb76a4
Merge branch 'master' into world-cals
alexcjohnson Dec 8, 2016
8d8e936
fix some tests for chinese, and robustify dateTime2ms
alexcjohnson Dec 8, 2016
0e05f95
update baseline image with chinese calendar
alexcjohnson Dec 8, 2016
509f287
perf: parseInt for string-leading int extraction
alexcjohnson Dec 9, 2016
7bd501f
prevent non-gregorian month/year todate range selectors
alexcjohnson Dec 9, 2016
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@
"tinycolor2": "^1.3.0",
"topojson-client": "^2.1.0",
"webgl-context": "^2.2.0",
"world-calendars": "0.1.0"
"world-calendars": "^1.0.0"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🍻

},
"devDependencies": {
"brfs": "^1.4.3",
Expand Down
1 change: 1 addition & 0 deletions src/components/calendars/calendars.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ module.exports = require('world-calendars/dist/main');

require('world-calendars/dist/plus');

require('world-calendars/dist/calendars/chinese');
require('world-calendars/dist/calendars/coptic');
require('world-calendars/dist/calendars/discworld');
require('world-calendars/dist/calendars/ethiopian');
Expand Down
8 changes: 6 additions & 2 deletions src/components/calendars/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ var handleTraceDefaults = function(traceIn, traceOut, coords, layout) {
// all support either of those dates. Instead I'll use the most significant
// number they *do* support, biased toward the present day.
var CANONICAL_TICK = {
chinese: '2000-01-01',
coptic: '2000-01-01',
discworld: '2000-01-01',
ethiopian: '2000-01-01',
Expand All @@ -58,11 +59,13 @@ var CANONICAL_TICK = {
};

// Start on a Sunday - for week ticks
// Discworld and Mayan calendars don't have 7-day weeks anyway so don't change them.
// Discworld and Mayan calendars don't have 7-day weeks but we're going to give them
// 7-day week ticks so start on our Sundays.
// If anyone really cares we can customize the auto tick spacings for these calendars.
var CANONICAL_SUNDAY = {
chinese: '2000-01-02',
coptic: '2000-01-03',
discworld: '2000-01-01',
discworld: '2000-01-03',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bug fix or typo?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see the comment change above - I decided it would be better to keep our Sundays unless and until we support the native weeks in these calendars.

ethiopian: '2000-01-05',
hebrew: '5000-01-01',
islamic: '1000-01-02',
Expand All @@ -78,6 +81,7 @@ var CANONICAL_SUNDAY = {
};

var DFLTRANGE = {
chinese: ['2000-01-01', '2001-01-01'],
coptic: ['1700-01-01', '1701-01-01'],
discworld: ['1800-01-01', '1801-01-01'],
ethiopian: ['2000-01-01', '2001-01-01'],
Expand Down
20 changes: 16 additions & 4 deletions src/lib/dates.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ var Registry = require('../registry');
var utcFormat = d3.time.format.utc;

var DATETIME_REGEXP = /^\s*(-?\d\d\d\d|\d\d)(-(\d?\d)(-(\d?\d)([ Tt]([01]?\d|2[0-3])(:([0-5]\d)(:([0-5]\d(\.\d+)?))?(Z|z|[+\-]\d\d:?\d\d)?)?)?)?)?\s*$/m;
// special regex for chinese calendars to support yyyy-mmi-dd etc for intercalary months
var DATETIME_REGEXP_CN = /^\s*(-?\d\d\d\d|\d\d)(-(\d?\di?)(-(\d?\d)([ Tt]([01]?\d|2[0-3])(:([0-5]\d)(:([0-5]\d(\.\d+)?))?(Z|z|[+\-]\d\d:?\d\d)?)?)?)?)?\s*$/m;

// for 2-digit years, the first year we map them onto
var YFIRST = new Date().getFullYear() - 70;
Expand Down Expand Up @@ -155,10 +157,12 @@ exports.dateTime2ms = function(s, calendar) {
calendar = '';
}

var match = s.match(DATETIME_REGEXP);
var isChinese = calendar && calendar.substr(0, 7) === 'chinese';

var match = s.match(isChinese ? DATETIME_REGEXP_CN : DATETIME_REGEXP);
if(!match) return BADNUM;
var y = match[1],
m = Number(match[3] || 1),
m = match[3] || '1',
d = Number(match[5] || 1),
H = Number(match[7] || 0),
M = Number(match[9] || 0),
Expand All @@ -167,11 +171,19 @@ exports.dateTime2ms = function(s, calendar) {
if(isWorld) {
// disallow 2-digit years for world calendars
if(y.length === 2) return BADNUM;
y = Number(y);

var cDate;
try {
cDate = Registry.getComponentMethod('calendars', 'getCal')(calendar)
.newDate(Number(y), m, d);
var calInstance = Registry.getComponentMethod('calendars', 'getCal')(calendar);
if(isChinese) {
var isIntercalary = m.charAt(m.length - 1) === 'i';
m = Number(isIntercalary ? m.substr(0, m.length - 1) : m);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think parseInt(m) woudl work here

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah good call - I always forget about parseInt.

cDate = calInstance.newDate(y, calInstance.toMonthIndex(y, m, isIntercalary), d);
}
else {
cDate = calInstance.newDate(y, Number(m), d);
}
}
catch(e) { return BADNUM; } // Invalid ... date

Expand Down
4 changes: 2 additions & 2 deletions src/plots/cartesian/axes.js
Original file line number Diff line number Diff line change
Expand Up @@ -924,9 +924,9 @@ function autoTickRound(ax) {
// 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)
// take off a leading minus (year < 0) and i (intercalary month) so length is consistent
var tick0ms = ax.r2l(ax.tick0),
tick0str = ax.l2r(tick0ms).replace(/^-/, ''),
tick0str = ax.l2r(tick0ms).replace(/(^-|i)/g, ''),
tick0len = tick0str.length;

if(String(dtick).charAt(0) === 'M') {
Expand Down
30 changes: 15 additions & 15 deletions test/image/mocks/gl3d_world-cals.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@
],
"type": "mesh3d",
"x": [
"2000-01-01",
"2000-02-01",
"2000-03-01",
"2000-01-01"
"2001-04-01",
"2001-04i-01",
"2001-05-01",
"2001-04-01"
],
"xcalendar": "ethiopian",
"xcalendar": "chinese",
"y": [
"0100-01-01",
"0100-01-01",
Expand All @@ -50,12 +50,12 @@
},
{
"x": [
"2000-01-01",
"2000-01-01",
"2000-03-01",
"2000-03-01"
"2001-04-01",
"2001-04-01",
"2001-05-01",
"2001-05-01"
],
"xcalendar": "ethiopian",
"xcalendar": "chinese",
"y": [
"0100-01-01",
"0100-03-01",
Expand All @@ -74,10 +74,10 @@
},
{
"x": [
"2000-01-01",
"2000-03-01"
"2001-04-01",
"2001-05-01"
],
"xcalendar": "ethiopian",
"xcalendar": "chinese",
"y": [
"0100-01-01",
"0100-03-01"
Expand Down Expand Up @@ -111,8 +111,8 @@
"type": "date"
},
"zaxis": {
"title": "ethiopian",
"calendar": "ethiopian",
"title": "chinese",
"calendar": "chinese",
"type": "date"
},
"camera": {
Expand Down
100 changes: 100 additions & 0 deletions test/jasmine/tests/lib_date_test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
var isNumeric = require('fast-isnumeric');

var Lib = require('@src/lib');
var calComponent = require('@src/components/calendars');

// use only the parts of world-calendars that we've imported for our tests
var calendars = require('@src/components/calendars/calendars');

describe('dates', function() {
'use strict';
Expand Down Expand Up @@ -240,6 +245,7 @@ describe('dates', function() {
[
[undefined, '1970-01-01'],
['gregorian', '1970-01-01'],
['chinese', '1969-11-24'],
['coptic', '1686-04-23'],
['discworld', '1798-12-27'],
['ethiopian', '1962-04-23'],
Expand Down Expand Up @@ -284,6 +290,55 @@ describe('dates', function() {
expect(Lib.dateTime2ms(expected_lastinstant, calendar)).toBe(lastInstant, calendar);
});
});

it('should contain canonical ticks sundays, ranges for all calendars', function() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

very nice test 🍻

var calList = Object.keys(calendars.calendars).filter(function(v) {
return v !== 'gregorian';
});

var canonicalTick = calComponent.CANONICAL_TICK,
canonicalSunday = calComponent.CANONICAL_SUNDAY,
dfltRange = calComponent.DFLTRANGE;
expect(Object.keys(canonicalTick).length).toBe(calList.length);
expect(Object.keys(canonicalSunday).length).toBe(calList.length);
expect(Object.keys(dfltRange).length).toBe(calList.length);

calList.forEach(function(calendar) {
expect(Lib.dateTime2ms(canonicalTick[calendar], calendar)).toBeDefined(calendar);
var sunday = Lib.dateTime2ms(canonicalSunday[calendar], calendar);
// convert back implicitly with gregorian calendar
expect(Lib.formatDate(sunday, '%A')).toBe('Sunday', calendar);

expect(Lib.dateTime2ms(dfltRange[calendar][0], calendar)).toBeDefined(calendar);
expect(Lib.dateTime2ms(dfltRange[calendar][1], calendar)).toBeDefined(calendar);
});
});

it('should handle Chinese intercalary months correctly', function() {
var intercalaryDates = [
'1995-08i-01',
'1995-08i-29',
'1984-10i-15',
'2023-02i-29'
];
intercalaryDates.forEach(function(v) {
var ms = Lib.dateTime2ms(v, 'chinese');
expect(Lib.ms2DateTime(ms, 0, 'chinese')).toBe(v);

// should also work without leading zeros
var vShort = v.replace(/-0/g, '-');
expect(Lib.dateTime2ms(vShort, 'chinese')).toBe(ms, vShort);
});

var badIntercalaryDates = [
'1995-07i-01',
'1995-08i-30',
'1995-09i-01'
];
badIntercalaryDates.forEach(function(v) {
expect(Lib.dateTime2ms(v, 'chinese')).toBeUndefined(v);
});
});
});

describe('cleanDate', function() {
Expand Down Expand Up @@ -341,6 +396,51 @@ describe('dates', function() {
});
});

describe('incrementMonth', function() {
it('should include Chinese intercalary months', function() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should add a similar tests to world-calendars

var start = '1995-06-01';
var expected = [
'1995-07-01',
'1995-08-01',
'1995-08i-01',
'1995-09-01',
'1995-10-01',
'1995-11-01',
'1995-12-01',
'1996-01-01'
];
var tick = Lib.dateTime2ms(start, 'chinese');
expected.forEach(function(v) {
tick = Lib.incrementMonth(tick, 1, 'chinese');
expect(tick).toBe(Lib.dateTime2ms(v, 'chinese'), v);
});
});

it('should increment years even over leap years', function() {
var start = '1995-06-01';
var expected = [
'1996-06-01',
'1997-06-01',
'1998-06-01',
'1999-06-01',
'2000-06-01',
'2001-06-01',
'2002-06-01',
'2003-06-01',
'2004-06-01',
'2005-06-01',
'2006-06-01',
'2007-06-01',
'2008-06-01'
];
var tick = Lib.dateTime2ms(start, 'chinese');
expected.forEach(function(v) {
tick = Lib.incrementMonth(tick, 12, 'chinese');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

12? I guess incrementMonth does some magic over leap years

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes exactly: #1220 (comment)

expect(tick).toBe(Lib.dateTime2ms(v, 'chinese'), v);
});
});
});

describe('isJSDate', function() {
it('should return true for any Date object but not the equivalent numbers', function() {
[
Expand Down