Skip to content

Commit bad4e1d

Browse files
committed
[IMP] pivot: support iso_week_number granularity
closes #4228 Task: 3932401 Signed-off-by: Lucas Lefèvre (lul) <[email protected]>
1 parent f5a8c01 commit bad4e1d

File tree

6 files changed

+67
-1
lines changed

6 files changed

+67
-1
lines changed

src/helpers/dates.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,14 @@ export class DateTime {
105105
return this.jsDate.getUTCSeconds();
106106
}
107107

108+
getIsoWeek() {
109+
const date = new Date(this.jsDate.getTime());
110+
const dayNumber = date.getUTCDay() || 7;
111+
date.setUTCDate(date.getUTCDate() + 4 - dayNumber);
112+
const yearStart = new Date(Date.UTC(date.getUTCFullYear(), 0, 1));
113+
return Math.ceil(((date.getTime() - yearStart.getTime()) / 86400000 + 1) / 7);
114+
}
115+
108116
setFullYear(year: number) {
109117
return this.jsDate.setUTCFullYear(year);
110118
}

src/helpers/pivot/pivot_registry.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,14 @@ pivotRegistry.add("SPREADSHEET", {
4242
definition: SpreadsheetPivotRuntimeDefinition,
4343
externalData: false,
4444
onIterationEndEvaluation: (pivot: SpreadsheetPivot) => pivot.markAsDirtyForEvaluation(),
45-
granularities: ["year_number", "quarter_number", "month_number", "day_of_month", "day"],
45+
granularities: [
46+
"year_number",
47+
"quarter_number",
48+
"month_number",
49+
"iso_week_number",
50+
"day_of_month",
51+
"day",
52+
],
4653
isMeasureCandidate: (field: PivotField) => !["date", "boolean"].includes(field.type),
4754
isGroupable: () => true,
4855
});

src/helpers/pivot/spreadsheet_pivot/date_spreadsheet_pivot.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ export function createDate(dimension: PivotDimension, value: FieldValue["value"]
2222
case "month_number":
2323
number = date.getMonth();
2424
break;
25+
case "iso_week_number":
26+
number = date.getIsoWeek();
27+
break;
2528
case "day_of_month":
2629
number = date.getDate();
2730
break;
@@ -50,6 +53,10 @@ export function createDate(dimension: PivotDimension, value: FieldValue["value"]
5053
* set: { 43_831 },
5154
* values: { '43_831': 0 }
5255
* },
56+
* iso_week_number: {
57+
* set: { 43_831 },
58+
* values: { '43_831': 1 }
59+
* },
5360
* day_of_month: {
5461
* set: { 43_831 },
5562
* values: { '43_831': 1 }
@@ -76,6 +83,10 @@ const MAP_VALUE_DIMENSION_DATE: Record<
7683
set: new Set<FieldValue["value"]>(),
7784
values: {},
7885
},
86+
iso_week_number: {
87+
set: new Set<FieldValue["value"]>(),
88+
values: {},
89+
},
7990
day_of_month: {
8091
set: new Set<FieldValue["value"]>(),
8192
values: {},

tests/functions/dates.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,3 +1012,27 @@ describe("Number of digits in year/month/days in format", () => {
10121012
expect(parseDateTime("1-3-2002", locale)).toMatchObject({ format: "m-d-yyyy" });
10131013
});
10141014
});
1015+
1016+
describe("Datetime", () => {
1017+
test("isoWeekNumber", () => {
1018+
expect(parseDateTime("2020/01/01", locale)?.jsDate?.getIsoWeek()).toBe(1);
1019+
// Week 53 in 2020. The first Thursday in 2021 is on the 7th of January.
1020+
// So, 1st to 3rd of January are in week 53 of 2020 (Friday, Saturday and Sunday).
1021+
expect(parseDateTime("2021/01/01", locale)?.jsDate?.getIsoWeek()).toBe(53);
1022+
expect(parseDateTime("2021/01/02", locale)?.jsDate?.getIsoWeek()).toBe(53);
1023+
expect(parseDateTime("2021/01/03", locale)?.jsDate?.getIsoWeek()).toBe(53);
1024+
expect(parseDateTime("2021/01/04", locale)?.jsDate?.getIsoWeek()).toBe(1);
1025+
expect(parseDateTime("2021/01/05", locale)?.jsDate?.getIsoWeek()).toBe(1);
1026+
expect(parseDateTime("2021/01/06", locale)?.jsDate?.getIsoWeek()).toBe(1);
1027+
expect(parseDateTime("2021/01/07", locale)?.jsDate?.getIsoWeek()).toBe(1);
1028+
expect(parseDateTime("2021/03/10", locale)?.jsDate?.getIsoWeek()).toBe(10);
1029+
expect(parseDateTime("2021/05/16", locale)?.jsDate?.getIsoWeek()).toBe(19);
1030+
expect(parseDateTime("2021/05/17", locale)?.jsDate?.getIsoWeek()).toBe(20);
1031+
1032+
expect(parseDateTime("2024/01/01", locale)?.jsDate?.getIsoWeek()).toBe(1);
1033+
expect(parseDateTime("2024/02/29", locale)?.jsDate?.getIsoWeek()).toBe(9);
1034+
expect(parseDateTime("2024/12/29", locale)?.jsDate?.getIsoWeek()).toBe(52);
1035+
// 2nd of January 2025 is the first thursday of the year, so week 1 starts on the 30th of December 2024
1036+
expect(parseDateTime("2024/12/30", locale)?.jsDate?.getIsoWeek()).toBe(1);
1037+
});
1038+
});

