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

fix(core): Add getUTCMonday, fix tests in U.S. timezone #879

Merged
merged 3 commits into from
Jun 17, 2024
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
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
Loading