fix: Infinite loop in timezones on the negative side of UTC (#12063)

* fix: Infinite loop in timezones on the negative side of UTC

* Update packages/features/calendars/lib/getAvailableDatesInMonth.test.ts

* Revert back to real system time after test

* Handle all dates as local time, given this all happens in the browser
This commit is contained in:
Alex van Andel 2023-10-25 14:18:25 +01:00 committed by GitHub
parent 327159c2ae
commit efc7be0b6b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 58 additions and 5 deletions

View File

@ -15,3 +15,5 @@ jobs:
- uses: ./.github/actions/yarn-install
# Should be an 8GB machine as per https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners
- run: yarn test
# We could add different timezones here that we need to run our tests in
- run: TZ=America/Los_Angeles yarn test -- --timeZoneDependentTestsOnly

View File

@ -1,4 +1,4 @@
import { describe, expect, test } from "vitest";
import { describe, expect, test, vi } from "vitest";
import { getAvailableDatesInMonth } from "@calcom/features/calendars/lib/getAvailableDatesInMonth";
import { daysInMonth, yyyymmdd } from "@calcom/lib/date-fns";
@ -8,7 +8,7 @@ describe("Test Suite: Date Picker", () => {
// *) Use right amount of days in given month. (28, 30, 31)
test("it returns the right amount of days in a given month", () => {
const currentDate = new Date();
const nextMonthDate = new Date(Date.UTC(currentDate.getFullYear(), currentDate.getMonth() + 1));
const nextMonthDate = new Date(currentDate.getFullYear(), currentDate.getMonth() + 1);
const result = getAvailableDatesInMonth({
browsingDate: nextMonthDate,
@ -35,5 +35,33 @@ describe("Test Suite: Date Picker", () => {
expect(result).toHaveLength(1);
});
test("it translates correctly regardless of system time", () => {
{
// test a date in negative UTC offset
vi.useFakeTimers().setSystemTime(new Date("2023-10-24T13:27:00.000-07:00"));
const currentDate = new Date();
const result = getAvailableDatesInMonth({
browsingDate: currentDate,
});
expect(result).toHaveLength(daysInMonth(currentDate) - currentDate.getDate() + 1);
}
{
// test a date in positive UTC offset
vi.useFakeTimers().setSystemTime(new Date("2023-10-24T13:27:00.000+07:00"));
const currentDate = new Date();
const result = getAvailableDatesInMonth({
browsingDate: currentDate,
});
expect(result).toHaveLength(daysInMonth(currentDate) - currentDate.getDate() + 1);
}
// Undo the forced time we applied earlier, reset to system default.
vi.setSystemTime(vi.getRealSystemTime());
vi.useRealTimers();
});
});
});

View File

@ -5,7 +5,7 @@ import { daysInMonth, yyyymmdd } from "@calcom/lib/date-fns";
// *) Dates in the past are not available.
// *) Use right amount of days in given month. (28, 30, 31)
export function getAvailableDatesInMonth({
browsingDate, // pass as UTC
browsingDate,
minDate = new Date(),
includedDates,
}: {
@ -15,12 +15,14 @@ export function getAvailableDatesInMonth({
}) {
const dates = [];
const lastDateOfMonth = new Date(
Date.UTC(browsingDate.getFullYear(), browsingDate.getMonth(), daysInMonth(browsingDate))
browsingDate.getFullYear(),
browsingDate.getMonth(),
daysInMonth(browsingDate)
);
for (
let date = browsingDate > minDate ? browsingDate : minDate;
date <= lastDateOfMonth;
date = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate() + 1))
date = new Date(date.getFullYear(), date.getMonth(), date.getDate() + 1)
) {
// intersect included dates
if (includedDates && !includedDates.includes(yyyymmdd(date))) {

View File

@ -1,6 +1,13 @@
import { defineWorkspace } from "vitest/config";
const packagedEmbedTestsOnly = process.argv.includes("--packaged-embed-tests-only");
const timeZoneDependentTestsOnly = process.argv.includes("--timeZoneDependentTestsOnly");
// eslint-disable-next-line turbo/no-undeclared-env-vars
const envTZ = process.env.TZ;
if (timeZoneDependentTestsOnly && !envTZ) {
throw new Error("TZ environment variable is not set");
}
// defineWorkspace provides a nice type hinting DX
const workspaces = packagedEmbedTestsOnly
? [
@ -11,6 +18,19 @@ const workspaces = packagedEmbedTestsOnly
},
},
]
: // It doesn't seem to be possible to fake timezone per test, so we rerun the entire suite with different TZ. See https://github.com/vitest-dev/vitest/issues/1575#issuecomment-1439286286
timeZoneDependentTestsOnly
? [
{
test: {
name: `TimezoneDependentTests:${envTZ}`,
include: ["packages/**/*.timezone.test.ts", "apps/**/*.timezone.test.ts"],
// TODO: Ignore the api until tests are fixed
exclude: ["**/node_modules/**/*", "packages/embeds/**/*"],
setupFiles: ["setupVitest.ts"],
},
},
]
: [
{
test: {
@ -20,6 +40,7 @@ const workspaces = packagedEmbedTestsOnly
setupFiles: ["setupVitest.ts"],
},
},
{
test: {
name: "@calcom/closecom",