code refactor

This commit is contained in:
MaysWind
2025-06-08 02:10:11 +08:00
parent 8c71f03f6f
commit ce4bca8272
14 changed files with 364 additions and 467 deletions
+43 -45
View File
@@ -1,30 +1,29 @@
// Unit tests for fiscal year functions
import moment from 'moment-timezone';
import { describe, expect, test, beforeAll } from '@jest/globals';
import fs from 'fs';
import path from 'path';
import { describe, expect, test, beforeAll } from '@jest/globals';
import moment from 'moment-timezone';
// Import all the fiscal year functions from the lib
import { FiscalYearStart, FiscalYearUnixTime } from '@/core/fiscalyear.ts';
import {
getFiscalYearFromUnixTime,
getFiscalYearStartUnixTime,
getFiscalYearEndUnixTime,
getFiscalYearTimeRangeFromUnixTime,
getAllFiscalYearsStartAndEndUnixTimes,
getFiscalYearTimeRangeFromYear
getFiscalYearTimeRangeFromYear,
formatUnixTime
} from '@/lib/datetime.ts';
import { formatUnixTime } from '@/lib/datetime.ts';
import { FiscalYearStart, FiscalYearUnixTime } from '@/core/fiscalyear.ts';
// Set test environment timezone to UTC, since the test data constants are in UTC
beforeAll(() => {
moment.tz.setDefault('UTC');
});
// UTILITIES
function importTestData(datasetName: string): any[] {
function importTestData(datasetName: string): unknown[] {
const data = JSON.parse(
fs.readFileSync(path.join(__dirname, 'fiscal_year.data.json'), 'utf8')
);
@@ -74,13 +73,13 @@ const TEST_FISCAL_YEAR_START_PRESETS: Record<string, FiscalYearStartConfig> = {
// VALIDATE FISCAL YEAR START PRESETS
describe('validateFiscalYearStart', () => {
Object.values(TEST_FISCAL_YEAR_START_PRESETS).forEach((testFiscalYearStart) => {
test(`should return true if fiscal year start value (uint16) is valid: id: ${testFiscalYearStart.id}; value: 0x${testFiscalYearStart.value.toString(16)}`, () => {
expect(FiscalYearStart.isValidType(testFiscalYearStart.value)).toBe(true);
test(`should return fiscal year start object if fiscal year start value (uint16) is valid: id: ${testFiscalYearStart.id}; value: 0x${testFiscalYearStart.value.toString(16)}`, () => {
expect(FiscalYearStart.valueOf(testFiscalYearStart.value)).toBeDefined();
});
test(`returns same month-date string for valid fiscal year start value: id: ${testFiscalYearStart.id}; value: 0x${testFiscalYearStart.value.toString(16)}`, () => {
const fiscalYearStart = FiscalYearStart.strictFromNumber(testFiscalYearStart.value);
expect(fiscalYearStart.toString()).toStrictEqual(testFiscalYearStart.monthDateString);
const fiscalYearStart = FiscalYearStart.valueOf(testFiscalYearStart.value);
expect(fiscalYearStart?.toMonthDashDayString()).toStrictEqual(testFiscalYearStart.monthDateString);
});
});
});
@@ -103,24 +102,20 @@ const TestCase_invalidFiscalYearValues = [
describe('validateFiscalYearStartInvalidValues', () => {
TestCase_invalidFiscalYearValues.forEach((testCase) => {
test(`should return false if fiscal year start value (uint16) is invalid: value: 0x${testCase.toString(16)}`, () => {
expect(FiscalYearStart.isValidType(testCase)).toBe(false);
test(`should return undefined if fiscal year start value (uint16) is invalid: value: 0x${testCase.toString(16)}`, () => {
expect(FiscalYearStart.valueOf(testCase)).not.toBeDefined();
});
});
});
// VALIDATE LEAP DAY FEBRUARY 29 IS NOT VALID
describe('validateFiscalYearStartLeapDay', () => {
test(`should return false if fiscal year start value (uint16) for February 29 is invalid: value: 0x0229}`, () => {
expect(FiscalYearStart.isValidType(0x0229)).toBe(false);
test(`should return undefined if fiscal year start value (uint16) for February 29 is invalid: value: 0x0229}`, () => {
expect(FiscalYearStart.valueOf(0x021D)).not.toBeDefined();
});
test(`should return error if fiscal year month-day string "02-29" is used to create fiscal year start object`, () => {
expect(() => FiscalYearStart.strictFromMonthDashDayString('02-29')).toThrow();
});
test(`should return error if integers "02" and "29" are used to create fiscal year start object`, () => {
expect(() => FiscalYearStart.validateMonthDay(2, 29)).toThrow();
test(`should return undefined if fiscal year month-day string "02-29" is used to create fiscal year start object`, () => {
expect(FiscalYearStart.parse('02-29')).not.toBeDefined();
});
});
@@ -133,8 +128,8 @@ type TestCase_getFiscalYearFromUnixTime = {
};
};
let TEST_CASES_GET_FISCAL_YEAR_FROM_UNIX_TIME: TestCase_getFiscalYearFromUnixTime[];
TEST_CASES_GET_FISCAL_YEAR_FROM_UNIX_TIME = importTestData('test_cases_getFiscalYearFromUnixTime') as TestCase_getFiscalYearFromUnixTime[];
const TEST_CASES_GET_FISCAL_YEAR_FROM_UNIX_TIME: TestCase_getFiscalYearFromUnixTime[] =
importTestData('test_cases_getFiscalYearFromUnixTime') as TestCase_getFiscalYearFromUnixTime[];
describe('getFiscalYearFromUnixTime', () => {
Object.values(TEST_FISCAL_YEAR_START_PRESETS).forEach((testFiscalYearStart) => {
@@ -161,8 +156,8 @@ type TestCase_getFiscalYearStartUnixTime = {
};
}
let TEST_CASES_GET_FISCAL_YEAR_START_UNIX_TIME: TestCase_getFiscalYearStartUnixTime[];
TEST_CASES_GET_FISCAL_YEAR_START_UNIX_TIME = importTestData('test_cases_getFiscalYearStartUnixTime') as TestCase_getFiscalYearStartUnixTime[];
const TEST_CASES_GET_FISCAL_YEAR_START_UNIX_TIME: TestCase_getFiscalYearStartUnixTime[] =
importTestData('test_cases_getFiscalYearStartUnixTime') as TestCase_getFiscalYearStartUnixTime[];
describe('getFiscalYearStartUnixTime', () => {
Object.values(TEST_FISCAL_YEAR_START_PRESETS).forEach((testFiscalYearStart) => {
@@ -172,7 +167,7 @@ describe('getFiscalYearStartUnixTime', () => {
const startUnixTime = getFiscalYearStartUnixTime(testCaseUnixTime, testFiscalYearStart.value);
const expected = testCase.expected[testFiscalYearStart.id];
const unixTimeISO = formatUnixTimeISO(startUnixTime);
expect({ unixTime: startUnixTime, ISO: unixTimeISO }).toStrictEqual({ unixTime: expected.unixTime, ISO: expected.unixTimeISO });
});
});
@@ -191,8 +186,8 @@ type TestCase_getFiscalYearEndUnixTime = {
};
}
let TEST_CASES_GET_FISCAL_YEAR_END_UNIX_TIME: TestCase_getFiscalYearEndUnixTime[];
TEST_CASES_GET_FISCAL_YEAR_END_UNIX_TIME = importTestData('test_cases_getFiscalYearEndUnixTime') as TestCase_getFiscalYearEndUnixTime[];
const TEST_CASES_GET_FISCAL_YEAR_END_UNIX_TIME: TestCase_getFiscalYearEndUnixTime[] =
importTestData('test_cases_getFiscalYearEndUnixTime') as TestCase_getFiscalYearEndUnixTime[];
describe('getFiscalYearEndUnixTime', () => {
Object.values(TEST_FISCAL_YEAR_START_PRESETS).forEach((testFiscalYearStart) => {
@@ -202,9 +197,9 @@ describe('getFiscalYearEndUnixTime', () => {
const endUnixTime = getFiscalYearEndUnixTime(testCaseUnixTime, testFiscalYearStart.value);
const expected = testCase.expected[testFiscalYearStart.id];
const unixTimeISO = formatUnixTimeISO(endUnixTime);
expect({ unixTime: endUnixTime, ISO: unixTimeISO }).toStrictEqual({ unixTime: expected.unixTime, ISO: expected.unixTimeISO });
});
});
});
@@ -218,8 +213,8 @@ type TestCase_getFiscalYearTimeRangeFromUnixTime = {
}
}
let TEST_CASES_GET_FISCAL_YEAR_UNIX_TIME_RANGE: TestCase_getFiscalYearTimeRangeFromUnixTime[];
TEST_CASES_GET_FISCAL_YEAR_UNIX_TIME_RANGE = importTestData('test_cases_getFiscalYearTimeRangeFromUnixTime') as TestCase_getFiscalYearTimeRangeFromUnixTime[];
const TEST_CASES_GET_FISCAL_YEAR_UNIX_TIME_RANGE: TestCase_getFiscalYearTimeRangeFromUnixTime[] =
importTestData('test_cases_getFiscalYearTimeRangeFromUnixTime') as TestCase_getFiscalYearTimeRangeFromUnixTime[];
describe('getFiscalYearTimeRangeFromUnixTime', () => {
Object.values(TEST_FISCAL_YEAR_START_PRESETS).forEach((testFiscalYearStart) => {
@@ -242,29 +237,31 @@ type TestCase_getAllFiscalYearsStartAndEndUnixTimes = {
expected: FiscalYearUnixTime[]
}
let TEST_CASES_GET_ALL_FISCAL_YEARS_START_AND_END_UNIX_TIMES: TestCase_getAllFiscalYearsStartAndEndUnixTimes[];
TEST_CASES_GET_ALL_FISCAL_YEARS_START_AND_END_UNIX_TIMES = importTestData('test_cases_getAllFiscalYearsStartAndEndUnixTimes') as TestCase_getAllFiscalYearsStartAndEndUnixTimes[];
const TEST_CASES_GET_ALL_FISCAL_YEARS_START_AND_END_UNIX_TIMES: TestCase_getAllFiscalYearsStartAndEndUnixTimes[] =
importTestData('test_cases_getAllFiscalYearsStartAndEndUnixTimes') as TestCase_getAllFiscalYearsStartAndEndUnixTimes[];
describe('getAllFiscalYearsStartAndEndUnixTimes', () => {
TEST_CASES_GET_ALL_FISCAL_YEARS_START_AND_END_UNIX_TIMES.forEach((testCase) => {
const fiscalYearStart = FiscalYearStart.strictFromMonthDashDayString(testCase.fiscalYearStart);
const fiscalYearStart = FiscalYearStart.parse(testCase.fiscalYearStart);
test(`returns correct fiscal year start and end unix times for ${getTestTitleFormatString(testCase.fiscalYearStartId, `${testCase.startYearMonth} to ${testCase.endYearMonth}`)}`, () => {
const fiscalYearStartAndEndUnixTimes = getAllFiscalYearsStartAndEndUnixTimes(testCase.startYearMonth, testCase.endYearMonth, fiscalYearStart.value);
expect(fiscalYearStart).toBeDefined();
const fiscalYearStartAndEndUnixTimes = getAllFiscalYearsStartAndEndUnixTimes(testCase.startYearMonth, testCase.endYearMonth, fiscalYearStart?.value || 0);
// Convert results to include ISO strings for better test output
const resultWithISO = fiscalYearStartAndEndUnixTimes.map(data => ({
...data,
minUnixTimeISO: formatUnixTimeISO(data.minUnixTime),
maxUnixTimeISO: formatUnixTimeISO(data.maxUnixTime)
}));
// Convert expected to include ISO strings
const expectedWithISO = testCase.expected.map(data => ({
...data,
minUnixTimeISO: formatUnixTimeISO(data.minUnixTime),
maxUnixTimeISO: formatUnixTimeISO(data.maxUnixTime)
}));
expect(resultWithISO).toStrictEqual(expectedWithISO);
});
});
@@ -277,15 +274,16 @@ type TestCase_getFiscalYearTimeRangeFromYear = {
expected: FiscalYearUnixTime;
}
let TEST_CASES_GET_FISCAL_YEAR_RANGE_FROM_YEAR: TestCase_getFiscalYearTimeRangeFromYear[];
TEST_CASES_GET_FISCAL_YEAR_RANGE_FROM_YEAR = importTestData('test_cases_getFiscalYearTimeRangeFromYear') as TestCase_getFiscalYearTimeRangeFromYear[];
const TEST_CASES_GET_FISCAL_YEAR_RANGE_FROM_YEAR: TestCase_getFiscalYearTimeRangeFromYear[] =
importTestData('test_cases_getFiscalYearTimeRangeFromYear') as TestCase_getFiscalYearTimeRangeFromYear[];
describe('getFiscalYearTimeRangeFromYear', () => {
TEST_CASES_GET_FISCAL_YEAR_RANGE_FROM_YEAR.forEach((testCase) => {
const fiscalYearStart = FiscalYearStart.strictFromMonthDashDayString(testCase.fiscalYearStart);
const fiscalYearStart = FiscalYearStart.parse(testCase.fiscalYearStart);
test(`returns correct fiscal year unix time range for input year integer ${testCase.year} and FY_START: ${testCase.fiscalYearStart}`, () => {
const fiscalYearRange = getFiscalYearTimeRangeFromYear(testCase.year, fiscalYearStart.value);
expect(fiscalYearStart).toBeDefined();
const fiscalYearRange = getFiscalYearTimeRangeFromYear(testCase.year, fiscalYearStart?.value || 0);
expect(fiscalYearRange).toStrictEqual(testCase.expected);
});
});
});
});
+59 -42
View File
@@ -442,7 +442,7 @@ export function getAllYearsStartAndEndUnixTimes(startYearMonth: YearMonth | stri
return allYearTimes;
}
export function getAllFiscalYearsStartAndEndUnixTimes(startYearMonth: YearMonth | string, endYearMonth: YearMonth | string, fiscalYearStart: number): FiscalYearUnixTime[] {
export function getAllFiscalYearsStartAndEndUnixTimes(startYearMonth: YearMonth | string, endYearMonth: YearMonth | string, fiscalYearStartValue: number): FiscalYearUnixTime[] {
// user selects date range: start=2024-01 and end=2026-12
// result should be 4x FiscalYearUnixTime made up of:
// - 2024-01->2024-06 (FY 24) - input start year-month->end of fiscal year in which the input start year-month falls
@@ -459,39 +459,45 @@ export function getAllFiscalYearsStartAndEndUnixTimes(startYearMonth: YearMonth
const inputStartUnixTime = getYearMonthFirstUnixTime(range.startYearMonth);
const inputEndUnixTime = getYearMonthLastUnixTime(range.endYearMonth);
const fiscalYearStartMonth = FiscalYearStart.strictFromNumber(fiscalYearStart).month;
let fiscalYearStart = FiscalYearStart.valueOf(fiscalYearStartValue);
if (!fiscalYearStart) {
fiscalYearStart = FiscalYearStart.Default;
}
const fiscalYearStartMonth = fiscalYearStart.month;
// Loop over 1 year before and 1 year after the input date range
// to include fiscal years that start in the previous calendar year.
for (let year = range.startYearMonth.year - 1; year <= range.endYearMonth.year + 1; year++) {
const thisYearMonthUnixTime = getYearMonthFirstUnixTime({ year: year, month: fiscalYearStartMonth });
const fiscalStartTime = getFiscalYearStartUnixTime(thisYearMonthUnixTime, fiscalYearStart);
const fiscalEndTime = getFiscalYearEndUnixTime(thisYearMonthUnixTime, fiscalYearStart);
const fiscalYear = getFiscalYearFromUnixTime(fiscalStartTime, fiscalYearStart);
const fiscalStartTime = getFiscalYearStartUnixTime(thisYearMonthUnixTime, fiscalYearStart.value);
const fiscalEndTime = getFiscalYearEndUnixTime(thisYearMonthUnixTime, fiscalYearStart.value);
const fiscalYear = getFiscalYearFromUnixTime(fiscalStartTime, fiscalYearStart.value);
if (fiscalStartTime <= inputEndUnixTime && fiscalEndTime >= inputStartUnixTime) {
let minUnixTime = fiscalStartTime;
let maxUnixTime = fiscalEndTime;
// Cap the min and max unix times to the input date range
if (minUnixTime < inputStartUnixTime) {
minUnixTime = inputStartUnixTime;
}
if (maxUnixTime > inputEndUnixTime) {
maxUnixTime = inputEndUnixTime;
}
const fiscalYearTime: FiscalYearUnixTime = {
year: fiscalYear,
minUnixTime: minUnixTime,
maxUnixTime: maxUnixTime,
};
allFiscalYearTimes.push(fiscalYearTime);
}
if (fiscalStartTime > inputEndUnixTime) {
break;
}
@@ -1010,25 +1016,29 @@ export function isDateRangeMatchOneMonth(minTime: number, maxTime: number): bool
return isDateRangeMatchFullMonths(minTime, maxTime);
}
export function getFiscalYearFromUnixTime(unixTime: number, fiscalYearStart: number): number {
export function getFiscalYearFromUnixTime(unixTime: number, fiscalYearStartValue: number): number {
const date = moment.unix(unixTime);
// For January 1 fiscal year start, fiscal year matches calendar year
if (fiscalYearStart === 0x0101) {
if (fiscalYearStartValue === FiscalYearStart.JanuaryFirstDay.value) {
return date.year();
}
// Get date components
const month = date.month() + 1; // 1-index
const day = date.date();
const year = date.year();
const [fiscalYearStartMonth, fiscalYearStartDay] = FiscalYearStart.strictFromNumber(fiscalYearStart).values();
let fiscalYearStart = FiscalYearStart.valueOf(fiscalYearStartValue);
if (!fiscalYearStart) {
fiscalYearStart = FiscalYearStart.Default;
}
// For other fiscal year starts:
// If input time comes before the fiscal year start day in the calendar year,
// it belongs to the fiscal year that ends in the current calendar year
if (month < fiscalYearStartMonth || (month === fiscalYearStartMonth && day < fiscalYearStartDay)) {
if (month < fiscalYearStart.month || (month === fiscalYearStart.month && day < fiscalYearStart.day)) {
return year;
}
@@ -1037,33 +1047,38 @@ export function getFiscalYearFromUnixTime(unixTime: number, fiscalYearStart: num
return year + 1;
}
export function getFiscalYearStartUnixTime(unixTime: number, fiscalYearStart: number): number {
export function getFiscalYearStartUnixTime(unixTime: number, fiscalYearStartValue: number): number {
const date = moment.unix(unixTime);
// For January 1 fiscal year start, fiscal year start time is always January 1 in the input calendar year
if (fiscalYearStart === 0x0101) {
if (fiscalYearStartValue === FiscalYearStart.JanuaryFirstDay.value) {
return moment().year(date.year()).month(0).date(1).hour(0).minute(0).second(0).millisecond(0).unix();
}
const [fiscalYearStartMonth, fiscalYearStartDay] = FiscalYearStart.strictFromNumber(fiscalYearStart).values();
let fiscalYearStart = FiscalYearStart.valueOf(fiscalYearStartValue);
if (!fiscalYearStart) {
fiscalYearStart = FiscalYearStart.Default;
}
const month = date.month() + 1; // 1-index
const day = date.date();
const year = date.year();
// For other fiscal year starts:
// If input time comes before the fiscal year start day in the calendar year,
// the relevant fiscal year has a start date in Calendar Year = Input Year, and end date in Calendar Year = Input Year + 1.
// If input time comes on or after the fiscal year start day in the calendar year,
// the relevant fiscal year has a start date in Calendar Year = Input Year - 1, and end date in Calendar Year = Input Year.
let startYear = year - 1;
if (month > fiscalYearStartMonth || (month === fiscalYearStartMonth && day >= fiscalYearStartDay)) {
if (month > fiscalYearStart.month || (month === fiscalYearStart.month && day >= fiscalYearStart.day)) {
startYear = year;
}
return moment().set({
year: startYear,
month: fiscalYearStartMonth - 1, // 0-index
date: fiscalYearStartDay,
month: fiscalYearStart.month - 1, // 0-index
date: fiscalYearStart.day,
hour: 0,
minute: 0,
second: 0,
@@ -1073,7 +1088,7 @@ export function getFiscalYearStartUnixTime(unixTime: number, fiscalYearStart: nu
export function getFiscalYearEndUnixTime(unixTime: number, fiscalYearStart: number): number {
const fiscalYearStartTime = moment.unix(getFiscalYearStartUnixTime(unixTime, fiscalYearStart));
return fiscalYearStartTime.add(1, 'year').subtract(1, 'second').unix();
return fiscalYearStartTime.add(1, 'years').subtract(1, 'seconds').unix();
}
export function getCurrentFiscalYear(fiscalYearStart: number): number {
@@ -1091,33 +1106,35 @@ export function getFiscalYearTimeRangeFromUnixTime(unixTime: number, fiscalYearS
};
}
export function getFiscalYearTimeRangeFromYear(year: number, fiscalYearStart: number): FiscalYearUnixTime {
export function getFiscalYearTimeRangeFromYear(year: number, fiscalYearStartValue: number): FiscalYearUnixTime {
const fiscalYear = year;
const fiscalYearStartObj = FiscalYearStart.strictFromNumber(fiscalYearStart);
let fiscalYearStart = FiscalYearStart.valueOf(fiscalYearStartValue);
if (!fiscalYearStart) {
fiscalYearStart = FiscalYearStart.Default;
}
// For a specified fiscal year (e.g., 2023), the start date is in the previous calendar year
// unless fiscal year starts on January 1
const calendarStartYear = fiscalYearStart === 0x0101 ? fiscalYear : fiscalYear - 1;
const calendarStartYear = fiscalYearStartValue === FiscalYearStart.JanuaryFirstDay.value ? fiscalYear : fiscalYear - 1;
// Create the timestamp for the start of the fiscal year
const fiscalYearStartUnixTime = moment().set({
year: calendarStartYear,
month: fiscalYearStartObj.month - 1, // 0-index
date: fiscalYearStartObj.day,
month: fiscalYearStart.month - 1, // 0-index
date: fiscalYearStart.day,
hour: 0,
minute: 0,
second: 0,
millisecond: 0,
}).unix();
// Fiscal year end is one year after start minus 1 second
const fiscalYearEndUnixTime = moment.unix(fiscalYearStartUnixTime).add(1, 'year').subtract(1, 'second').unix();
const fiscalYearEndUnixTime = moment.unix(fiscalYearStartUnixTime).add(1, 'years').subtract(1, 'seconds').unix();
return {
year: fiscalYear,
minUnixTime: fiscalYearStartUnixTime,
maxUnixTime: fiscalYearEndUnixTime,
};
}