mirror of https://github.com/mongodb/mongo
248 lines
12 KiB
JavaScript
248 lines
12 KiB
JavaScript
// SERVER-7695: Add $isoWeek, $isoWeekYear, and $isoDayOfWeek aggregation expressions.
|
|
import {DateUtil} from "jstests/libs/dateutil.js";
|
|
|
|
const coll = db[jsTestName()];
|
|
let testOpCount = 0;
|
|
|
|
coll.drop();
|
|
|
|
// Seed collection so that the pipeline will execute.
|
|
assert.commandWorked(coll.insert({date: ISODate("2021-04-14T04:48:00.00Z")}));
|
|
|
|
/**
|
|
* Helper for testing that 'op' returns 'expResult'.
|
|
*/
|
|
function testOp(op, value, expResult) {
|
|
testOpCount++;
|
|
let pipeline = [{$project: {_id: 0, result: {}}}];
|
|
pipeline[0].$project.result[op] = value;
|
|
let res = assert.commandWorked(coll.runCommand("aggregate", {pipeline: pipeline, cursor: {}}));
|
|
|
|
// in the case of $dateToString the date is on property date
|
|
let date = value.date || value;
|
|
if (date.valueOf() < 0 && _isWindows() && res.code === 16422) {
|
|
// some versions of windows (but not all) fail with dates before 1970
|
|
print("skipping test of " + date.tojson() + " because system doesn't support old dates");
|
|
return;
|
|
}
|
|
|
|
if (date.valueOf() / 1000 < -2 * 1024 * 1024 * 1024 && res.code == 16421) {
|
|
// we correctly detected that we are outside of the range of a 32-bit time_t
|
|
print("skipping test of " + date.tojson() + " because it is outside of time_t range");
|
|
return;
|
|
}
|
|
|
|
assert.eq(res.cursor.firstBatch[0].result, expResult, tojson(pipeline));
|
|
}
|
|
|
|
// While development, there was a bug which caused an error with $dateToString if the order of
|
|
// %V and %G changed, so I added this test to prevent regression.
|
|
testOp("$dateToString", {date: new Date("1900-12-31T23:59:59Z"), format: "%V-%G"}, "01-1901");
|
|
// This was failing, but it shouldn't as it is the same as above, only rotated.
|
|
testOp("$dateToString", {date: new Date("1900-12-31T23:59:59Z"), format: "%G-%V"}, "1901-01");
|
|
|
|
// Test an example where $isoDayOfWeek depends on stored values, and therefore is not eligible for a
|
|
// constant folding optimization.
|
|
testOp("$isoDayOfWeek", "$date", 3);
|
|
|
|
// 1900 is special because it's devisible by 4 and by 100 but not 400 so it's not a leap year.
|
|
// 2000 is special, because it's devisible by 4, 100, 400 and so it is a leap year.
|
|
const years = {
|
|
common: [
|
|
1900, // Starting and ending on Monday (special).
|
|
2002, // Starting and ending on Tuesday.
|
|
2014, // Starting and ending on Wednesday.
|
|
2015, // Starting and ending on Thursday.
|
|
2010, // Starting and ending on Friday.
|
|
2011, // Starting and ending on Saturday.
|
|
2006, // Starting and ending on Sunday.
|
|
],
|
|
leap: [
|
|
1996, // Starting on Monday, ending on Tuesday.
|
|
2008, // Starting on Tuesday, ending on Wednesday.
|
|
1992, // Starting on Wednesday, ending on Thursday.
|
|
2004, // Starting on Thursday, ending on Friday.
|
|
2016, // Starting on Friday, ending on Saturday.
|
|
2000, // Starting on Saturday, ending on Sunday (special).
|
|
2012, // Starting on Sunday, ending on Monday.
|
|
],
|
|
commonAfterLeap: [
|
|
2001, // Starting and ending on Monday.
|
|
2013, // Starting and ending on Tuesday.
|
|
1997, // Starting and ending on Wednesday.
|
|
2009, // Starting and ending on Thursday.
|
|
1993, // Starting and ending on Friday.
|
|
2005, // Starting and ending on Saturday.
|
|
2017, // Starting and ending on Sunday.
|
|
],
|
|
};
|
|
|
|
const MONDAY = 1;
|
|
const TUESDAY = 2;
|
|
const WEDNESDAY = 3;
|
|
const THURSDAY = 4;
|
|
const FRIDAY = 5;
|
|
const SATURDAY = 6;
|
|
const SUNDAY = 7;
|
|
|
|
["common", "leap", "commonAfterLeap"].forEach(function (type) {
|
|
years[type].forEach(function (year, day) {
|
|
// forEach starts indexing at zero but weekdays start with Monday on 1 so we add +1.
|
|
day = day + 1;
|
|
let newYear = DateUtil.getNewYear(year);
|
|
let endOfFirstWeekInYear = DateUtil.getEndOfFirstWeekInYear(year, day);
|
|
let startOfSecondWeekInYear = DateUtil.getStartOfSecondWeekInYear(year, day);
|
|
let birthday = DateUtil.getBirthday(year);
|
|
let endOfSecondToLastWeekInYear = DateUtil.getEndOfSecondToLastWeekInYear(year, day, type);
|
|
let startOfLastWeekInYear = DateUtil.getStartOfLastWeekInYear(year, day, type);
|
|
let newYearsEve = DateUtil.getNewYearsEve(year);
|
|
|
|
testOp("$isoDayOfWeek", newYear, day);
|
|
testOp("$isoDayOfWeek", endOfFirstWeekInYear, SUNDAY);
|
|
testOp("$isoDayOfWeek", startOfSecondWeekInYear, MONDAY);
|
|
testOp("$isoDayOfWeek", endOfSecondToLastWeekInYear, SUNDAY);
|
|
testOp("$isoDayOfWeek", startOfLastWeekInYear, MONDAY);
|
|
if (type === "leap") {
|
|
testOp("$isoDayOfWeek", newYearsEve, DateUtil.shiftWeekday(day, 1));
|
|
} else {
|
|
testOp("$isoDayOfWeek", newYearsEve, day);
|
|
}
|
|
|
|
if (type === "leap") {
|
|
testOp("$isoDayOfWeek", birthday, DateUtil.shiftWeekday(day, 4));
|
|
} else {
|
|
testOp("$isoDayOfWeek", birthday, DateUtil.shiftWeekday(day, 3));
|
|
}
|
|
|
|
testOp("$isoWeekYear", birthday, year);
|
|
// In leap years staring on Thursday, the birthday is in week 28, every year else it is
|
|
// in week 27.
|
|
if (type === "leap" && day === THURSDAY) {
|
|
testOp("$isoWeek", birthday, 28);
|
|
} else {
|
|
testOp("$isoWeek", birthday, 27);
|
|
}
|
|
|
|
if (day <= THURSDAY) {
|
|
// A year starting between Monday and Thursday will always start in week 1.
|
|
testOp("$isoWeek", newYear, 1);
|
|
testOp("$isoWeekYear", newYear, year);
|
|
testOp("$isoWeek", endOfFirstWeekInYear, 1);
|
|
testOp("$isoWeekYear", endOfFirstWeekInYear, year);
|
|
testOp("$isoWeek", startOfSecondWeekInYear, 2);
|
|
testOp("$isoWeekYear", startOfSecondWeekInYear, year);
|
|
testOp("$dateToString", {format: "%G-W%V-%u", date: newYear}, "" + year + "-W01-" + day);
|
|
} else if (day == FRIDAY || (day == SATURDAY && type === "commonAfterLeap")) {
|
|
// A year starting on Friday will always start with week 53 of the previous year.
|
|
// A common year starting on a Saturday and after a leap year will also start with
|
|
// week 53 of the previous year.
|
|
testOp("$isoWeek", newYear, 53);
|
|
testOp("$isoWeekYear", newYear, year - 1);
|
|
testOp("$isoWeek", endOfFirstWeekInYear, 53);
|
|
testOp("$isoWeekYear", endOfFirstWeekInYear, year - 1);
|
|
testOp("$isoWeek", startOfSecondWeekInYear, 1);
|
|
testOp("$isoWeekYear", startOfSecondWeekInYear, year);
|
|
testOp("$dateToString", {format: "%G-W%V-%u", date: newYear}, "" + (year - 1) + "-W53-" + day);
|
|
} else {
|
|
// A year starting on Saturday (except after a leap year) or Sunday will always
|
|
// start with week 52 of the previous year.
|
|
testOp("$isoWeek", newYear, 52);
|
|
testOp("$isoWeekYear", newYear, year - 1);
|
|
testOp("$isoWeek", endOfFirstWeekInYear, 52);
|
|
testOp("$isoWeekYear", endOfFirstWeekInYear, year - 1);
|
|
testOp("$isoWeek", startOfSecondWeekInYear, 1);
|
|
testOp("$isoWeekYear", startOfSecondWeekInYear, year);
|
|
testOp("$dateToString", {format: "%G-W%V-%u", date: newYear}, "" + (year - 1) + "-W52-" + day);
|
|
}
|
|
|
|
if (type === "leap") {
|
|
if (day <= TUESDAY) {
|
|
// A leap year starting between Monday and Tuesday will always end in week 1 of
|
|
// the next year.
|
|
testOp("$isoWeek", newYearsEve, 1);
|
|
testOp("$isoWeekYear", newYearsEve, year + 1);
|
|
testOp("$isoWeek", endOfSecondToLastWeekInYear, 52);
|
|
testOp("$isoWeekYear", endOfSecondToLastWeekInYear, year);
|
|
testOp("$isoWeek", startOfLastWeekInYear, 1);
|
|
testOp("$isoWeekYear", startOfLastWeekInYear, year + 1);
|
|
testOp(
|
|
"$dateToString",
|
|
{format: "%G-W%V-%u", date: newYearsEve},
|
|
"" + (year + 1) + "-W01-" + DateUtil.shiftWeekday(day, 1),
|
|
);
|
|
} else if (day <= THURSDAY) {
|
|
// A leap year starting on Wednesday or Thursday will always end with week 53.
|
|
testOp("$isoWeek", newYearsEve, 53);
|
|
testOp("$isoWeekYear", newYearsEve, year);
|
|
testOp("$isoWeek", endOfSecondToLastWeekInYear, 52);
|
|
testOp("$isoWeekYear", endOfSecondToLastWeekInYear, year);
|
|
testOp("$isoWeek", startOfLastWeekInYear, 53);
|
|
testOp("$isoWeekYear", startOfLastWeekInYear, year);
|
|
testOp(
|
|
"$dateToString",
|
|
{format: "%G-W%V-%u", date: newYearsEve},
|
|
"" + year + "-W53-" + DateUtil.shiftWeekday(day, 1),
|
|
);
|
|
} else if (day <= SATURDAY) {
|
|
// A leap year starting on Friday or Sarturday will always and with week 52
|
|
testOp("$isoWeek", newYearsEve, 52);
|
|
testOp("$isoWeekYear", newYearsEve, year);
|
|
testOp("$isoWeek", endOfSecondToLastWeekInYear, 51);
|
|
testOp("$isoWeekYear", endOfSecondToLastWeekInYear, year);
|
|
testOp("$isoWeek", startOfLastWeekInYear, 52);
|
|
testOp("$isoWeekYear", startOfLastWeekInYear, year);
|
|
testOp(
|
|
"$dateToString",
|
|
{format: "%G-W%V-%u", date: newYearsEve},
|
|
"" + year + "-W52-" + DateUtil.shiftWeekday(day, 1),
|
|
);
|
|
} else {
|
|
// A leap year starting on Sunday will always end with week 1
|
|
testOp("$isoWeek", newYearsEve, 1);
|
|
testOp("$isoWeekYear", newYearsEve, year + 1);
|
|
testOp("$isoWeek", endOfSecondToLastWeekInYear, 51);
|
|
testOp("$isoWeekYear", endOfSecondToLastWeekInYear, year);
|
|
testOp("$isoWeek", startOfLastWeekInYear, 52);
|
|
testOp("$isoWeekYear", startOfLastWeekInYear, year);
|
|
testOp(
|
|
"$dateToString",
|
|
{format: "%G-W%V-%u", date: newYearsEve},
|
|
"" + (year + 1) + "-W01-" + DateUtil.shiftWeekday(day, 1),
|
|
);
|
|
}
|
|
} else {
|
|
if (day <= WEDNESDAY) {
|
|
// A common year starting between Monday and Wednesday will always end in week 1
|
|
// of the next year.
|
|
testOp("$isoWeek", newYearsEve, 1);
|
|
testOp("$isoWeekYear", newYearsEve, year + 1);
|
|
testOp("$isoWeek", endOfSecondToLastWeekInYear, 52);
|
|
testOp("$isoWeekYear", endOfSecondToLastWeekInYear, year);
|
|
testOp("$isoWeek", startOfLastWeekInYear, 1);
|
|
testOp("$isoWeekYear", startOfLastWeekInYear, year + 1);
|
|
testOp("$dateToString", {format: "%G-W%V-%u", date: newYearsEve}, "" + (year + 1) + "-W01-" + day);
|
|
} else if (day === THURSDAY) {
|
|
// A common year starting on Thursday will always end with week 53.
|
|
testOp("$isoWeek", newYearsEve, 53);
|
|
testOp("$isoWeekYear", newYearsEve, year);
|
|
testOp("$isoWeek", endOfSecondToLastWeekInYear, 52);
|
|
testOp("$isoWeekYear", endOfSecondToLastWeekInYear, year);
|
|
testOp("$isoWeek", startOfLastWeekInYear, 53);
|
|
testOp("$isoWeekYear", startOfLastWeekInYear, year);
|
|
testOp("$dateToString", {format: "%G-W%V-%u", date: newYearsEve}, "" + year + "-W53-" + day);
|
|
} else {
|
|
// A common year starting on between Friday and Sunday will always end with week
|
|
// 52.
|
|
testOp("$isoWeek", newYearsEve, 52);
|
|
testOp("$isoWeekYear", newYearsEve, year);
|
|
testOp("$isoWeek", endOfSecondToLastWeekInYear, 51);
|
|
testOp("$isoWeekYear", endOfSecondToLastWeekInYear, year);
|
|
testOp("$isoWeek", startOfLastWeekInYear, 52);
|
|
testOp("$isoWeekYear", startOfLastWeekInYear, year);
|
|
testOp("$dateToString", {format: "%G-W%V-%u", date: newYearsEve}, "" + year + "-W52-" + day);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
assert.eq(testOpCount, 486, "unexpected number of calls to 'testOp()'");
|