Skip to content

Commit

Permalink
fix(core): Add getUTCMonday, fix tests in U.S. timezone (#879)
Browse files Browse the repository at this point in the history
  • Loading branch information
donmccurdy authored Jun 17, 2024
1 parent 353b162 commit 6a3bcf1
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 5 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Not released

- Fix time zone handling in week counts, separate getMonday and getUTCMonday utilities [#879](https://github.com/CartoDB/carto-react/pull/879)

## 3.0.0

### 3.0.0-alpha.12 (2024-06-14)
Expand Down
66 changes: 66 additions & 0 deletions packages/react-core/__tests__/utils/dateUtils.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { getMonday, getUTCMonday } from '../../src/utils/dateUtils';

const TEST_CASES = [
{
title: 'monday',
date: [2023, 9, 4],
expected: [2023, 9, 4]
},
{
title: 'monday, cross year',
date: [2014, 12, 1],
expected: [2014, 12, 1]
},
{
title: 'monday, february/march normal year',
date: [2015, 2, 23],
expected: [2015, 2, 23]
},
{
title: 'monday, february/march step year',
date: [2016, 2, 22],
expected: [2016, 2, 22]
},
{
title: 'sunday, cross year',
date: [2023, 1, 1],
expected: [2022, 12, 26]
},
{
title: 'saturday, february/march step year',
date: [2024, 3, 2],
expected: [2024, 2, 26]
}
];

/** Returns midnight (local time) for given year/month/date. */
function createDate([year, month, date]) {
return new Date(year, month - 1, date);
}

/** Returns midnight (UTC) for given year/month/date. */
function createUTCDate([year, month, date]) {
return new Date(Date.UTC(year, month - 1, date));
}

describe('getMonday', () => {
for (const { date, expected, title } of TEST_CASES) {
const expectedString = createDate(expected).toLocaleString();
it(`${date} ===> ${expectedString} - ${title}`, () => {
const local = getMonday(createDate(date));
const localString = new Date(local).toLocaleString();
expect(localString).toBe(expectedString);
});
}
});

describe('getUTCMonday', () => {
for (const { date, expected, title } of TEST_CASES) {
const expectedString = createUTCDate(expected).toISOString();
it(`${date} ===> ${expectedString} - ${title}`, () => {
const utc = getUTCMonday(createUTCDate(date));
const utcString = new Date(utc).toISOString();
expect(utcString).toBe(expectedString);
});
}
});
2 changes: 1 addition & 1 deletion packages/react-core/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export {
REQUEST_GET_MAX_URL_LENGTH
} from './utils/requestsUtils';

export { getMonday } from './utils/dateUtils';
export { getMonday, getUTCMonday } from './utils/dateUtils';

export { InvalidColumnError } from './utils/InvalidColumnError';

Expand Down
2 changes: 1 addition & 1 deletion packages/react-core/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export {
REQUEST_GET_MAX_URL_LENGTH
} from './utils/requestsUtils';

export { getMonday } from './utils/dateUtils';
export { getMonday, getUTCMonday } from './utils/dateUtils';

export { InvalidColumnError } from './utils/InvalidColumnError';

Expand Down
4 changes: 2 additions & 2 deletions packages/react-core/src/operations/groupByDate.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { getMonday } from '../utils/dateUtils';
import { getUTCMonday } from '../utils/dateUtils';
import { aggregate, aggregationFunctions } from './aggregation';
import { GroupDateTypes } from './constants/GroupDateTypes';

const GROUP_KEY_FN_MAPPING = {
// @ts-ignore
[GroupDateTypes.YEARS]: (date) => Date.UTC(date.getUTCFullYear()),
[GroupDateTypes.MONTHS]: (date) => Date.UTC(date.getUTCFullYear(), date.getUTCMonth()),
[GroupDateTypes.WEEKS]: (date) => getMonday(date),
[GroupDateTypes.WEEKS]: (date) => getUTCMonday(date),
[GroupDateTypes.DAYS]: (date) =>
Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()),
[GroupDateTypes.HOURS]: (date) =>
Expand Down
17 changes: 17 additions & 0 deletions packages/react-core/src/utils/dateUtils.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,24 @@
/**
* Returns midnight (local time) on the Monday preceeding a given date, in
* milliseconds since the UNIX epoch.
*/
export function getMonday(date) {
const dateCp = new Date(date);
const day = dateCp.getDay();
const diff = dateCp.getDate() - day + (day ? 1 : -6); // adjust when day is sunday
dateCp.setDate(diff);
dateCp.setHours(0, 0, 0, 0);
return dateCp.getTime();
}

/**
* Returns midnight (UTC) on the Monday preceeding a given date, in
* milliseconds since the UNIX epoch.
*/
export function getUTCMonday(date) {
const dateCp = new Date(date);
const day = dateCp.getUTCDay();
const diff = dateCp.getUTCDate() - day + (day ? 1 : -6); // adjust when day is sunday
dateCp.setUTCDate(diff);
return Date.UTC(dateCp.getUTCFullYear(), dateCp.getUTCMonth(), dateCp.getUTCDate());
}
9 changes: 8 additions & 1 deletion packages/react-ui/__tests__/widgets/utils/timeFormat.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,19 @@ import { formatBucketRange } from '../../../src/widgets/TimeSeriesWidgetUI/utils
describe('timeFormat', () => {
describe('formatBucketRange', () => {
describe('buckets from date', () => {
/** https://stackoverflow.com/a/33909265 */
function parseISOLocal(s) {
var b = s.split(/\D/);
return new Date(b[0], b[1] - 1, b[2], b[3] ?? 0, b[4] ?? 0, b[5] ?? 0);
}
function defineCases(cases) {
cases.forEach(({ date, expected, title, ...params }) =>
it(`${date} / ${params.stepMultiplier ?? 1} ${
params.stepSize
} ===> ${expected} ${title ? `- ${title}` : ''}`, () =>
expect(formatBucketRange({ date: new Date(date), ...params })).toBe(expected))
expect(formatBucketRange({ date: parseISOLocal(date), ...params })).toBe(
expected
))
);
}
describe('second', () => {
Expand Down

0 comments on commit 6a3bcf1

Please sign in to comment.