Improve date handling (#94)

This commit is contained in:
Simon J
2022-02-02 13:26:01 +11:00
committed by GitHub
parent d0c80b2f20
commit df9a6d4663
3 changed files with 115 additions and 62 deletions

View File

@@ -1,13 +1,38 @@
import dayjs, { Dayjs } from "dayjs"; import dayjs, { Dayjs } from "dayjs";
export const isChristmas = (clock: Clock = SystemClock) => clock.now().month() == 11 && clock.now().date() == 25; function fixedDateMonthEvent(dateMonth: string) {
export const isMay4 = (clock: Clock = SystemClock) => clock.now().month() == 4 && clock.now().date() == 4; const date = Number.parseInt(dateMonth.split("/")[0]!);
export const isHalloween = (clock: Clock = SystemClock) => clock.now().month() == 9 && clock.now().date() == 31 const month = Number.parseInt(dateMonth.split("/")[1]!);
export const isHoli = (clock: Clock = SystemClock) => ["2022/03/18", "2023/03/07", "2024/03/25", "2025/03/14"].map(dayjs).find(it => it.isSame(clock.now())) != undefined return (clock: Clock = SystemClock) => {
export const isCNY = (clock: Clock = SystemClock) => ["2022/02/01", "2023/01/22", "2024/02/10", "2025/02/29"].map(dayjs).find(it => it.isSame(clock.now())) != undefined return clock.now().date() == date && clock.now().month() == month - 1;
export const isCNY_2022 = (clock: Clock = SystemClock) => clock.now().isSame(dayjs("2022/02/01")) };
export const isCNY_2023 = (clock: Clock = SystemClock) => clock.now().isSame(dayjs("2023/01/22")) }
export const isCNY_2024 = (clock: Clock = SystemClock) => clock.now().isSame(dayjs("2024/02/10"))
function fixedDateEvent(date: string) {
const dayjsDate = dayjs(date);
return (clock: Clock = SystemClock) => {
return clock.now().isSame(dayjsDate, "day");
};
}
function anyOf(rules: ((clock: Clock) => boolean)[]) {
return (clock: Clock = SystemClock) => {
return rules.find((rule) => rule(clock)) != undefined;
};
}
export const isChristmas = fixedDateMonthEvent("25/12");
export const isMay4 = fixedDateMonthEvent("04/05");
export const isHalloween = fixedDateMonthEvent("31/10");
export const isHoli = anyOf(
["2022/03/18", "2023/03/07", "2024/03/25", "2025/03/14"].map(fixedDateEvent)
)
export const isCNY_2022 = fixedDateEvent("2022/02/01");
export const isCNY_2023 = fixedDateEvent("2023/01/22");
export const isCNY_2024 = fixedDateEvent("2024/02/10");
export const isCNY_2025 = fixedDateEvent("2025/02/29");
export const isCNY = anyOf([isCNY_2022, isCNY_2023, isCNY_2024, isCNY_2025]);
export interface Clock { export interface Clock {
now(): Dayjs; now(): Dayjs;
@@ -22,7 +47,8 @@ export class FixedClock implements Clock {
this.time = time; this.time = time;
} }
add = (t: number, unit: dayjs.UnitTypeShort) => this.time = this.time.add(t, unit) add = (t: number, unit: dayjs.UnitTypeShort) =>
(this.time = this.time.add(t, unit));
now = () => this.time; now = () => this.time;
} }

View File