tests/pivots/spreadsheet_pivot/date_spreadsheet_pivot.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ function createPivotDimension(granularity: string): PivotDimension {
1616
const YEAR_NUMBER_DIMENSION = createPivotDimension("year_number");
1717
const QUARTER_NUMBER_DIMENSION = createPivotDimension("quarter_number");
1818
const MONTH_NUMBER_DIMENSION = createPivotDimension("month_number");
19+
const ISO_WEEK_NUMBER_DIMENSION = createPivotDimension("iso_week_number");
1920
const DAY_OF_MONTH_DIMENSION = createPivotDimension("day_of_month");
2021
const DAY_DIMENSION = createPivotDimension("day");
2122

@@ -26,20 +27,23 @@ describe("Date Spreadsheet Pivot", () => {
2627
expect(createDate(YEAR_NUMBER_DIMENSION, d05_april_2024, DEFAULT_LOCALE)).toBe(2024);
2728
expect(createDate(QUARTER_NUMBER_DIMENSION, d05_april_2024, DEFAULT_LOCALE)).toBe(2);
2829
expect(createDate(MONTH_NUMBER_DIMENSION, d05_april_2024, DEFAULT_LOCALE)).toBe(3);
30+
expect(createDate(ISO_WEEK_NUMBER_DIMENSION, d05_april_2024, DEFAULT_LOCALE)).toBe(14);
2931
expect(createDate(DAY_OF_MONTH_DIMENSION, d05_april_2024, DEFAULT_LOCALE)).toBe(5);
3032
expect(createDate(DAY_DIMENSION, d05_april_2024, DEFAULT_LOCALE)).toBe(d05_april_2024);
3133

3234
const d04_may_2024 = 45_416;
3335
expect(createDate(YEAR_NUMBER_DIMENSION, d04_may_2024, DEFAULT_LOCALE)).toBe(2024);
3436
expect(createDate(QUARTER_NUMBER_DIMENSION, d04_may_2024, DEFAULT_LOCALE)).toBe(2);
3537
expect(createDate(MONTH_NUMBER_DIMENSION, d04_may_2024, DEFAULT_LOCALE)).toBe(4);
38+
expect(createDate(ISO_WEEK_NUMBER_DIMENSION, d04_may_2024, DEFAULT_LOCALE)).toBe(18);
3639
expect(createDate(DAY_OF_MONTH_DIMENSION, d04_may_2024, DEFAULT_LOCALE)).toBe(4);
3740
expect(createDate(DAY_DIMENSION, d04_may_2024, DEFAULT_LOCALE)).toBe(d04_may_2024);
3841

3942
const d01_january_2019 = 43_466;
4043
expect(createDate(YEAR_NUMBER_DIMENSION, d01_january_2019, DEFAULT_LOCALE)).toBe(2019);
4144
expect(createDate(QUARTER_NUMBER_DIMENSION, d01_january_2019, DEFAULT_LOCALE)).toBe(1);
4245
expect(createDate(MONTH_NUMBER_DIMENSION, d01_january_2019, DEFAULT_LOCALE)).toBe(0);
46+
expect(createDate(ISO_WEEK_NUMBER_DIMENSION, d01_january_2019, DEFAULT_LOCALE)).toBe(1);
4347
expect(createDate(DAY_OF_MONTH_DIMENSION, d01_january_2019, DEFAULT_LOCALE)).toBe(1);
4448
expect(createDate(DAY_DIMENSION, d01_january_2019, DEFAULT_LOCALE)).toBe(d01_january_2019);
4549
});
@@ -50,6 +54,7 @@ describe("Date Spreadsheet Pivot", () => {
5054
expect(createDate(YEAR_NUMBER_DIMENSION, d05_april_2024_15h, DEFAULT_LOCALE)).toBe(2024);
5155
expect(createDate(QUARTER_NUMBER_DIMENSION, d05_april_2024_15h, DEFAULT_LOCALE)).toBe(2);
5256
expect(createDate(MONTH_NUMBER_DIMENSION, d05_april_2024_15h, DEFAULT_LOCALE)).toBe(3);
57+
expect(createDate(ISO_WEEK_NUMBER_DIMENSION, d05_april_2024_15h, DEFAULT_LOCALE)).toBe(14);
5358
expect(createDate(DAY_OF_MONTH_DIMENSION, d05_april_2024_15h, DEFAULT_LOCALE)).toBe(5);
5459
expect(createDate(DAY_DIMENSION, d05_april_2024_15h, DEFAULT_LOCALE)).toBe(
5560
Math.floor(d05_april_2024_15h)

tests/pivots/spreadsheet_pivot/spreadsheet_pivot.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,17 @@ describe("Spreadsheet Pivot", () => {
420420
expect(getEvaluatedGrid(model, "B26:E26")).toEqual([["1", "2", "Total", ""]]);
421421
});
422422

423+
test("iso_week_number should be supported", () => {
424+
const model = createModelWithPivot("A1:I5");
425+
updatePivot(model, "1", {
426+
columns: [{ name: "Created on", granularity: "iso_week_number", order: "asc" }],
427+
rows: [],
428+
measures: [{ name: "Expected Revenue", aggregator: "sum" }],
429+
});
430+
setCellContent(model, "A26", `=pivot(1)`);
431+
expect(getEvaluatedGrid(model, "B26:F26")).toEqual([["5", "9", "14", "Total", ""]]);
432+
});
433+
423434
describe("Pivot reevaluation", () => {
424435
test("Pivot fields reevaluation", () => {
425436
const model = new Model({

0 commit comments

Comments
 (0)