/** * Tests $dateDiff expression. * @tags: [ * ] */ import "jstests/libs/query/sbe_assert_error_override.js"; import {executeAggregationTestCase} from "jstests/libs/query/aggregation_pipeline_utils.js"; const testDB = db.getSiblingDB(jsTestName()); const coll = testDB.collection; // Drop the test database. assert.commandWorked(testDB.dropDatabase()); const someDate = new Date("2020-11-01T18:23:36Z"); const aggregationPipelineWithDateDiff = [ { $project: { _id: false, date_diff: { $dateDiff: {startDate: "$startDate", endDate: "$endDate", unit: "$unit", timezone: "$timeZone"}, }, }, }, ]; const aggregationPipelineWithDateDiffAndStartOfWeek = [ { $project: { _id: false, date_diff: { $dateDiff: { startDate: "$startDate", endDate: "$endDate", unit: "$unit", timezone: "$timeZone", startOfWeek: "$startOfWeek", }, }, }, }, ]; const testCases = [ { // Parameters are constants, timezone is not specified. pipeline: [ { $project: { _id: true, date_diff: { $dateDiff: { startDate: new Date("2020-11-01T18:23:36Z"), endDate: new Date("2020-11-02T00:00:00Z"), unit: "hour", }, }, }, }, ], inputDocuments: [{_id: 1}], expectedResults: [{_id: 1, date_diff: NumberLong("6")}], }, { // Parameters are field paths. pipeline: aggregationPipelineWithDateDiffAndStartOfWeek, inputDocuments: [ { startDate: new Date("2020-11-01T18:23:36Z"), endDate: new Date("2020-11-02T00:00:00Z"), unit: "hour", timeZone: "America/New_York", startOfWeek: "IGNORED", // Ignored when unit is not week. }, ], expectedResults: [{date_diff: NumberLong("6")}], }, { // Parameters are field paths, 'timezone' is not specified. pipeline: [ { $project: { _id: false, date_diff: {$dateDiff: {startDate: "$startDate", endDate: "$endDate", unit: "$unit"}}, }, }, ], inputDocuments: [ { startDate: new Date("2020-11-01T18:23:36Z"), endDate: new Date("2020-11-02T00:00:00Z"), unit: "hour", }, ], expectedResults: [{date_diff: NumberLong("6")}], }, { // 'startDate' and 'endDate' are object ids. pipeline: [ { $project: { _id: false, date_diff: { $dateDiff: { startDate: "$_id", endDate: "$_id", unit: "millisecond", timezone: "America/New_York", }, }, }, }, ], inputDocuments: [{}], expectedResults: [{date_diff: NumberLong("0")}], }, { // 'startDate' and 'endDate' are timestamps. pipeline: [ { $project: { _id: false, date_diff: {$dateDiff: {startDate: "$ts", endDate: "$ts", unit: "millisecond"}}, }, }, ], inputDocuments: [{ts: new Timestamp()}], expectedResults: [{date_diff: NumberLong("0")}], }, { // Invalid 'startDate' type. pipeline: aggregationPipelineWithDateDiff, inputDocuments: [{startDate: "string", endDate: someDate, unit: "hour", timeZone: "UTC"}], expectedErrorCode: 5166307, }, { // Missing 'startDate' value in the document, invalid other fields. pipeline: aggregationPipelineWithDateDiff, inputDocuments: [{endDate: 1, unit: "century", timeZone: "INVALID"}], expectedResults: [{date_diff: null}], }, { // Null 'startDate'. pipeline: aggregationPipelineWithDateDiff, inputDocuments: [{startDate: null, endDate: someDate, unit: "hour", timeZone: "UTC"}], expectedResults: [{date_diff: null}], }, { // Invalid 'endDate' type. pipeline: aggregationPipelineWithDateDiff, inputDocuments: [{startDate: someDate, endDate: 1, unit: "hour", timeZone: "UTC"}], expectedErrorCode: 5166307, }, { // Missing 'endDate' value in the document, invalid other fields. pipeline: aggregationPipelineWithDateDiff, inputDocuments: [{startDate: "", unit: "epoch", timeZone: "INVALID"}], expectedResults: [{date_diff: null}], }, { // Null 'unit'. pipeline: aggregationPipelineWithDateDiff, inputDocuments: [{startDate: someDate, endDate: someDate, unit: null, timeZone: "UTC"}], expectedResults: [{date_diff: null}], }, { // Missing 'unit' value in the document, invalid other fields. pipeline: aggregationPipelineWithDateDiff, inputDocuments: [{startDate: 1, endDate: 2, timeZone: "INVALID"}], expectedResults: [{date_diff: null}], }, { // Invalid 'unit' type. pipeline: aggregationPipelineWithDateDiff, inputDocuments: [{startDate: someDate, endDate: someDate, unit: 5, timeZone: "UTC"}], expectedErrorCode: 5439013, }, { // Invalid 'unit' value. pipeline: aggregationPipelineWithDateDiff, inputDocuments: [{startDate: someDate, endDate: someDate, unit: "decade", timeZone: "UTC"}], expectedErrorCode: ErrorCodes.FailedToParse, }, { // Null 'timezone'. pipeline: aggregationPipelineWithDateDiff, inputDocuments: [{startDate: someDate, endDate: someDate, unit: "hour", timeZone: null}], expectedResults: [{date_diff: null}], }, { // Missing 'timezone' value in the document, invalid other fields. Result could be a null // answer or an error code depending whether pipeline is optimized. pipeline: aggregationPipelineWithDateDiff, inputDocuments: [{startDate: 1, endDate: 2, unit: "century"}], expectedResults: [{date_diff: null}], expectedErrorCode: ErrorCodes.FailedToParse, }, { // Invalid 'timezone' type. pipeline: aggregationPipelineWithDateDiff, inputDocuments: [{startDate: someDate, endDate: someDate, unit: "hour", timeZone: 1}], expectedErrorCode: 40517, }, { // Invalid 'timezone' value. pipeline: aggregationPipelineWithDateDiff, inputDocuments: [{startDate: someDate, endDate: someDate, unit: "hour", timeZone: "America/Invalid"}], expectedErrorCode: 40485, }, { // Specified 'startOfWeek'. pipeline: aggregationPipelineWithDateDiffAndStartOfWeek, inputDocuments: [ { startDate: new Date("2021-01-24T18:23:36Z"), // Sunday. endDate: new Date("2021-01-25T02:23:36Z"), // Monday. unit: "week", timeZone: "GMT", startOfWeek: "MONDAY", }, ], expectedResults: [{date_diff: NumberLong("1")}], }, { // Specified 'startOfWeek' and timezone. pipeline: aggregationPipelineWithDateDiffAndStartOfWeek, inputDocuments: [ { startDate: new Date("2021-01-17T05:00:00Z"), // Sunday in New York. endDate: new Date("2021-01-17T04:59:00Z"), // Saturday in New York. unit: "week", timeZone: "America/New_York", startOfWeek: "sunday", }, ], expectedResults: [{date_diff: NumberLong("-1")}], }, { // Unspecified 'startOfWeek' - defaults to Sunday. pipeline: [ { $project: { _id: false, date_diff: {$dateDiff: {startDate: "$startDate", endDate: "$endDate", unit: "week"}}, }, }, ], inputDocuments: [ { startDate: new Date("2021-01-24T18:23:36Z"), // Sunday. endDate: new Date("2021-01-25T02:23:36Z"), // Monday. }, ], expectedResults: [{date_diff: NumberLong("0")}], }, { // Null 'startOfWeek'. pipeline: aggregationPipelineWithDateDiffAndStartOfWeek, inputDocuments: [{startDate: someDate, endDate: someDate, unit: "week", startOfWeek: null}], expectedResults: [{date_diff: null}], }, { // Missing 'startOfWeek' value in the document, invalid other fields. pipeline: aggregationPipelineWithDateDiffAndStartOfWeek, inputDocuments: [{startDate: 1, endDate: 2, unit: "week", timeZone: 1}], expectedResults: [{date_diff: null}], }, { // Invalid 'startOfWeek' type. pipeline: aggregationPipelineWithDateDiffAndStartOfWeek, inputDocuments: [{startDate: someDate, endDate: someDate, unit: "week", timeZone: "GMT", startOfWeek: 1}], expectedErrorCode: 5439015, }, { // Invalid 'startOfWeek' type, unit is not the week. pipeline: aggregationPipelineWithDateDiffAndStartOfWeek, inputDocuments: [{startDate: someDate, endDate: someDate, unit: "hour", timeZone: "GMT", startOfWeek: 1}], expectedResults: [{date_diff: NumberLong("0")}], }, { // Invalid 'startOfWeek' value. pipeline: aggregationPipelineWithDateDiffAndStartOfWeek, inputDocuments: [ { startDate: someDate, endDate: someDate, unit: "week", timeZone: "GMT", startOfWeek: "FRIDIE", }, ], expectedErrorCode: 5439016, }, ]; testCases.forEach((testCase) => executeAggregationTestCase(coll, testCase));