@@ -1,58 +1,85 @@
import dayjs from "dayjs"; import { randomInt } from "crypto";
import { isChristmas, isCNY, isHalloween, isHoli } from "../src/clock"; import dayjs, { Dayjs } from "dayjs";
import timezone from "dayjs/plugin/timezone";
dayjs.extend(timezone);
describe("isChristmas", () => { import { Clock, isChristmas, isCNY, isCNY_2022, isCNY_2023, isCNY_2024, isCNY_2025, isHalloween, isHoli, isMay4 } from "../src/clock";
["2000/12/25", "2022/12/25", "2030/12/25"].forEach((date) => {
it(`should return true for ${date} regardless of year`, () => {
expect(isChristmas({ now: () => dayjs(date) })).toEqual(true);
const randomDate = () => dayjs().subtract(randomInt(1, 1000), 'days');
const randomDates = (count: number, exclude: string[]) => {
const result: Dayjs[] = [];
while(result.length < count) {
const next = randomDate();
if(!exclude.find(it => dayjs(it).isSame(next, 'date'))) {
result.push(next)
}
}
return result
}
function describeFixedDateMonthEvent(
name: string,
dateMonth: string,
f: (clock: Clock) => boolean
) {
const randomYear = randomInt(2020, 3000);
const date = dateMonth.split("/")[0];
const month = dateMonth.split("/")[1];
describe(name, () => {
it(`should return true for ${randomYear}-${month}-${date}T00:00:00 ragardless of year`, () => {
expect(f({ now: () => dayjs(`${randomYear}-${month}-${date}T00:00:00Z`) })).toEqual(true);
}); });
it(`should return true for ${randomYear}-${month}-${date}T12:00:00 regardless of year`, () => {
expect(f({ now: () => dayjs(`${randomYear}-${month}-${date}T12:00:00Z`) })).toEqual(true);
});
it(`should return true for ${randomYear}-${month}-${date}T23:59:00 regardless of year`, () => {
expect(f({ now: () => dayjs(`${randomYear}-${month}-${date}T23:59:00`) })).toEqual(true);
}); });
["2000/12/24", "2000/12/26", "2021/01/01"].forEach((date) => { ["2000/12/24", "2000/12/26", "2021/01/01"].forEach((date) => {
it(`should return false for ${date} regardless of year`, () => { it(`should return false for ${date}`, () => {
expect(isChristmas({ now: () => dayjs(date) })).toEqual(false); expect(f({ now: () => dayjs(date) })).toEqual(false);
}); });
}); });
}); });
}
describe("isHalloween", () => { function describeFixedDateEvent(
["2000/10/31", "2022/10/31", "2030/10/31"].forEach((date) => { name: string,
it(`should return true for ${date} regardless of year`, () => { dates: string[],
expect(isHalloween({ now: () => dayjs(date) })).toEqual(true); f: (clock: Clock) => boolean
) {
describe(name, () => {
dates.forEach((date) => {
it(`should return true for ${date}T00:00:00`, () => {
expect(f({ now: () => dayjs(`${date}T00:00:00`) })).toEqual(true);
});
it(`should return true for ${date}T23:59:59`, () => {
expect(f({ now: () => dayjs(`${date}T23:59:59`) })).toEqual(true);
}); });
}); });
["2000/09/31", "2000/10/30", "2021/01/01"].forEach((date) => { randomDates(10, dates).forEach((date) => {
it(`should return false for ${date} regardless of year`, () => { it(`should return false for ${date}`, () => {
expect(isHalloween({ now: () => dayjs(date) })).toEqual(false); expect(f({ now: () => dayjs(date) })).toEqual(false);
}); });
}); });
}); });
}
describe("isHoli", () => { describeFixedDateMonthEvent("christmas", "25/12", isChristmas);
["2022/03/18", "2023/03/07", "2024/03/25", "2025/03/14"].forEach((date) => { describeFixedDateMonthEvent("halloween", "31/10", isHalloween);
it(`should return true for ${date} regardless of year`, () => { describeFixedDateMonthEvent("may4", "04/05", isMay4);
expect(isHoli({ now: () => dayjs(date) })).toEqual(true);
});
});
["2000/09/31", "2000/10/30", "2021/01/01"].forEach((date) => { describeFixedDateEvent("holi", ["2022-03-18", "2023-03-07", "2024-03-25", "2025-03-14"], isHoli);
it(`should return false for ${date} regardless of year`, () => { describeFixedDateEvent("cny", ["2022-02-01", "2023-01-22", "2024-02-10", "2025-02-29"], isCNY);
expect(isHoli({ now: () => dayjs(date) })).toEqual(false); describeFixedDateEvent("cny 2022", ["2022-02-01"], isCNY_2022);
}); describeFixedDateEvent("cny 2023", ["2023/01/22"], isCNY_2023);
}); describeFixedDateEvent("cny 2024", ["2024/02/10"], isCNY_2024);
}); describeFixedDateEvent("cny 2025", ["2025/02/29"], isCNY_2025);
describe("isCNY", () => {
["2022/02/01", "2023/01/22", "2024/02/10", "2025/02/29"].forEach((date) => {
it(`should return true for ${date} regardless of year`, () => {
expect(isCNY({ now: () => dayjs(date) })).toEqual(true);
});
});
["2000/09/31", "2000/10/30", "2021/01/01"].forEach((date) => {
it(`should return false for ${date} regardless of year`, () => {
expect(isCNY({ now: () => dayjs(date) })).toEqual(false);
});
});
});

View File

@@ -54,7 +54,7 @@
] ]
/* List of folders to include type definitions from. */, /* List of folders to include type definitions from. */,
// "types": ["src/customTypes/scale-that-svg.d.ts"], /* Type declaration files to be included in compilation. */ // "types": ["src/customTypes/scale-that-svg.d.ts"], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */