Skip to content
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

More quarter support (add/subtract quarters; startOf/endOf quarter) #1586

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
24 changes: 21 additions & 3 deletions moment.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
isoDurationRegex = /^(-)?P(?:(?:([0-9,.]*)Y)?(?:([0-9,.]*)M)?(?:([0-9,.]*)D)?(?:T(?:([0-9,.]*)H)?(?:([0-9,.]*)M)?(?:([0-9,.]*)S)?)?|([0-9,.]*)W)$/,

// format tokens
formattingTokens = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|S{1,4}|X|zz?|ZZ?|.)/g,
formattingTokens = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Q|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|S{1,4}|X|zz?|ZZ?|.)/g,
localFormattingTokens = /(\[[^\[]*\])|(\\)?(LT|LL?L?L?|l{1,4})/g,

// parsing token regexes
Expand Down Expand Up @@ -123,6 +123,7 @@
w : 'week',
W : 'isoWeek',
M : 'month',
Q : 'quarter',
y : 'year',
DDD : 'dayOfYear',
e : 'weekday',
Expand Down Expand Up @@ -338,6 +339,7 @@
function Duration(duration) {
var normalizedInput = normalizeObjectUnits(duration),
years = normalizedInput.year || 0,
quarters = normalizedInput.quarter || 0,
months = normalizedInput.month || 0,
weeks = normalizedInput.week || 0,
days = normalizedInput.day || 0,
Expand All @@ -359,6 +361,7 @@
// which months you are are talking about, so we have to store
// it separately.
this._months = +months +
quarters * 3 +
years * 12;

this._data = {};
Expand Down Expand Up @@ -946,6 +949,8 @@
function getParseRegexForToken(token, config) {
var a, strict = config._strict;
switch (token) {
case 'Q':
return parseTokenOneDigit;
case 'DDDD':
return parseTokenThreeDigits;
case 'YYYY':
Expand Down Expand Up @@ -1037,6 +1042,12 @@
var a, datePartArray = config._a;

switch (token) {
// QUARTER
case 'Q':
if (input != null) {
datePartArray[MONTH] = (toInt(input) - 1) * 3;
}
break;
// MONTH
case 'M' : // fall through to MM
case 'MM' :
Expand Down Expand Up @@ -1970,6 +1981,7 @@
case 'year':
this.month(0);
/* falls through */
case 'quarter':
case 'month':
this.date(1);
/* falls through */
Expand All @@ -1996,6 +2008,11 @@
this.isoWeekday(1);
}

// quarters are also special
if (units === 'quarter') {
this.month(Math.floor(this.month() / 3) * 3);
}

return this;
},

Expand Down Expand Up @@ -2103,8 +2120,8 @@
return input == null ? dayOfYear : this.add("d", (input - dayOfYear));
},

quarter : function () {
return Math.ceil((this.month() + 1.0) / 3.0);
quarter : function (input) {
return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3);
},

weekYear : function (input) {
Expand Down Expand Up @@ -2251,6 +2268,7 @@
moment.fn.months = moment.fn.month;
moment.fn.weeks = moment.fn.week;
moment.fn.isoWeeks = moment.fn.isoWeek;
moment.fn.quarters = moment.fn.quarter;

// add aliased format methods
moment.fn.toJSON = moment.fn.toISOString;
Expand Down
52 changes: 34 additions & 18 deletions test/moment/add_subtract.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ exports.add = {
},

"add short" : function (test) {
test.expect(12);
test.expect(16);

var a = moment(), b, c;
var a = moment(), b, c, d;
a.year(2011);
a.month(9);
a.date(12);
Expand All @@ -28,19 +28,24 @@ exports.add = {
test.equal(a.add({w: 1}).date(), 20, 'Add week');
test.equal(a.add({M: 1}).month(), 10, 'Add month');
test.equal(a.add({y: 1}).year(), 2012, 'Add year');
test.equal(a.add({Q: 1}).month(), 1, 'Add quarter');

b = moment([2010, 0, 31]).add({M: 1});
c = moment([2010, 1, 28]).subtract({M: 1});
d = moment([2010, 1, 28]).subtract({Q: 1});

test.equal(b.month(), 1, 'add month, jan 31st to feb 28th');
test.equal(b.date(), 28, 'add month, jan 31st to feb 28th');
test.equal(c.month(), 0, 'subtract month, feb 28th to jan 28th');
test.equal(c.date(), 28, 'subtract month, feb 28th to jan 28th');
test.equal(d.month(), 10, 'subtract quarter, feb 28th 2010 to nov 28th 2009');
test.equal(d.date(), 28, 'subtract quarter, feb 28th 2010 to nov 28th 2009');
test.equal(d.year(), 2009, 'subtract quarter, feb 28th 2010 to nov 28th 2009');
test.done();
},

"add long" : function (test) {
test.expect(8);
test.expect(9);

var a = moment();
a.year(2011);
Expand All @@ -59,11 +64,12 @@ exports.add = {
test.equal(a.add({weeks: 1}).date(), 20, 'Add week');
test.equal(a.add({months: 1}).month(), 10, 'Add month');
test.equal(a.add({years: 1}).year(), 2012, 'Add year');
test.equal(a.add({quarters: 1}).month(), 1, 'Add quarter');
test.done();
},

"add long singular" : function (test) {
test.expect(8);
test.expect(9);

var a = moment();
a.year(2011);
Expand All @@ -82,11 +88,12 @@ exports.add = {
test.equal(a.add({week: 1}).date(), 20, 'Add week');
test.equal(a.add({month: 1}).month(), 10, 'Add month');
test.equal(a.add({year: 1}).year(), 2012, 'Add year');
test.equal(a.add({quarter: 1}).month(), 1, 'Add quarter');
test.done();
},

"add string long" : function (test) {
test.expect(9);
test.expect(10);

var a = moment(), b;
a.year(2011);
Expand All @@ -108,11 +115,12 @@ exports.add = {
test.equal(a.add('month', 1).month(), 10, 'Add month');
test.equal(a.add('year', 1).year(), 2012, 'Add year');
test.equal(b.add('day', '01').date(), 13, 'Add date');
test.equal(a.add('quarter', 1).month(), 1, 'Add quarter');
test.done();
},

"add string long singular" : function (test) {
test.expect(9);
test.expect(10);

var a = moment(), b;
a.year(2011);
Expand All @@ -134,11 +142,12 @@ exports.add = {
test.equal(a.add('months', 1).month(), 10, 'Add month');
test.equal(a.add('years', 1).year(), 2012, 'Add year');
test.equal(b.add('days', '01').date(), 13, 'Add date');
test.equal(a.add('quarters', 1).month(), 1, 'Add quarter');
test.done();
},

"add string short" : function (test) {
test.expect(8);
test.expect(9);

var a = moment();
a.year(2011);
Expand All @@ -157,11 +166,12 @@ exports.add = {
test.equal(a.add('w', 1).date(), 20, 'Add week');
test.equal(a.add('M', 1).month(), 10, 'Add month');
test.equal(a.add('y', 1).year(), 2012, 'Add year');
test.equal(a.add('Q', 1).month(), 1, 'Add quarter');
test.done();
},

"add string long reverse args" : function (test) {
test.expect(8);
test.expect(9);

var a = moment();
a.year(2011);
Expand All @@ -173,18 +183,19 @@ exports.add = {
a.milliseconds(500);

test.equal(a.add(50, 'millisecond').milliseconds(), 550, 'Add milliseconds');
test.equal(a.add(1, 'second', 1).seconds(), 9, 'Add seconds');
test.equal(a.add(1, 'minute', 1).minutes(), 8, 'Add minutes');
test.equal(a.add(1, 'hour', 1).hours(), 7, 'Add hours');
test.equal(a.add(1, 'day', 1).date(), 13, 'Add date');
test.equal(a.add(1, 'week', 1).date(), 20, 'Add week');
test.equal(a.add(1, 'month', 1).month(), 10, 'Add month');
test.equal(a.add(1, 'year', 1).year(), 2012, 'Add year');
test.equal(a.add(1, 'second').seconds(), 9, 'Add seconds');
test.equal(a.add(1, 'minute').minutes(), 8, 'Add minutes');
test.equal(a.add(1, 'hour').hours(), 7, 'Add hours');
test.equal(a.add(1, 'day').date(), 13, 'Add date');
test.equal(a.add(1, 'week').date(), 20, 'Add week');
test.equal(a.add(1, 'month').month(), 10, 'Add month');
test.equal(a.add(1, 'year').year(), 2012, 'Add year');
test.equal(a.add(1, 'quarter').month(), 1, 'Add quarter');
test.done();
},

"add string long singular reverse args" : function (test) {
test.expect(8);
test.expect(9);

var a = moment();
a.year(2011);
Expand All @@ -203,11 +214,12 @@ exports.add = {
test.equal(a.add(1, 'weeks').date(), 20, 'Add week');
test.equal(a.add(1, 'months').month(), 10, 'Add month');
test.equal(a.add(1, 'years').year(), 2012, 'Add year');
test.equal(a.add(1, 'quarters').month(), 1, 'Add quarter');
test.done();
},

"add string short reverse args" : function (test) {
test.expect(8);
test.expect(9);

var a = moment();
a.year(2011);
Expand All @@ -226,6 +238,7 @@ exports.add = {
test.equal(a.add(1, 'w').date(), 20, 'Add week');
test.equal(a.add(1, 'M').month(), 10, 'Add month');
test.equal(a.add(1, 'y').year(), 2012, 'Add year');
test.equal(a.add(1, 'Q').month(), 1, 'Add quarter');
test.done();
},

Expand All @@ -240,10 +253,12 @@ exports.add = {
var a = moment(new Date(2011, 2, 12, 5, 0, 0)),
b = moment(new Date(2011, 2, 12, 5, 0, 0)),
c = moment(new Date(2011, 2, 12, 5, 0, 0)),
d = moment(new Date(2011, 2, 12, 5, 0, 0));
d = moment(new Date(2011, 2, 12, 5, 0, 0)),
e = moment(new Date(2011, 2, 12, 5, 0, 0));
a.add('days', 1);
b.add('hours', 24);
c.add('months', 1);
e.add('quarter', 1);
test.equal(a.hours(), 5, 'adding days over DST difference should result in the same hour');
if (b.isDST() && !d.isDST()) {
test.equal(b.hours(), 6, 'adding hours over DST difference should result in a different hour');
Expand All @@ -253,6 +268,7 @@ exports.add = {
test.equal(b.hours(), 5, 'adding hours over DST difference should result in a same hour if the timezone does not have daylight savings time');
}
test.equal(c.hours(), 5, 'adding months over DST difference should result in the same hour');
test.equal(e.hours(), 5, 'adding quarters over DST difference should result in the same hour');
test.done();
}
};
3 changes: 3 additions & 0 deletions test/moment/create.js
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ exports.create = {
"string with format" : function (test) {
moment.lang('en');
var a = [
['YYYY-Q', '2014-4'],
['MM-DD-YYYY', '12-02-1999'],
['DD-MM-YYYY', '12-02-1999'],
['DD/MM/YYYY', '12/02/1999'],
Expand Down Expand Up @@ -661,6 +662,8 @@ exports.create = {
},

"strict parsing" : function (test) {
test.equal(moment("2014-", "YYYY-Q", true).isValid(), false, "fail missing quarter");

test.equal(moment("2012-05", "YYYY-MM", true).format("YYYY-MM"), "2012-05", "parse correct string");
test.equal(moment(" 2012-05", "YYYY-MM", true).isValid(), false, "fail on extra whitespace");
test.equal(moment("foo 2012-05", "[foo] YYYY-MM", true).format('YYYY-MM'), "2012-05", "handle fixed text");
Expand Down
4 changes: 2 additions & 2 deletions test/moment/normalizeUnits.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ exports.normalizeUnits = {
},

"normalize units" : function (test) {
var fullKeys = ["year", "month", "isoWeek", "week", "day", "hour", "minute", "second", "millisecond", "date", 'dayOfYear', 'weekday', 'isoWeekday', 'weekYear', 'isoWeekYear'],
aliases = ["y", "M", "W", "w", "d", "h", "m", "s", "ms", "D", 'DDD', 'e', 'E', 'gg', 'GG'],
var fullKeys = ["year", "quarter", "month", "isoWeek", "week", "day", "hour", "minute", "second", "millisecond", "date", 'dayOfYear', 'weekday', 'isoWeekday', 'weekYear', 'isoWeekYear'],
aliases = ["y", "Q", "M", "W", "w", "d", "h", "m", "s", "ms", "D", 'DDD', 'e', 'E', 'gg', 'GG'],
length = fullKeys.length,
fullKey,
fullKeyCaps,
Expand Down
Loading